feat: Ajout filtres membre/secteur dans historique admin (#42)

- Ajout de 2 dropdowns de filtres dans history_page.dart (admin uniquement)
- Filtre par membre (fkUser) : liste dynamique depuis passages
- Filtre par secteur (fkSector) : liste dynamique depuis passages
- Valeurs par défaut : "Tous" pour chaque filtre
- Tri alphabétique des dropdowns
- Mise à jour du planning : #42 validée (26/01)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-01-26 10:53:07 +01:00
parent 5b6808db25
commit a392305820
2 changed files with 157 additions and 8 deletions

140
app/lib/presentation/pages/history_page.dart Normal file → Executable file
View File

@@ -55,6 +55,8 @@ class _HistoryContentState extends State<HistoryContent> {
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<HistoryContent> {
}
}
// 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<HistoryContent> {
children: [
// Barre de recherche
Expanded(
flex: 3,
child: TextFormField(
controller: _searchController,
decoration: const InputDecoration(
@@ -319,6 +336,21 @@ class _HistoryContentState extends State<HistoryContent> {
},
),
),
// 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<HistoryContent> {
}
}
}
/// 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 = <int>{};
final memberNames = <int, String>{};
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<int?>(
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<int?>(
value: null,
child: Text('Tous'),
),
...sortedMembers.map((memberId) {
return DropdownMenuItem<int?>(
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 = <int>{};
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 = <int, String>{};
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<int?>(
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<int?>(
value: null,
child: Text('Tous'),
),
...sortedSectors.map((sectorId) {
return DropdownMenuItem<int?>(
value: sectorId,
child: Text(
sectorNames[sectorId] ?? 'Secteur #$sectorId',
overflow: TextOverflow.ellipsis,
),
);
}),
],
onChanged: (int? newValue) {
setState(() {
_selectedSectorId = newValue;
});
_applyFilters();
},
);
}
}