import 'package:flutter/material.dart'; import 'package:p_tits_pas/services/user_service.dart'; /// Configuration pour personnaliser l'affichage des utilisateurs class UserDisplayConfig { final String title; final String role; final IconData defaultIcon; final List filterFields; final List actions; final String Function(Map) getSubtitle; final String Function(Map) getDisplayName; const UserDisplayConfig({ required this.title, required this.role, required this.defaultIcon, required this.filterFields, required this.actions, required this.getSubtitle, required this.getDisplayName, }); } /// Configuration d'un champ de filtre class FilterField { final String label; final String hint; final FilterType type; final List? options; final bool Function(Map, String) filter; const FilterField({ required this.label, required this.hint, required this.type, required this.filter, this.options, }); } enum FilterType { text, dropdown, number } /// Configuration d'une action sur un utilisateur class UserAction { final IconData icon; final Color color; final String tooltip; final Future Function(BuildContext, Map) onPressed; const UserAction({ required this.icon, required this.color, required this.tooltip, required this.onPressed, }); } /// Widget de gestion d'utilisateurs réutilisable class BaseUserManagementWidget extends StatefulWidget { final UserDisplayConfig config; const BaseUserManagementWidget({ super.key, required this.config, }); @override State createState() => _BaseUserManagementWidgetState(); } class _BaseUserManagementWidgetState extends State { final UserService _userService = UserService(); final Map _filterControllers = {}; List> _allUsers = []; List> _filteredUsers = []; bool _isLoading = true; String? _error; @override void initState() { super.initState(); _initializeFilters(); _loadUsers(); } void _initializeFilters() { for (final field in widget.config.filterFields) { _filterControllers[field.label] = TextEditingController(); } } @override void dispose() { for (final controller in _filterControllers.values) { controller.dispose(); } super.dispose(); } Future _loadUsers() async { setState(() { _isLoading = true; _error = null; }); try { final users = await _userService.getUsersByRole(widget.config.role); setState(() { _allUsers = users; _filteredUsers = users; _isLoading = false; }); } catch (e) { setState(() { _error = e.toString(); _isLoading = false; }); } } void _applyFilters() { setState(() { _filteredUsers = _allUsers.where((user) { return widget.config.filterFields.every((field) { final controller = _filterControllers[field.label]; if (controller == null || controller.text.isEmpty) return true; return field.filter(user, controller.text); }); }).toList(); }); } String _getStatusDisplay(Map user) { final status = user['statut']; if (status == null) return 'Non défini'; switch (status.toString().toLowerCase()) { case 'actif': return 'Actif'; case 'en attente': return 'En attente'; case 'inactif': return 'Inactif'; case 'deleted': return 'Supprimé'; default: return status.toString(); } } Color _getStatusColor(Map user) { final status = user['statut']?.toString().toLowerCase(); switch (status) { case 'actif': return Colors.green; case 'en attente': return Colors.orange; case 'inactif': return Colors.grey; case 'supprimé': return Colors.red; default: return Colors.grey; } } void _showUserDetails(Map user) { showDialog( context: context, builder: (context) => AlertDialog( title: Text(widget.config.getDisplayName(user)), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Email: ${user['email']}'), Text('Rôle: ${user['role']}'), Text('Statut: ${_getStatusDisplay(user)}'), Text('ID: ${user['id']}'), if (user['createdAt'] != null) Text( 'Créé le: ${DateTime.parse(user['createdAt']).toLocal().toString().split(' ')[0]}'), // Affichage des champs spécifiques selon le type d'utilisateur ...user.entries .where((e) => ![ 'id', 'email', 'role', 'status', 'createdAt', 'updatedAt', 'firstName', 'lastName' ].contains(e.key)) .map((e) => Text('${e.key}: ${e.value}')) .toList(), ], ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Fermer'), ), ], ), ); } @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '${widget.config.title} (${_filteredUsers.length})', style: Theme.of(context).textTheme.headlineSmall, ), IconButton( icon: const Icon(Icons.refresh), onPressed: _loadUsers, tooltip: 'Actualiser', ), ], ), const SizedBox(height: 16), _buildFilterSection(), const SizedBox(height: 16), Expanded( child: _buildUsersList(), ), ], ), ); } Widget _buildFilterSection() { return Wrap( spacing: 16, runSpacing: 8, children: widget.config.filterFields.map((field) { final controller = _filterControllers[field.label]!; switch (field.type) { case FilterType.text: case FilterType.number: return SizedBox( width: 250, child: TextField( controller: controller, keyboardType: field.type == FilterType.number ? TextInputType.number : TextInputType.text, decoration: InputDecoration( labelText: field.label, hintText: field.hint, border: const OutlineInputBorder(), prefixIcon: const Icon(Icons.search), ), onChanged: (value) => _applyFilters(), ), ); case FilterType.dropdown: return SizedBox( width: 200, child: DropdownButtonFormField( decoration: InputDecoration( labelText: field.label, border: const OutlineInputBorder(), ), items: [ const DropdownMenuItem(value: '', child: Text("Tous")), ...?field.options?.map((option) => DropdownMenuItem(value: option, child: Text(option))), ], onChanged: (value) { controller.text = value ?? ''; _applyFilters(); }, ), ); } }).toList(), ); } Widget _buildUsersList() { if (_isLoading) { return const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator(), SizedBox(height: 16), Text('Chargement...'), ], ), ); } if (_error != null) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.error_outline, size: 48, color: Colors.red[300]), const SizedBox(height: 16), Text( 'Erreur de chargement', style: Theme.of(context).textTheme.titleLarge, ), const SizedBox(height: 8), Text( _error!, textAlign: TextAlign.center, style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox(height: 16), ElevatedButton( onPressed: _loadUsers, child: const Text('Réessayer'), ), ], ), ); } if (_filteredUsers.isEmpty) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.people_outline, size: 48, color: Colors.grey[400]), const SizedBox(height: 16), Text( _allUsers.isEmpty ? 'Aucun utilisateur trouvé' : 'Aucun résultat', style: Theme.of(context).textTheme.titleLarge, ), if (_allUsers.isNotEmpty) ...[ const SizedBox(height: 8), const Text('Essayez de modifier vos critères de recherche'), ], ], ), ); } return ListView.builder( itemCount: _filteredUsers.length, itemBuilder: (context, index) { final user = _filteredUsers[index]; return Card( margin: const EdgeInsets.symmetric(vertical: 4), child: ListTile( leading: CircleAvatar( backgroundColor: _getStatusColor(user).withOpacity(0.2), child: Icon( widget.config.defaultIcon, color: _getStatusColor(user), ), ), title: Text(widget.config.getDisplayName(user)), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(widget.config.getSubtitle(user)), const SizedBox(height: 4), Row( children: [ Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 2), decoration: BoxDecoration( color: _getStatusColor(user).withOpacity(0.2), borderRadius: BorderRadius.circular(12), border: Border.all(color: _getStatusColor(user)), ), child: Text( _getStatusDisplay(user), style: TextStyle( color: _getStatusColor(user), fontSize: 12, fontWeight: FontWeight.w500, ), ), ), ], ), ], ), isThreeLine: true, trailing: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( icon: const Icon(Icons.visibility, color: Colors.blue), tooltip: "Voir détails", onPressed: () => _showUserDetails(user), ), ...widget.config.actions .map( (action) => IconButton( icon: Icon(action.icon, color: action.color), tooltip: action.tooltip, onPressed: () => action.onPressed(context, user), ), ) .toList(), ], ), ), ); }, ); } }