diff --git a/app/lib/presentation/pages/history_page.dart b/app/lib/presentation/pages/history_page.dart old mode 100644 new mode 100755 index 2ff7ec66..b671a1d9 --- a/app/lib/presentation/pages/history_page.dart +++ b/app/lib/presentation/pages/history_page.dart @@ -55,6 +55,8 @@ class _HistoryContentState extends State { String _selectedTypeFilter = 'Tous les types'; String _searchQuery = ''; int? selectedTypeId; + int? _selectedMemberId; // null = "Tous" (admin uniquement) + int? _selectedSectorId; // null = "Tous" (admin uniquement) // Contrôleur de recherche final TextEditingController _searchController = TextEditingController(); @@ -221,6 +223,20 @@ class _HistoryContentState extends State { } } + // Filtre par membre (admin uniquement) + if (isAdmin && _selectedMemberId != null) { + if (passage.fkUser != _selectedMemberId) { + return false; + } + } + + // Filtre par secteur (admin uniquement) + if (isAdmin && _selectedSectorId != null) { + if (passage.fkSector != _selectedSectorId) { + return false; + } + } + // Filtre par recherche textuelle if (_searchQuery.isNotEmpty) { final query = _searchQuery.toLowerCase(); @@ -302,6 +318,7 @@ class _HistoryContentState extends State { children: [ // Barre de recherche Expanded( + flex: 3, child: TextFormField( controller: _searchController, decoration: const InputDecoration( @@ -319,6 +336,21 @@ class _HistoryContentState extends State { }, ), ), + // Filtres admin uniquement + if (isAdmin) ...[ + const SizedBox(width: 8), + // Filtre par membre + Expanded( + flex: 2, + child: _buildMemberDropdown(), + ), + const SizedBox(width: 8), + // Filtre par secteur + Expanded( + flex: 2, + child: _buildSectorDropdown(), + ), + ], ], ), ), @@ -563,4 +595,112 @@ class _HistoryContentState extends State { } } } + + /// Construit le dropdown de sélection de membre (admin uniquement) + Widget _buildMemberDropdown() { + // Récupérer les membres uniques depuis les passages de l'opération courante + final memberIds = {}; + final memberNames = {}; + + for (final passage in _originalPassages) { + if (!memberIds.contains(passage.fkUser)) { + memberIds.add(passage.fkUser); + // Utiliser le nom du passage (qui contient prenom + nom) + memberNames[passage.fkUser] = passage.name.isNotEmpty ? passage.name : 'Membre #${passage.fkUser}'; + } + } + + // Trier par nom + final sortedMembers = memberIds.toList() + ..sort((a, b) => (memberNames[a] ?? '').compareTo(memberNames[b] ?? '')); + + return DropdownButtonFormField( + value: _selectedMemberId, + decoration: const InputDecoration( + border: OutlineInputBorder(), + contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), + isDense: true, + labelText: 'Membre', + prefixIcon: Icon(Icons.person, size: 20), + ), + items: [ + const DropdownMenuItem( + value: null, + child: Text('Tous'), + ), + ...sortedMembers.map((memberId) { + return DropdownMenuItem( + value: memberId, + child: Text( + memberNames[memberId] ?? 'Membre #$memberId', + overflow: TextOverflow.ellipsis, + ), + ); + }), + ], + onChanged: (int? newValue) { + setState(() { + _selectedMemberId = newValue; + }); + _applyFilters(); + }, + ); + } + + /// Construit le dropdown de sélection de secteur (admin uniquement) + Widget _buildSectorDropdown() { + // Récupérer les secteurs uniques depuis les passages de l'opération courante + final sectorIds = {}; + + for (final passage in _originalPassages) { + if (passage.fkSector != null && !sectorIds.contains(passage.fkSector)) { + sectorIds.add(passage.fkSector!); + } + } + + // Récupérer les noms des secteurs depuis le repository + final allSectors = sectorRepository.getAllSectors(); + final sectorNames = {}; + for (final sector in allSectors) { + if (sectorIds.contains(sector.id)) { + sectorNames[sector.id] = sector.libelle; + } + } + + // Trier par nom + final sortedSectors = sectorIds.toList() + ..sort((a, b) => (sectorNames[a] ?? '').compareTo(sectorNames[b] ?? '')); + + return DropdownButtonFormField( + value: _selectedSectorId, + decoration: const InputDecoration( + border: OutlineInputBorder(), + contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), + isDense: true, + labelText: 'Secteur', + prefixIcon: Icon(Icons.location_on, size: 20), + ), + items: [ + const DropdownMenuItem( + value: null, + child: Text('Tous'), + ), + ...sortedSectors.map((sectorId) { + return DropdownMenuItem( + value: sectorId, + child: Text( + sectorNames[sectorId] ?? 'Secteur #$sectorId', + overflow: TextOverflow.ellipsis, + ), + ); + }), + ], + onChanged: (int? newValue) { + setState(() { + _selectedSectorId = newValue; + }); + _applyFilters(); + }, + ); + } } diff --git a/docs/PLANNING-2026-Q1.md b/docs/PLANNING-2026-Q1.md old mode 100644 new mode 100755 index 52785288..f06a2af2 --- a/docs/PLANNING-2026-Q1.md +++ b/docs/PLANNING-2026-Q1.md @@ -31,11 +31,9 @@ |-------|------------|--------------------------------------------|--------| | 19/01 | `#13` Jour 1 | ✅ `#204` Design couleurs flashy | à livrer v3.6.3 | | 19/01 | | ✅ `#205` Écrans utilisateurs simplifiés | à livrer v3.6.3 | -| 20/01 | `#13` Jour 2 | `#113` Couleur repasses orange | | -| 20/01 | | `#72`Épaisseur police lisibilité | | -| 21/01 | `#13` Jour 3 | `#71`Visibilité bouton "Envoyer message" | | -| 21/01 | | `#59`Listing rues invisible (clavier) | | -| 22/01 | `#13` Jour 4 | `#42`Historique adresses cliquables | | +| 20/01 | `#13` Jour 2 | ✅ `#113` Couleur repasses orange | ✅ Validé 26/01 | +| 20/01 | | ✅ `#72` Épaisseur police lisibilité (theme + BtnPassages) | ✅ Livré 26/01 | +| 21/01 | `#13` Jour 3 | ✅ `#42` Filtres membre/secteur history (admin) | ✅ Livré 26/01 | | 23/01 | `#13` Jour 5 | `#74`Simplifier DashboardLayout/AppScaffold | | | 24/01 | | `#28`Gestion reçus Flutter nouveaux champs | | | 25/01 | | `#50`Modifier secteur au clic | | @@ -143,17 +141,28 @@ --- +## PHASE 7 : DIVERS (sans date) +### Tâches diverses - À planifier ultérieurement + +| ID | Tâche | Cat | Statut | +|------|----------------------------------------|------|--------| +| `#71` | Visibilité bouton "Envoyer message" | UX | | +| `#59` | Listing rues invisible (clavier) | UX | | + +--- + ## RÉCAPITULATIF | Phase | Période | Jours | Tâches | Focus | |-----------|--------------|-------|--------|---------------------| -| 1 | 16-18/01 | 3 | 5 | Bugs critiques | -| 2 | 19-25/01 | 7 | 10 | Stripe iOS + UX | +| 1 | 16-18/01 | 3 | 7 | Bugs critiques | +| 2 | 19-25/01 | 7 | 8 | Stripe iOS + UX | | 3 | 26/01-07/02 | 10 | 25 | MAP / Carte | | 4 | 08-14/02 | 6 | 11 | Stripe + Passages | | 5 | 15-22/02 | 7 | 22 | Admin + Membres | | 6 | 23-28/02 | 5 | 15 | Export + Divers | -| **TOTAL** | **44 jours** | | **88** | | +| 7 | Sans date | - | 2 | Divers | +| **TOTAL** | **44 jours** | | **90** | | ---