import 'package:flutter/material.dart'; import '../../../../core/models/membre_search_result.dart' as search_model; import '../../data/models/membre_complete_model.dart'; /// Widget d'affichage des résultats de recherche de membres /// Gère la pagination, le tri et l'affichage des membres trouvés class MembreSearchResults extends StatefulWidget { final search_model.MembreSearchResult result; final Function(MembreCompletModel)? onMembreSelected; final bool showPagination; const MembreSearchResults({ super.key, required this.result, this.onMembreSelected, this.showPagination = true, }); @override State createState() => _MembreSearchResultsState(); } class _MembreSearchResultsState extends State { @override Widget build(BuildContext context) { if (widget.result.isEmpty) { return _buildEmptyState(); } return Column( children: [ // En-tête avec informations sur les résultats _buildResultsHeader(), // Liste des membres Expanded( child: _buildMembersList(), ), // Pagination si activée if (widget.showPagination && widget.result.totalPages > 1) _buildPagination(), ], ); } /// État vide quand aucun résultat Widget _buildEmptyState() { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.search_off, size: 64, color: Colors.grey[400], ), const SizedBox(height: 16), Text( 'Aucun membre trouvé', style: Theme.of(context).textTheme.titleLarge?.copyWith( color: Colors.grey[600], ), ), const SizedBox(height: 8), Text( 'Essayez de modifier vos critères de recherche', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Colors.grey[500], ), ), const SizedBox(height: 24), ElevatedButton.icon( onPressed: () => Navigator.of(context).pop(), icon: const Icon(Icons.tune), label: const Text('Modifier les critères'), ), ], ), ); } /// En-tête avec informations sur les résultats Widget _buildResultsHeader() { return Container( padding: const EdgeInsets.all(16.0), decoration: BoxDecoration( color: Theme.of(context).primaryColor.withOpacity(0.1), border: Border( bottom: BorderSide( color: Theme.of(context).dividerColor, width: 1, ), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon( Icons.search, color: Theme.of(context).primaryColor, size: 20, ), const SizedBox(width: 8), Expanded( child: Text( widget.result.resultDescription, style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), ), Chip( label: Text('${widget.result.executionTimeMs}ms'), backgroundColor: Colors.green.withOpacity(0.1), labelStyle: const TextStyle( color: Colors.green, fontSize: 12, ), ), ], ), if (widget.result.criteria.description.isNotEmpty) ...[ const SizedBox(height: 8), Text( 'Critères: ${widget.result.criteria.description}', style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Colors.grey[600], ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ], ], ), ); } /// Liste des membres trouvés Widget _buildMembersList() { return ListView.builder( itemCount: widget.result.membres.length, itemBuilder: (context, index) { final membre = widget.result.membres[index]; return _buildMembreCard(membre, index); }, ); } /// Carte d'affichage d'un membre Widget _buildMembreCard(MembreCompletModel membre, int index) { return Card( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), child: ListTile( leading: CircleAvatar( backgroundColor: _getStatusColor(membre.statut), child: Text( _getInitials(membre.nom, membre.prenom), style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, ), ), ), title: Text( '${membre.prenom} ${membre.nom}', style: const TextStyle(fontWeight: FontWeight.bold), ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (membre.email.isNotEmpty) Row( children: [ const Icon(Icons.email, size: 14, color: Colors.grey), const SizedBox(width: 4), Expanded( child: Text( membre.email, style: const TextStyle(fontSize: 12), overflow: TextOverflow.ellipsis, ), ), ], ), if (membre.telephone?.isNotEmpty == true) Row( children: [ const Icon(Icons.phone, size: 14, color: Colors.grey), const SizedBox(width: 4), Text( membre.telephone!, style: const TextStyle(fontSize: 12), ), ], ), if (membre.organisationNom?.isNotEmpty == true) Row( children: [ const Icon(Icons.business, size: 14, color: Colors.grey), const SizedBox(width: 4), Expanded( child: Text( membre.organisationNom!, style: const TextStyle(fontSize: 12), overflow: TextOverflow.ellipsis, ), ), ], ), ], ), trailing: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ _buildStatusChip(membre.statut), if (membre.role?.isNotEmpty == true) ...[ const SizedBox(height: 4), Text( _formatRoles(membre.role!), style: const TextStyle( fontSize: 10, color: Colors.grey, ), textAlign: TextAlign.center, ), ], ], ), onTap: widget.onMembreSelected != null ? () => widget.onMembreSelected!(membre) : null, ), ); } /// Pagination Widget _buildPagination() { return Container( padding: const EdgeInsets.all(16.0), decoration: BoxDecoration( color: Theme.of(context).cardColor, border: Border( top: BorderSide( color: Theme.of(context).dividerColor, width: 1, ), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Bouton page précédente ElevatedButton.icon( onPressed: widget.result.hasPrevious ? _goToPreviousPage : null, icon: const Icon(Icons.chevron_left), label: const Text('Précédent'), style: ElevatedButton.styleFrom( backgroundColor: Colors.grey[100], foregroundColor: Colors.grey[700], ), ), // Indicateur de page Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: Theme.of(context).primaryColor.withOpacity(0.1), borderRadius: BorderRadius.circular(20), ), child: Text( 'Page ${widget.result.currentPage + 1} / ${widget.result.totalPages}', style: TextStyle( color: Theme.of(context).primaryColor, fontWeight: FontWeight.bold, ), ), ), // Bouton page suivante ElevatedButton.icon( onPressed: widget.result.hasNext ? _goToNextPage : null, icon: const Icon(Icons.chevron_right), label: const Text('Suivant'), style: ElevatedButton.styleFrom( backgroundColor: Theme.of(context).primaryColor, foregroundColor: Colors.white, ), ), ], ), ); } /// Chip de statut Widget _buildStatusChip(StatutMembre statut) { final color = _getStatusColor(statut); return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(12), border: Border.all(color: color, width: 1), ), child: Text( _getStatusLabel(statut), style: TextStyle( color: color, fontSize: 10, fontWeight: FontWeight.bold, ), ), ); } /// Obtient la couleur du statut Color _getStatusColor(StatutMembre statut) { switch (statut) { case StatutMembre.actif: return Colors.green; case StatutMembre.inactif: return Colors.orange; case StatutMembre.suspendu: return Colors.red; case StatutMembre.enAttente: return Colors.grey; default: return Colors.grey; } } /// Obtient le libellé du statut String _getStatusLabel(StatutMembre statut) { switch (statut) { case StatutMembre.actif: return 'Actif'; case StatutMembre.inactif: return 'Inactif'; case StatutMembre.suspendu: return 'Suspendu'; case StatutMembre.enAttente: return 'En attente'; } } /// Obtient les initiales d'un membre String _getInitials(String nom, String prenom) { final nomInitial = nom.isNotEmpty ? nom[0].toUpperCase() : ''; final prenomInitial = prenom.isNotEmpty ? prenom[0].toUpperCase() : ''; return '$prenomInitial$nomInitial'; } /// Formate les rôles pour l'affichage String _formatRoles(String roles) { final rolesList = roles.split(',').map((r) => r.trim()).toList(); if (rolesList.length <= 2) { return rolesList.join(', '); } return '${rolesList.take(2).join(', ')}...'; } /// Navigation vers la page précédente void _goToPreviousPage() { // TODO: Implémenter la navigation vers la page précédente ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Navigation vers la page précédente'), duration: Duration(seconds: 1), ), ); } /// Navigation vers la page suivante void _goToNextPage() { // TODO: Implémenter la navigation vers la page suivante ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Navigation vers la page suivante'), duration: Duration(seconds: 1), ), ); } }