import 'package:flutter/material.dart'; import '../../../../core/models/membre_search_criteria.dart'; import '../../../../core/models/membre_search_result.dart'; import '../widgets/membre_search_results.dart'; import '../widgets/search_statistics_card.dart'; /// Page de recherche avancée des membres /// Interface complète pour la recherche sophistiquée avec filtres multiples class AdvancedSearchPage extends StatefulWidget { const AdvancedSearchPage({super.key}); @override State createState() => _AdvancedSearchPageState(); } class _AdvancedSearchPageState extends State with TickerProviderStateMixin { late TabController _tabController; MembreSearchCriteria _currentCriteria = MembreSearchCriteria.empty; MembreSearchResult? _currentResult; bool _isSearching = false; String? _errorMessage; // Contrôleurs pour les champs de recherche final _queryController = TextEditingController(); final _nomController = TextEditingController(); final _prenomController = TextEditingController(); final _emailController = TextEditingController(); final _telephoneController = TextEditingController(); final _regionController = TextEditingController(); final _villeController = TextEditingController(); final _professionController = TextEditingController(); // Valeurs pour les filtres String? _selectedStatut; final List _selectedRoles = []; final List _selectedOrganisations = []; RangeValues _ageRange = const RangeValues(18, 65); DateTimeRange? _adhesionDateRange; bool _includeInactifs = false; bool _membreBureau = false; bool _responsable = false; @override void initState() { super.initState(); _tabController = TabController(length: 3, vsync: this); } @override void dispose() { _tabController.dispose(); _queryController.dispose(); _nomController.dispose(); _prenomController.dispose(); _emailController.dispose(); _telephoneController.dispose(); _regionController.dispose(); _villeController.dispose(); _professionController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Recherche Avancée'), backgroundColor: Theme.of(context).primaryColor, foregroundColor: Colors.white, elevation: 0, bottom: TabBar( controller: _tabController, indicatorColor: Colors.white, labelColor: Colors.white, unselectedLabelColor: Colors.white70, tabs: const [ Tab(icon: Icon(Icons.search), text: 'Critères'), Tab(icon: Icon(Icons.list), text: 'Résultats'), Tab(icon: Icon(Icons.analytics), text: 'Statistiques'), ], ), ), body: TabBarView( controller: _tabController, children: [ _buildSearchCriteriaTab(), _buildSearchResultsTab(), _buildStatisticsTab(), ], ), floatingActionButton: _buildSearchFab(), ); } /// Onglet des critères de recherche Widget _buildSearchCriteriaTab() { return SingleChildScrollView( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Recherche rapide _buildQuickSearchSection(), const SizedBox(height: 24), // Critères détaillés _buildDetailedCriteriaSection(), const SizedBox(height: 24), // Filtres avancés _buildAdvancedFiltersSection(), const SizedBox(height: 24), // Boutons d'action _buildActionButtons(), ], ), ); } /// Section de recherche rapide Widget _buildQuickSearchSection() { return Card( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.flash_on, color: Theme.of(context).primaryColor), const SizedBox(width: 8), Text( 'Recherche Rapide', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 16), TextField( controller: _queryController, decoration: const InputDecoration( labelText: 'Rechercher un membre', hintText: 'Nom, prénom ou email...', prefixIcon: Icon(Icons.search), border: OutlineInputBorder(), ), onSubmitted: (_) => _performQuickSearch(), ), const SizedBox(height: 12), Wrap( spacing: 8, children: [ _buildQuickFilterChip('Membres actifs', () { _selectedStatut = 'ACTIF'; _includeInactifs = false; }), _buildQuickFilterChip('Membres bureau', () { _membreBureau = true; _selectedStatut = 'ACTIF'; }), _buildQuickFilterChip('Responsables', () { _responsable = true; _selectedStatut = 'ACTIF'; }), ], ), ], ), ), ); } /// Section des critères détaillés Widget _buildDetailedCriteriaSection() { return Card( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.tune, color: Theme.of(context).primaryColor), const SizedBox(width: 8), Text( 'Critères Détaillés', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 16), Row( children: [ Expanded( child: TextField( controller: _nomController, decoration: const InputDecoration( labelText: 'Nom', border: OutlineInputBorder(), ), ), ), const SizedBox(width: 12), Expanded( child: TextField( controller: _prenomController, decoration: const InputDecoration( labelText: 'Prénom', border: OutlineInputBorder(), ), ), ), ], ), const SizedBox(height: 16), TextField( controller: _emailController, decoration: const InputDecoration( labelText: 'Email', hintText: 'exemple@unionflow.com', border: OutlineInputBorder(), ), ), const SizedBox(height: 16), Row( children: [ Expanded( child: TextField( controller: _telephoneController, decoration: const InputDecoration( labelText: 'Téléphone', border: OutlineInputBorder(), ), ), ), const SizedBox(width: 12), Expanded( child: DropdownButtonFormField( value: _selectedStatut, decoration: const InputDecoration( labelText: 'Statut', border: OutlineInputBorder(), ), items: const [ DropdownMenuItem(value: null, child: Text('Tous')), DropdownMenuItem(value: 'ACTIF', child: Text('Actif')), DropdownMenuItem(value: 'INACTIF', child: Text('Inactif')), DropdownMenuItem(value: 'SUSPENDU', child: Text('Suspendu')), DropdownMenuItem(value: 'RADIE', child: Text('Radié')), ], onChanged: (value) => setState(() => _selectedStatut = value), ), ), ], ), ], ), ), ); } /// Section des filtres avancés Widget _buildAdvancedFiltersSection() { return Card( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.filter_alt, color: Theme.of(context).primaryColor), const SizedBox(width: 8), Text( 'Filtres Avancés', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 16), // Tranche d'âge Text('Tranche d\'âge: ${_ageRange.start.round()}-${_ageRange.end.round()} ans'), RangeSlider( values: _ageRange, min: 18, max: 80, divisions: 62, labels: RangeLabels( '${_ageRange.start.round()}', '${_ageRange.end.round()}', ), onChanged: (values) => setState(() => _ageRange = values), ), const SizedBox(height: 16), // Options booléennes CheckboxListTile( title: const Text('Inclure les membres inactifs'), value: _includeInactifs, onChanged: (value) => setState(() => _includeInactifs = value ?? false), ), CheckboxListTile( title: const Text('Membres du bureau uniquement'), value: _membreBureau, onChanged: (value) => setState(() => _membreBureau = value ?? false), ), CheckboxListTile( title: const Text('Responsables uniquement'), value: _responsable, onChanged: (value) => setState(() => _responsable = value ?? false), ), ], ), ), ); } /// Boutons d'action Widget _buildActionButtons() { return Row( children: [ Expanded( child: OutlinedButton.icon( onPressed: _clearCriteria, icon: const Icon(Icons.clear), label: const Text('Effacer'), ), ), const SizedBox(width: 16), Expanded( flex: 2, child: ElevatedButton.icon( onPressed: _isSearching ? null : _performAdvancedSearch, icon: _isSearching ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.search), label: Text(_isSearching ? 'Recherche...' : 'Rechercher'), ), ), ], ); } /// Onglet des résultats Widget _buildSearchResultsTab() { if (_currentResult == null) { return const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.search, size: 64, color: Colors.grey), SizedBox(height: 16), Text( 'Aucune recherche effectuée', style: TextStyle(fontSize: 18, color: Colors.grey), ), SizedBox(height: 8), Text( 'Utilisez l\'onglet Critères pour lancer une recherche', style: TextStyle(color: Colors.grey), ), ], ), ); } if (_errorMessage != null) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.error, size: 64, color: Colors.red), const SizedBox(height: 16), Text( 'Erreur de recherche', style: Theme.of(context).textTheme.titleLarge, ), const SizedBox(height: 8), Text( _errorMessage!, textAlign: TextAlign.center, style: const TextStyle(color: Colors.red), ), const SizedBox(height: 16), ElevatedButton( onPressed: () => setState(() => _errorMessage = null), child: const Text('Réessayer'), ), ], ), ); } return MembreSearchResults(result: _currentResult!); } /// Onglet des statistiques Widget _buildStatisticsTab() { if (_currentResult?.statistics == null) { return const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.analytics, size: 64, color: Colors.grey), SizedBox(height: 16), Text( 'Aucune statistique disponible', style: TextStyle(fontSize: 18, color: Colors.grey), ), SizedBox(height: 8), Text( 'Effectuez une recherche pour voir les statistiques', style: TextStyle(color: Colors.grey), ), ], ), ); } return SearchStatisticsCard(statistics: _currentResult!.statistics!); } /// FAB de recherche Widget _buildSearchFab() { return FloatingActionButton.extended( onPressed: _isSearching ? null : _performAdvancedSearch, icon: _isSearching ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white), ) : const Icon(Icons.search), label: Text(_isSearching ? 'Recherche...' : 'Rechercher'), ); } /// Chip de filtre rapide Widget _buildQuickFilterChip(String label, VoidCallback onTap) { return ActionChip( label: Text(label), onPressed: onTap, backgroundColor: Theme.of(context).primaryColor.withOpacity(0.1), labelStyle: TextStyle(color: Theme.of(context).primaryColor), ); } /// Effectue une recherche rapide void _performQuickSearch() { if (_queryController.text.trim().isEmpty) return; final criteria = MembreSearchCriteria.quickSearch(_queryController.text.trim()); _performSearch(criteria); } /// Effectue une recherche avancée void _performAdvancedSearch() { final criteria = _buildSearchCriteria(); _performSearch(criteria); } /// Construit les critères de recherche à partir des champs MembreSearchCriteria _buildSearchCriteria() { return MembreSearchCriteria( query: _queryController.text.trim().isEmpty ? null : _queryController.text.trim(), nom: _nomController.text.trim().isEmpty ? null : _nomController.text.trim(), prenom: _prenomController.text.trim().isEmpty ? null : _prenomController.text.trim(), email: _emailController.text.trim().isEmpty ? null : _emailController.text.trim(), telephone: _telephoneController.text.trim().isEmpty ? null : _telephoneController.text.trim(), statut: _selectedStatut, ageMin: _ageRange.start.round(), ageMax: _ageRange.end.round(), region: _regionController.text.trim().isEmpty ? null : _regionController.text.trim(), ville: _villeController.text.trim().isEmpty ? null : _villeController.text.trim(), profession: _professionController.text.trim().isEmpty ? null : _professionController.text.trim(), organisationIds: _selectedOrganisations.isEmpty ? null : _selectedOrganisations, roles: _selectedRoles.isEmpty ? null : _selectedRoles, membreBureau: _membreBureau ? true : null, responsable: _responsable ? true : null, includeInactifs: _includeInactifs, ); } /// Effectue la recherche void _performSearch(MembreSearchCriteria criteria) async { if (!criteria.hasAnyCriteria) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Veuillez spécifier au moins un critère de recherche'), backgroundColor: Colors.orange, ), ); return; } setState(() { _isSearching = true; _errorMessage = null; _currentCriteria = criteria; }); try { // TODO: Appeler le service de recherche // final result = await _searchService.searchMembresAdvanced(criteria: criteria); // Simulation pour l'instant await Future.delayed(const Duration(seconds: 2)); final result = MembreSearchResult.empty(criteria); setState(() { _currentResult = result; _isSearching = false; }); // Basculer vers l'onglet des résultats _tabController.animateTo(1); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(result.resultDescription), backgroundColor: Colors.green, ), ); } catch (e) { setState(() { _errorMessage = e.toString(); _isSearching = false; }); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Erreur de recherche: $e'), backgroundColor: Colors.red, ), ); } } /// Efface tous les critères void _clearCriteria() { setState(() { _queryController.clear(); _nomController.clear(); _prenomController.clear(); _emailController.clear(); _telephoneController.clear(); _regionController.clear(); _villeController.clear(); _professionController.clear(); _selectedStatut = null; _selectedRoles.clear(); _selectedOrganisations.clear(); _ageRange = const RangeValues(18, 65); _adhesionDateRange = null; _includeInactifs = false; _membreBureau = false; _responsable = false; _currentResult = null; _errorMessage = null; }); } }