import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:youmazgestion/Models/users.dart'; import 'package:youmazgestion/Services/stock_managementDatabase.dart'; import '../Components/app_bar.dart'; import 'editUser.dart'; class ListUserPage extends StatefulWidget { const ListUserPage({super.key}); @override _ListUserPageState createState() => _ListUserPageState(); } class _ListUserPageState extends State { List userList = []; List filteredUserList = []; List> pointsDeVente = []; bool isLoading = true; String searchQuery = ''; int? selectedPointDeVenteFilter; final TextEditingController _searchController = TextEditingController(); @override void initState() { super.initState(); _loadData(); _searchController.addListener(_filterUsers); } @override void dispose() { _searchController.dispose(); super.dispose(); } Future _loadData() async { setState(() { isLoading = true; }); try { // Charger les utilisateurs et points de vente en parallèle final futures = await Future.wait([ AppDatabase.instance.getAllUsers(), AppDatabase.instance.getPointsDeVente(), ]); final users = futures[0] as List; final points = futures[1] as List>; setState(() { userList = users; filteredUserList = users; pointsDeVente = points; isLoading = false; }); } catch (e) { print('Erreur lors du chargement: $e'); setState(() { isLoading = false; }); _showErrorSnackbar('Erreur lors du chargement des données'); } } void _filterUsers() { final query = _searchController.text.toLowerCase(); setState(() { searchQuery = query; filteredUserList = userList.where((user) { final matchesSearch = query.isEmpty || user.name.toLowerCase().contains(query) || user.lastName.toLowerCase().contains(query) || user.username.toLowerCase().contains(query) || user.email.toLowerCase().contains(query) || (user.roleName?.toLowerCase().contains(query) ?? false); final matchesPointDeVente = selectedPointDeVenteFilter == null || user.pointDeVenteId == selectedPointDeVenteFilter; return matchesSearch && matchesPointDeVente; }).toList(); }); } void _onPointDeVenteFilterChanged(int? pointDeVenteId) { setState(() { selectedPointDeVenteFilter = pointDeVenteId; }); _filterUsers(); } String _getPointDeVenteName(int? pointDeVenteId) { if (pointDeVenteId == null) return 'Aucun'; try { final point = pointsDeVente.firstWhere((p) => p['id'] == pointDeVenteId); return point['nom'] ?? 'Inconnu'; } catch (e) { return 'Inconnu'; } } Future _deleteUser(Users user, int index) async { // Vérifier si l'utilisateur peut être supprimé final canDelete = await _checkCanDeleteUser(user); if (!canDelete['canDelete']) { _showCannotDeleteDialog(user, canDelete['reason']); return; } // Afficher la confirmation de suppression final confirmed = await _showDeleteConfirmation(user); if (!confirmed) return; try { await AppDatabase.instance.deleteUser(user.id!); setState(() { userList.removeWhere((u) => u.id == user.id); filteredUserList.removeWhere((u) => u.id == user.id); }); _showSuccessSnackbar('Utilisateur supprimé avec succès'); } catch (e) { print('Erreur lors de la suppression: $e'); _showErrorSnackbar('Erreur lors de la suppression de l\'utilisateur'); } } Future> _checkCanDeleteUser(Users user) async { // Ici vous pouvez ajouter des vérifications métier // Par exemple, vérifier si l'utilisateur a des commandes en cours, etc. // Pour l'instant, on autorise la suppression sauf pour le Super Admin if (user.roleName?.toLowerCase() == 'super admin') { return { 'canDelete': false, 'reason': 'Le Super Admin ne peut pas être supprimé pour des raisons de sécurité.' }; } // Vérifier s'il y a des commandes associées à cet utilisateur try { final db = AppDatabase.instance; final commandes = await db.database.then((connection) => connection.query('SELECT COUNT(*) as count FROM commandes WHERE commandeurId = ? OR validateurId = ?', [user.id, user.id]) ); final commandeCount = commandes.first['count'] as int; if (commandeCount > 0) { return { 'canDelete': false, 'reason': 'Cet utilisateur a $commandeCount commande(s) associée(s). Impossible de le supprimer.' }; } } catch (e) { print('Erreur lors de la vérification des contraintes: $e'); } return {'canDelete': true, 'reason': ''}; } Future _showDeleteConfirmation(Users user) async { return await showDialog( context: context, builder: (context) => AlertDialog( title: Row( children: const [ Icon(Icons.warning, color: Colors.orange), SizedBox(width: 8), Text("Confirmer la suppression"), ], ), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Êtes-vous sûr de vouloir supprimer cet utilisateur ?"), const SizedBox(height: 12), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.grey.shade100, borderRadius: BorderRadius.circular(8), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Nom: ${user.name} ${user.lastName}", style: const TextStyle(fontWeight: FontWeight.w500)), Text("Username: ${user.username}"), Text("Email: ${user.email}"), Text("Rôle: ${user.roleName ?? 'N/A'}"), Text("Point de vente: ${_getPointDeVenteName(user.pointDeVenteId)}"), ], ), ), const SizedBox(height: 12), const Text( "Cette action est irréversible.", style: TextStyle(color: Colors.red, fontStyle: FontStyle.italic), ), ], ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), child: const Text("Annuler"), ), ElevatedButton( onPressed: () => Navigator.of(context).pop(true), style: ElevatedButton.styleFrom( backgroundColor: Colors.red, foregroundColor: Colors.white, ), child: const Text("Supprimer"), ), ], ), ) ?? false; } void _showCannotDeleteDialog(Users user, String reason) { showDialog( context: context, builder: (context) => AlertDialog( title: Row( children: const [ Icon(Icons.block, color: Colors.red), SizedBox(width: 8), Text("Suppression impossible"), ], ), content: Column( mainAxisSize: MainAxisSize.min, children: [ Text( "L'utilisateur ${user.name} ${user.lastName} ne peut pas être supprimé.", style: const TextStyle(fontWeight: FontWeight.w500), ), const SizedBox(height: 12), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.red.shade50, borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.red.shade200), ), child: Row( children: [ const Icon(Icons.info, color: Colors.red, size: 20), const SizedBox(width: 8), Expanded(child: Text(reason)), ], ), ), ], ), actions: [ ElevatedButton( onPressed: () => Navigator.of(context).pop(), style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, foregroundColor: Colors.white, ), child: const Text("Compris"), ), ], ), ); } void _showUserDetails(Users user) { showDialog( context: context, builder: (context) => AlertDialog( title: Text("Détails de ${user.name} ${user.lastName}"), content: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ _buildDetailRow("ID", "${user.id}"), _buildDetailRow("Prénom", user.name), _buildDetailRow("Nom", user.lastName), _buildDetailRow("Username", user.username), _buildDetailRow("Email", user.email), _buildDetailRow("Rôle", user.roleName ?? 'N/A'), _buildDetailRow("Point de vente", _getPointDeVenteName(user.pointDeVenteId)), ], ), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text("Fermer"), ), ElevatedButton( onPressed: () { Navigator.of(context).pop(); Get.to(() => EditUserPage(user: user))?.then((_) => _loadData()); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, foregroundColor: Colors.white, ), child: const Text("Modifier"), ), ], ), ); } Widget _buildDetailRow(String label, String value) { return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 100, child: Text( "$label:", style: const TextStyle(fontWeight: FontWeight.w500), ), ), Expanded(child: Text(value)), ], ), ); } void _showSuccessSnackbar(String message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Row( children: [ const Icon(Icons.check_circle, color: Colors.white), const SizedBox(width: 8), Text(message), ], ), backgroundColor: Colors.green, duration: const Duration(seconds: 3), ), ); } void _showErrorSnackbar(String message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Row( children: [ const Icon(Icons.error, color: Colors.white), const SizedBox(width: 8), Text(message), ], ), backgroundColor: Colors.red, duration: const Duration(seconds: 4), ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: CustomAppBar(title: 'Liste des utilisateurs'), body: isLoading ? const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator(), SizedBox(height: 16), Text('Chargement des utilisateurs...'), ], ), ) : Column( children: [ // Barre de recherche et filtres Container( padding: const EdgeInsets.all(16), color: Colors.grey.shade50, child: Column( children: [ // Barre de recherche TextField( controller: _searchController, decoration: InputDecoration( hintText: 'Rechercher par nom, username, email...', prefixIcon: const Icon(Icons.search), suffixIcon: searchQuery.isNotEmpty ? IconButton( icon: const Icon(Icons.clear), onPressed: () { _searchController.clear(); }, ) : null, border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), ), filled: true, fillColor: Colors.white, ), ), const SizedBox(height: 12), // Filtre par point de vente Row( children: [ const Icon(Icons.filter_list, color: Colors.grey), const SizedBox(width: 8), const Text('Point de vente:'), const SizedBox(width: 12), Expanded( child: DropdownButtonFormField( value: selectedPointDeVenteFilter, decoration: InputDecoration( contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), border: OutlineInputBorder( borderRadius: BorderRadius.circular(6), ), filled: true, fillColor: Colors.white, ), items: [ const DropdownMenuItem( value: null, child: Text('Tous'), ), ...pointsDeVente.map((point) => DropdownMenuItem( value: point['id'] as int, child: Text(point['nom'] ?? 'N/A'), )), ], onChanged: _onPointDeVenteFilterChanged, ), ), ], ), ], ), ), // Statistiques Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), color: Colors.blue.shade50, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Total: ${userList.length} utilisateur(s)', style: const TextStyle(fontWeight: FontWeight.w500), ), if (filteredUserList.length != userList.length) Text( 'Affichés: ${filteredUserList.length}', style: TextStyle( color: Colors.blue.shade700, fontWeight: FontWeight.w500, ), ), ], ), ), // Liste des utilisateurs Expanded( child: filteredUserList.isEmpty ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( searchQuery.isNotEmpty ? Icons.search_off : Icons.people_outline, size: 64, color: Colors.grey.shade400, ), const SizedBox(height: 16), Text( searchQuery.isNotEmpty ? 'Aucun utilisateur trouvé' : 'Aucun utilisateur dans la base de données', style: TextStyle( fontSize: 16, color: Colors.grey.shade600, ), ), ], ), ) : RefreshIndicator( onRefresh: _loadData, child: ListView.builder( padding: const EdgeInsets.all(16), itemCount: filteredUserList.length, itemBuilder: (context, index) { Users user = filteredUserList[index]; return _buildUserCard(user, index); }, ), ), ), ], ), ); } Widget _buildUserCard(Users user, int index) { final pointDeVenteName = _getPointDeVenteName(user.pointDeVenteId); final isSuperAdmin = user.roleName?.toLowerCase() == 'super admin'; return Card( elevation: 2, margin: const EdgeInsets.only(bottom: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: isSuperAdmin ? BorderSide(color: Colors.orange.shade300, width: 1) : BorderSide.none, ), child: InkWell( onTap: () => _showUserDetails(user), borderRadius: BorderRadius.circular(12), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // En-tête avec nom et badge Row( children: [ CircleAvatar( backgroundColor: isSuperAdmin ? Colors.orange.shade100 : Colors.blue.shade100, child: Icon( isSuperAdmin ? Icons.admin_panel_settings : Icons.person, color: isSuperAdmin ? Colors.orange.shade700 : Colors.blue.shade700, ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "${user.name} ${user.lastName}", style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 16, ), ), Text( "@${user.username}", style: TextStyle( color: Colors.grey.shade600, fontSize: 14, ), ), ], ), ), if (isSuperAdmin) Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: Colors.orange.shade100, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.orange.shade300), ), child: Text( 'ADMIN', style: TextStyle( color: Colors.orange.shade800, fontSize: 10, fontWeight: FontWeight.bold, ), ), ), ], ), const SizedBox(height: 12), // Informations détaillées Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildInfoChip(Icons.email, user.email), const SizedBox(height: 4), _buildInfoChip(Icons.badge, user.roleName ?? 'N/A'), const SizedBox(height: 4), _buildInfoChip(Icons.store, pointDeVenteName), ], ), ), // Boutons d'actions Column( children: [ IconButton( icon: const Icon(Icons.visibility, size: 20), color: Colors.blue.shade600, onPressed: () => _showUserDetails(user), tooltip: 'Voir les détails', ), IconButton( icon: const Icon(Icons.edit, size: 20), color: Colors.green.shade600, onPressed: () { Get.to(() => EditUserPage(user: user))?.then((_) => _loadData()); }, tooltip: 'Modifier', ), IconButton( icon: Icon( isSuperAdmin ? Icons.lock : Icons.delete, size: 20, ), color: isSuperAdmin ? Colors.grey : Colors.red.shade600, onPressed: isSuperAdmin ? null : () => _deleteUser(user, index), tooltip: isSuperAdmin ? 'Protection Super Admin' : 'Supprimer', ), ], ), ], ), ], ), ), ), ); } Widget _buildInfoChip(IconData icon, String text) { return Row( children: [ Icon(icon, size: 14, color: Colors.grey.shade600), const SizedBox(width: 6), Expanded( child: Text( text, style: TextStyle( fontSize: 13, color: Colors.grey.shade700, ), overflow: TextOverflow.ellipsis, ), ), ], ); } }