import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:intl/intl.dart'; import '../../../../core/di/injection.dart'; import '../../../../core/models/membre_model.dart'; import '../../../../core/models/cotisation_model.dart'; import '../../../../shared/theme/app_theme.dart'; import '../bloc/membres_bloc.dart'; import '../bloc/membres_event.dart'; import '../bloc/membres_state.dart'; import '../widgets/membre_info_section.dart'; import '../widgets/membre_stats_section.dart'; import '../widgets/membre_cotisations_section.dart'; import '../widgets/membre_actions_section.dart'; import '../widgets/membre_delete_dialog.dart'; import 'membre_edit_page.dart'; /// Page de détails complète d'un membre class MembreDetailsPage extends StatefulWidget { const MembreDetailsPage({ super.key, required this.membreId, this.membre, }); final String membreId; final MembreModel? membre; @override State createState() => _MembreDetailsPageState(); } class _MembreDetailsPageState extends State with SingleTickerProviderStateMixin { late MembresBloc _membresBloc; late TabController _tabController; MembreModel? _currentMembre; List _cotisations = []; bool _isLoadingCotisations = false; @override void initState() { super.initState(); _membresBloc = getIt(); _tabController = TabController(length: 3, vsync: this); _currentMembre = widget.membre; // Charger les détails du membre si pas fourni if (_currentMembre == null) { _membresBloc.add(LoadMembreById(widget.membreId)); } // Charger les cotisations du membre _loadMemberCotisations(); } @override void dispose() { _tabController.dispose(); super.dispose(); } Future _loadMemberCotisations() async { setState(() { _isLoadingCotisations = true; }); try { // TODO: Implémenter le chargement des cotisations via le repository // final cotisations = await getIt() // .getCotisationsByMembre(widget.membreId); // setState(() { // _cotisations = cotisations; // }); // Simulation temporaire await Future.delayed(const Duration(seconds: 1)); setState(() { _cotisations = _generateMockCotisations(); }); } catch (e) { // Gérer l'erreur debugPrint('Erreur lors du chargement des cotisations: $e'); } finally { setState(() { _isLoadingCotisations = false; }); } } List _generateMockCotisations() { // Données de test temporaires return [ CotisationModel( id: '1', numeroReference: 'COT-2025-001', membreId: widget.membreId, typeCotisation: 'MENSUELLE', periode: 'Janvier 2025', montantDu: 25000, montantPaye: 25000, codeDevise: 'XOF', statut: 'PAYEE', dateEcheance: DateTime(2025, 1, 31), datePaiement: DateTime(2025, 1, 15), annee: 2025, recurrente: true, nombreRappels: 0, dateCreation: DateTime(2025, 1, 1), ), CotisationModel( id: '2', numeroReference: 'COT-2025-002', membreId: widget.membreId, typeCotisation: 'MENSUELLE', periode: 'Février 2025', montantDu: 25000, montantPaye: 0, codeDevise: 'XOF', statut: 'EN_ATTENTE', dateEcheance: DateTime(2025, 2, 28), annee: 2025, recurrente: true, nombreRappels: 1, dateCreation: DateTime(2025, 2, 1), ), ]; } @override Widget build(BuildContext context) { return BlocProvider.value( value: _membresBloc, child: Scaffold( backgroundColor: AppTheme.backgroundLight, body: BlocConsumer( listener: (context, state) { if (state is MembreLoaded) { setState(() { _currentMembre = state.membre; }); } else if (state is MembresError) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(state.message), backgroundColor: AppTheme.errorColor, ), ); } }, builder: (context, state) { if (state is MembresLoading && _currentMembre == null) { return const Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator(), SizedBox(height: 16), Text('Chargement des détails...'), ], ), ), ); } if (state is MembresError && _currentMembre == null) { return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.error, size: 64, color: AppTheme.errorColor), const SizedBox(height: 16), Text(state.message), const SizedBox(height: 16), ElevatedButton( onPressed: () => _membresBloc.add(LoadMembreById(widget.membreId)), child: const Text('Réessayer'), ), ], ), ), ); } if (_currentMembre == null) { return const Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.person_off, size: 64), SizedBox(height: 16), Text('Membre non trouvé'), ], ), ), ); } return _buildContent(); }, ), ), ); } Widget _buildContent() { return NestedScrollView( headerSliverBuilder: (context, innerBoxIsScrolled) { return [ _buildAppBar(innerBoxIsScrolled), _buildMemberHeader(), _buildTabBar(), ]; }, body: TabBarView( controller: _tabController, children: [ _buildInfoTab(), _buildCotisationsTab(), _buildStatsTab(), ], ), ); } Widget _buildAppBar(bool innerBoxIsScrolled) { return SliverAppBar( expandedHeight: 0, floating: true, pinned: true, backgroundColor: AppTheme.primaryColor, foregroundColor: Colors.white, title: Text( _currentMembre?.nomComplet ?? 'Détails du membre', style: const TextStyle( fontWeight: FontWeight.w600, fontSize: 18, ), ), actions: [ IconButton( icon: const Icon(Icons.edit), onPressed: _editMember, tooltip: 'Modifier', ), PopupMenuButton( onSelected: _handleMenuAction, itemBuilder: (context) => [ const PopupMenuItem( value: 'call', child: ListTile( leading: Icon(Icons.phone), title: Text('Appeler'), contentPadding: EdgeInsets.zero, ), ), const PopupMenuItem( value: 'message', child: ListTile( leading: Icon(Icons.message), title: Text('Message'), contentPadding: EdgeInsets.zero, ), ), const PopupMenuItem( value: 'export', child: ListTile( leading: Icon(Icons.download), title: Text('Exporter'), contentPadding: EdgeInsets.zero, ), ), const PopupMenuItem( value: 'delete', child: ListTile( leading: Icon(Icons.delete, color: Colors.red), title: Text('Supprimer', style: TextStyle(color: Colors.red)), contentPadding: EdgeInsets.zero, ), ), ], ), ], ); } Widget _buildMemberHeader() { return SliverToBoxAdapter( child: Container( color: AppTheme.primaryColor, padding: const EdgeInsets.fromLTRB(16, 0, 16, 24), child: MembreInfoSection( membre: _currentMembre!, showActions: false, ), ), ); } Widget _buildTabBar() { return SliverPersistentHeader( pinned: true, delegate: _TabBarDelegate( TabBar( controller: _tabController, labelColor: AppTheme.primaryColor, unselectedLabelColor: AppTheme.textSecondary, indicatorColor: AppTheme.primaryColor, indicatorWeight: 3, tabs: const [ Tab(text: 'Informations'), Tab(text: 'Cotisations'), Tab(text: 'Statistiques'), ], ), ), ); } Widget _buildInfoTab() { return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( children: [ MembreInfoSection( membre: _currentMembre!, showActions: true, onEdit: _editMember, onCall: _callMember, onMessage: _messageMember, ), const SizedBox(height: 16), MembreActionsSection( membre: _currentMembre!, onEdit: _editMember, onDelete: _deleteMember, onExport: _exportMember, ), ], ), ); } Widget _buildCotisationsTab() { return MembreCotisationsSection( membre: _currentMembre!, cotisations: _cotisations, isLoading: _isLoadingCotisations, onRefresh: _loadMemberCotisations, ); } Widget _buildStatsTab() { return SingleChildScrollView( padding: const EdgeInsets.all(16), child: MembreStatsSection( membre: _currentMembre!, cotisations: _cotisations, ), ); } void _editMember() async { if (widget.membre == null) return; final result = await Navigator.of(context).push( MaterialPageRoute( builder: (context) => MembreEditPage(membre: widget.membre!), ), ); // Si le membre a été modifié avec succès, recharger les données if (result == true) { _loadMemberCotisations(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Membre modifié avec succès !'), backgroundColor: AppTheme.successColor, ), ); } } void _callMember() { // TODO: Implémenter l'appel ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Appel - À implémenter')), ); } void _messageMember() { // TODO: Implémenter l'envoi de message ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Message - À implémenter')), ); } void _deleteMember() async { if (widget.membre == null) return; final result = await showDialog( context: context, barrierDismissible: false, builder: (context) => MembreDeleteDialog(membre: widget.membre!), ); // Si le membre a été supprimé/désactivé avec succès if (result == true && mounted) { // Retourner à la liste des membres Navigator.of(context).pop(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Membre traité avec succès !'), backgroundColor: AppTheme.successColor, ), ); } } void _exportMember() { // TODO: Implémenter l'export ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Export - À implémenter')), ); } void _handleMenuAction(String action) { switch (action) { case 'call': _callMember(); break; case 'message': _messageMember(); break; case 'export': _exportMember(); break; case 'delete': _deleteMember(); break; } } } class _TabBarDelegate extends SliverPersistentHeaderDelegate { const _TabBarDelegate(this.tabBar); final TabBar tabBar; @override double get minExtent => tabBar.preferredSize.height; @override double get maxExtent => tabBar.preferredSize.height; @override Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { return Container( color: Colors.white, child: tabBar, ); } @override bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) { return false; } }