From d41936441c140383876685247737a41346c9a783 Mon Sep 17 00:00:00 2001 From: "b.razafimandimbihery" Date: Tue, 27 May 2025 22:05:03 +0300 Subject: [PATCH] mis a jour gestion d'utilisateur --- lib/Components/appDrawer.dart | 38 +++- lib/Models/menu.dart | 94 ++++++++ lib/Services/app_database.dart | 353 +++++++++++++++++++---------- lib/Views/RoleListPage.dart | 215 ++++++++++++++++++ lib/Views/RolePermissionPage.dart | 177 +++++++++++++++ lib/Views/gestionRole.dart | 122 ++++++---- lib/Views/loginPage.dart | 2 +- lib/controller/userController.dart | 101 ++++++++- lib/main.dart | 5 +- 9 files changed, 919 insertions(+), 188 deletions(-) create mode 100644 lib/Models/menu.dart create mode 100644 lib/Views/RoleListPage.dart create mode 100644 lib/Views/RolePermissionPage.dart diff --git a/lib/Components/appDrawer.dart b/lib/Components/appDrawer.dart index 013a53c..c676b06 100644 --- a/lib/Components/appDrawer.dart +++ b/lib/Components/appDrawer.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:youmazgestion/Views/RoleListPage.dart'; import 'package:youmazgestion/Views/historique.dart'; import 'package:youmazgestion/Views/addProduct.dart'; import 'package:youmazgestion/Views/bilanMois.dart'; @@ -50,8 +51,21 @@ class CustomDrawer extends StatelessWidget { leading: const Icon(Icons.home), iconColor: Colors.lightBlueAccent, title: const Text("Accueil"), - onTap: () { - Get.to(const AccueilPage()); + onTap: () async { + bool hasPermission = await userController.hasPermission('view', '/accueil'); + if (hasPermission) { + Get.to(const AccueilPage()); + } else { + Get.snackbar( + "Accès refusé", + "Vous n'avez pas les droits pour accéder à cette page", + backgroundColor: Colors.red, + colorText: Colors.white, + icon: const Icon(Icons.error), + duration: const Duration(seconds: 3), + snackPosition: SnackPosition.TOP, + ); + } }, ), ListTile( @@ -59,7 +73,7 @@ class CustomDrawer extends StatelessWidget { iconColor: Colors.green, title: const Text("Ajouter un utilisateur"), onTap: () async { - bool hasPermission = await userController.hasAnyPermission(['create']); + bool hasPermission = await userController.hasPermission('create', '/ajouter-utilisateur'); if (hasPermission) { Get.to(const RegistrationPage()); } else { @@ -80,7 +94,7 @@ class CustomDrawer extends StatelessWidget { iconColor: const Color.fromARGB(255, 4, 54, 95), title: const Text("Modifier/Supprimer un utilisateur"), onTap: () async { - bool hasPermission = await userController.hasAnyPermission(['update', 'delete']); + bool hasPermission = await userController.hasPermission('update', '/modifier-utilisateur'); if (hasPermission) { Get.to(const ListUserPage()); } else { @@ -101,7 +115,7 @@ class CustomDrawer extends StatelessWidget { iconColor: Colors.indigoAccent, title: const Text("Ajouter un produit"), onTap: () async { - bool hasPermission = await userController.hasAnyPermission(['create']); + bool hasPermission = await userController.hasPermission('create', '/ajouter-produit'); if (hasPermission) { Get.to(const AddProductPage()); } else { @@ -122,7 +136,7 @@ class CustomDrawer extends StatelessWidget { iconColor: Colors.redAccent, title: const Text("Modifier/Supprimer un produit"), onTap: () async { - bool hasPermission = await userController.hasAnyPermission(['update', 'delete']); + bool hasPermission = await userController.hasPermission('update', '/modifier-produit'); if (hasPermission) { Get.to(GestionProduit()); } else { @@ -142,7 +156,7 @@ class CustomDrawer extends StatelessWidget { leading: const Icon(Icons.bar_chart), title: const Text("Bilan"), onTap: () async { - bool hasPermission = await userController.hasAnyPermission(['read']); + bool hasPermission = await userController.hasPermission('read', '/bilan'); if (hasPermission) { Get.to(const BilanMois()); } else { @@ -162,12 +176,14 @@ class CustomDrawer extends StatelessWidget { leading: const Icon(Icons.warning_amber), title: const Text("Gérer les rôles"), onTap: () async { - bool hasPermission = await userController.hasAnyPermission(['update', 'delete','create']); + bool hasPermission = await userController.hasPermission('admin', '/gerer-roles'); if (hasPermission) { - Get.to(const HandleUserRole()); + Get.to(const RoleListPage()); + print("permission accepted"); } else { + print("permission not accepted for" +userController.username); Get.snackbar( - "Accès refusé", + "Accès refusé ", "Vous n'avez pas les droits pour gérer les rôles", backgroundColor: Colors.red, colorText: Colors.white, @@ -183,7 +199,7 @@ class CustomDrawer extends StatelessWidget { iconColor: Colors.blueAccent, title: const Text("Gestion de stock"), onTap: () async { - bool hasPermission = await userController.hasAnyPermission(['update']); + bool hasPermission = await userController.hasPermission('update', '/gestion-stock'); if (hasPermission) { Get.to(const GestionStockPage()); } else { diff --git a/lib/Models/menu.dart b/lib/Models/menu.dart new file mode 100644 index 0000000..546c91f --- /dev/null +++ b/lib/Models/menu.dart @@ -0,0 +1,94 @@ +// Models/menu.dart +class Menu { + final int? id; + final String title; + final String icon; + final String route; + final int orderIndex; + final bool isActive; + final int? parentId; + + Menu({ + this.id, + required this.title, + required this.icon, + required this.route, + this.orderIndex = 0, + this.isActive = true, + this.parentId, + }); + + Map toMap() { + return { + 'id': id, + 'title': title, + 'icon': icon, + 'route': route, + 'order_index': orderIndex, + 'is_active': isActive ? 1 : 0, + 'parent_id': parentId, + }; + } + + factory Menu.fromMap(Map map) { + return Menu( + id: map['id']?.toInt(), + title: map['title'] ?? '', + icon: map['icon'] ?? '', + route: map['route'] ?? '', + orderIndex: map['order_index']?.toInt() ?? 0, + isActive: (map['is_active'] ?? 1) == 1, + parentId: map['parent_id']?.toInt(), + ); + } + + Menu copyWith({ + int? id, + String? title, + String? icon, + String? route, + int? orderIndex, + bool? isActive, + int? parentId, + }) { + return Menu( + id: id ?? this.id, + title: title ?? this.title, + icon: icon ?? this.icon, + route: route ?? this.route, + orderIndex: orderIndex ?? this.orderIndex, + isActive: isActive ?? this.isActive, + parentId: parentId ?? this.parentId, + ); + } + + @override + String toString() { + return 'Menu(id: $id, title: $title, icon: $icon, route: $route, orderIndex: $orderIndex, isActive: $isActive, parentId: $parentId)'; + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is Menu && + other.id == id && + other.title == title && + other.icon == icon && + other.route == route && + other.orderIndex == orderIndex && + other.isActive == isActive && + other.parentId == parentId; + } + + @override + int get hashCode { + return id.hashCode ^ + title.hashCode ^ + icon.hashCode ^ + route.hashCode ^ + orderIndex.hashCode ^ + isActive.hashCode ^ + parentId.hashCode; + } +} \ No newline at end of file diff --git a/lib/Services/app_database.dart b/lib/Services/app_database.dart index 6391e74..a2669dc 100644 --- a/lib/Services/app_database.dart +++ b/lib/Services/app_database.dart @@ -26,6 +26,7 @@ class AppDatabase { _database = await _initDB('app_database.db'); await _createDB(_database, 1); await insertDefaultPermissions(); + await insertDefaultMenus(); await insertDefaultRoles(); await insertDefaultSuperAdmin(); } @@ -36,13 +37,11 @@ class AppDatabase { bool dbExists = await File(path).exists(); if (!dbExists) { - // Optionnel : copier depuis assets si vous avez une DB pré-remplie try { ByteData data = await rootBundle.load('assets/database/$filePath'); List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); await File(path).writeAsBytes(bytes); } catch (e) { - // Si pas de fichier dans assets, on continue avec une DB vide print('Pas de fichier DB dans assets, création d\'une nouvelle DB'); } } @@ -54,7 +53,6 @@ class AppDatabase { final tables = await db.rawQuery("SELECT name FROM sqlite_master WHERE type='table'"); final tableNames = tables.map((row) => row['name'] as String).toList(); - // Table des rôles if (!tableNames.contains('roles')) { await db.execute(''' CREATE TABLE roles ( @@ -65,7 +63,6 @@ class AppDatabase { print("Table 'roles' créée."); } - // Table des permissions if (!tableNames.contains('permissions')) { await db.execute(''' CREATE TABLE permissions ( @@ -76,7 +73,17 @@ class AppDatabase { print("Table 'permissions' créée."); } - // Table de liaison role_permissions + if (!tableNames.contains('menu')) { + await db.execute(''' + CREATE TABLE menu ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE, + route TEXT NOT NULL UNIQUE + ) + '''); + print("Table 'menu' créée."); + } + if (!tableNames.contains('role_permissions')) { await db.execute(''' CREATE TABLE role_permissions ( @@ -90,7 +97,19 @@ class AppDatabase { print("Table 'role_permissions' créée."); } - // Table des utilisateurs + if (!tableNames.contains('menu_permissions')) { + await db.execute(''' + CREATE TABLE menu_permissions ( + menu_id INTEGER, + permission_id INTEGER, + PRIMARY KEY (menu_id, permission_id), + FOREIGN KEY (menu_id) REFERENCES menu(id) ON DELETE CASCADE, + FOREIGN KEY (permission_id) REFERENCES permissions(id) ON DELETE CASCADE + ) + '''); + print("Table 'menu_permissions' créée."); + } + if (!tableNames.contains('users')) { await db.execute(''' CREATE TABLE users ( @@ -106,9 +125,22 @@ class AppDatabase { '''); print("Table 'users' créée."); } - } + if (!tableNames.contains('role_menu_permissions')) { + await db.execute(''' + CREATE TABLE role_menu_permissions ( + role_id INTEGER, + menu_id INTEGER, + permission_id INTEGER, + PRIMARY KEY (role_id, menu_id, permission_id), + FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE, + FOREIGN KEY (menu_id) REFERENCES menu(id) ON DELETE CASCADE, + FOREIGN KEY (permission_id) REFERENCES permissions(id) ON DELETE CASCADE + ) + '''); + print("Table 'role_menu_permissions' créée."); +} - // ========== INSERTION DES DONNÉES PAR DÉFAUT ========== + } Future insertDefaultPermissions() async { final db = await database; @@ -123,93 +155,142 @@ class AppDatabase { } } - Future insertDefaultRoles() async { + Future insertDefaultMenus() async { final db = await database; - final existingRoles = await db.query('roles'); - - if (existingRoles.isEmpty) { - // Créer le rôle Super Admin - int superAdminRoleId = await db.insert('roles', {'designation': 'Super Admin'}); - - // Créer d'autres rôles de base - int adminRoleId = await db.insert('roles', {'designation': 'Admin'}); - int userRoleId = await db.insert('roles', {'designation': 'User'}); - - // Assigner toutes les permissions au Super Admin - final permissions = await db.query('permissions'); + final existingMenus = await db.query('menu'); + + if (existingMenus.isEmpty) { + await db.insert('menu', {'name': 'Accueil', 'route': '/accueil'}); + await db.insert('menu', {'name': 'Ajouter un utilisateur', 'route': '/ajouter-utilisateur'}); + await db.insert('menu', {'name': 'Modifier/Supprimer un utilisateur', 'route': '/modifier-utilisateur'}); + await db.insert('menu', {'name': 'Ajouter un produit', 'route': '/ajouter-produit'}); + await db.insert('menu', {'name': 'Modifier/Supprimer un produit', 'route': '/modifier-produit'}); + await db.insert('menu', {'name': 'Bilan', 'route': '/bilan'}); + await db.insert('menu', {'name': 'Gérer les rôles', 'route': '/gerer-roles'}); + await db.insert('menu', {'name': 'Gestion de stock', 'route': '/gestion-stock'}); + await db.insert('menu', {'name': 'Historique', 'route': '/historique'}); + await db.insert('menu', {'name': 'Déconnexion', 'route': '/deconnexion'}); + print("Menus par défaut insérés"); + } + } + + Future insertDefaultRoles() async { + final db = await database; + final existingRoles = await db.query('roles'); + + if (existingRoles.isEmpty) { + int superAdminRoleId = await db.insert('roles', {'designation': 'Super Admin'}); + int adminRoleId = await db.insert('roles', {'designation': 'Admin'}); + int userRoleId = await db.insert('roles', {'designation': 'User'}); + + final permissions = await db.query('permissions'); + final menus = await db.query('menu'); + + // Assigner toutes les permissions à tous les menus pour le Super Admin + for (var menu in menus) { for (var permission in permissions) { - await db.insert('role_permissions', { + await db.insert('role_menu_permissions', { 'role_id': superAdminRoleId, + 'menu_id': menu['id'], 'permission_id': permission['id'], }); } - - // Assigner quelques permissions à l'Admin - final viewPermission = await db.query('permissions', where: 'name = ?', whereArgs: ['view']); - final createPermission = await db.query('permissions', where: 'name = ?', whereArgs: ['create']); - final updatePermission = await db.query('permissions', where: 'name = ?', whereArgs: ['update']); - - if (viewPermission.isNotEmpty) { - await db.insert('role_permissions', { - 'role_id': adminRoleId, - 'permission_id': viewPermission.first['id'], - }); - } - if (createPermission.isNotEmpty) { - await db.insert('role_permissions', { - 'role_id': adminRoleId, - 'permission_id': createPermission.first['id'], - }); - } - if (updatePermission.isNotEmpty) { - await db.insert('role_permissions', { - 'role_id': adminRoleId, - 'permission_id': updatePermission.first['id'], - }); - } - - // Assigner seulement la permission view à User - if (viewPermission.isNotEmpty) { - await db.insert('role_permissions', { - 'role_id': userRoleId, - 'permission_id': viewPermission.first['id'], - }); + } + + // Assigner quelques permissions à l'Admin et à l'User + final viewPermission = await db.query('permissions', where: 'name = ?', whereArgs: ['view']); + final createPermission = await db.query('permissions', where: 'name = ?', whereArgs: ['create']); + final updatePermission = await db.query('permissions', where: 'name = ?', whereArgs: ['update']); + + if (viewPermission.isNotEmpty) { + await db.insert('role_menu_permissions', { + 'role_id': adminRoleId, + 'menu_id': 1, // Assurez-vous que l'ID du menu est correct + 'permission_id': viewPermission.first['id'], + }); + await db.insert('role_menu_permissions', { + 'role_id': userRoleId, + 'menu_id': 1, // Assurez-vous que l'ID du menu est correct + 'permission_id': viewPermission.first['id'], + }); + } + + if (createPermission.isNotEmpty) { + await db.insert('role_menu_permissions', { + 'role_id': adminRoleId, + 'menu_id': 1, // Assurez-vous que l'ID du menu est correct + 'permission_id': createPermission.first['id'], + }); + } + + if (updatePermission.isNotEmpty) { + await db.insert('role_menu_permissions', { + 'role_id': adminRoleId, + 'menu_id': 1, // Assurez-vous que l'ID du menu est correct + 'permission_id': updatePermission.first['id'], + }); + } + + print("Rôles par défaut créés et permissions assignées"); + } else { + // Si les rôles existent déjà, vérifier et ajouter les permissions manquantes pour le Super Admin + final superAdminRole = await db.query('roles', where: 'designation = ?', whereArgs: ['Super Admin']); + if (superAdminRole.isNotEmpty) { + final superAdminRoleId = superAdminRole.first['id'] as int; + final permissions = await db.query('permissions'); + final menus = await db.query('menu'); + + // Vérifier et ajouter les permissions manquantes pour le Super Admin + for (var menu in menus) { + for (var permission in permissions) { + final existingPermission = await db.query( + 'role_menu_permissions', + where: 'role_id = ? AND menu_id = ? AND permission_id = ?', + whereArgs: [superAdminRoleId, menu['id'], permission['id']], + ); + if (existingPermission.isEmpty) { + await db.insert('role_menu_permissions', { + 'role_id': superAdminRoleId, + 'menu_id': menu['id'], + 'permission_id': permission['id'], + }); + } + } } - - print("Rôles par défaut créés et permissions assignées"); + + print("Permissions manquantes ajoutées pour le Super Admin"); } } +} + Future insertDefaultSuperAdmin() async { final db = await database; - - // Vérifier si un super admin existe déjà + final existingSuperAdmin = await db.rawQuery(''' SELECT u.* FROM users u INNER JOIN roles r ON u.role_id = r.id WHERE r.designation = 'Super Admin' '''); - + if (existingSuperAdmin.isEmpty) { - // Récupérer l'ID du rôle Super Admin - final superAdminRole = await db.query('roles', - where: 'designation = ?', + final superAdminRole = await db.query('roles', + where: 'designation = ?', whereArgs: ['Super Admin'] ); - + if (superAdminRole.isNotEmpty) { final superAdminRoleId = superAdminRole.first['id'] as int; - - // Créer l'utilisateur Super Admin + await db.insert('users', { 'name': 'Super', 'lastname': 'Admin', 'email': 'superadmin@youmazgestion.com', - 'password': 'admin123', // CHANGEZ CE MOT DE PASSE ! + 'password': 'admin123', 'username': 'superadmin', 'role_id': superAdminRoleId, }); - + print("Super Admin créé avec succès !"); print("Username: superadmin"); print("Password: admin123"); @@ -220,8 +301,6 @@ class AppDatabase { } } - // ========== GESTION DES UTILISATEURS ========== - Future createUser(Users user) async { final db = await database; return await db.insert('users', user.toMap()); @@ -247,7 +326,7 @@ class AppDatabase { final db = await database; final result = await db.rawQuery(''' SELECT users.id - FROM users + FROM users WHERE users.username = ? AND users.password = ? ''', [username, password]); return result.isNotEmpty; @@ -256,9 +335,9 @@ class AppDatabase { Future getUser(String username) async { final db = await database; final result = await db.rawQuery(''' - SELECT users.*, roles.designation as role_name - FROM users - INNER JOIN roles ON users.role_id = roles.id + SELECT users.*, roles.designation as role_name + FROM users + INNER JOIN roles ON users.role_id = roles.id WHERE users.username = ? ''', [username]); @@ -273,8 +352,8 @@ class AppDatabase { final db = await database; final result = await db.rawQuery(''' SELECT users.username, users.id, roles.designation as role_name, roles.id as role_id - FROM users - INNER JOIN roles ON users.role_id = roles.id + FROM users + INNER JOIN roles ON users.role_id = roles.id WHERE username = ? AND password = ? ''', [username, password]); @@ -293,16 +372,14 @@ class AppDatabase { Future> getAllUsers() async { final db = await database; final result = await db.rawQuery(''' - SELECT users.*, roles.designation as role_name - FROM users - INNER JOIN roles ON users.role_id = roles.id + SELECT users.*, roles.designation as role_name + FROM users + INNER JOIN roles ON users.role_id = roles.id ORDER BY users.id ASC '''); return result.map((json) => Users.fromMap(json)).toList(); } - // ========== GESTION DES RÔLES ========== - Future createRole(Role role) async { final db = await database; return await db.insert('roles', role.toMap()); @@ -333,8 +410,6 @@ class AppDatabase { ); } - // ========== GESTION DES PERMISSIONS ========== - Future> getAllPermissions() async { final db = await database; final result = await db.query('permissions', orderBy: 'name ASC'); @@ -386,7 +461,22 @@ class AppDatabase { ); } - // ========== MÉTHODES UTILITAIRES POUR LA SÉCURITÉ ========== + Future assignMenuPermission(int menuId, int permissionId) async { + final db = await database; + await db.insert('menu_permissions', { + 'menu_id': menuId, + 'permission_id': permissionId, + }, conflictAlgorithm: ConflictAlgorithm.ignore); + } + + Future removeMenuPermission(int menuId, int permissionId) async { + final db = await database; + await db.delete( + 'menu_permissions', + where: 'menu_id = ? AND permission_id = ?', + whereArgs: [menuId, permissionId], + ); + } Future isSuperAdmin(String username) async { final db = await database; @@ -396,20 +486,18 @@ class AppDatabase { INNER JOIN roles r ON u.role_id = r.id WHERE u.username = ? AND r.designation = 'Super Admin' ''', [username]); - + return (result.first['count'] as int) > 0; } Future changePassword(String username, String oldPassword, String newPassword) async { final db = await database; - - // Vérifier l'ancien mot de passe + final isValidOldPassword = await verifyUser(username, oldPassword); if (!isValidOldPassword) { throw Exception('Ancien mot de passe incorrect'); } - - // Changer le mot de passe + await db.update( 'users', {'password': newPassword}, @@ -418,7 +506,21 @@ class AppDatabase { ); } - // ========== UTILITAIRES ========== + Future hasPermission(String username, String permissionName, String menuRoute) async { + final db = await database; + final result = await db.rawQuery(''' + SELECT COUNT(*) as count + FROM permissions p + JOIN role_menu_permissions rmp ON p.id = rmp.permission_id + JOIN roles r ON rmp.role_id = r.id + JOIN users u ON u.role_id = r.id + JOIN menu m ON m.route = ? + WHERE u.username = ? AND p.name = ? AND rmp.menu_id = m.id + ''', [menuRoute, username, permissionName]); + + return (result.first['count'] as int) > 0; +} + Future close() async { if (_database.isOpen) { @@ -426,52 +528,75 @@ class AppDatabase { } } - Future hasPermission(String username, String permissionName) async { - final db = await database; - final result = await db.rawQuery(''' - SELECT COUNT(*) as count - FROM permissions p - JOIN role_permissions rp ON p.id = rp.permission_id - JOIN roles r ON rp.role_id = r.id - JOIN users u ON u.role_id = r.id - WHERE u.username = ? AND p.name = ? - ''', [username, permissionName]); - - return (result.first['count'] as int) > 0; - } - - // ========== MÉTHODE DE DEBUG ========== - Future printDatabaseInfo() async { final db = await database; - + print("=== INFORMATIONS DE LA BASE DE DONNÉES ==="); - - // Compter les utilisateurs + final userCount = await getUserCount(); print("Nombre d'utilisateurs: $userCount"); - - // Lister tous les utilisateurs + final users = await getAllUsers(); print("Utilisateurs:"); for (var user in users) { print(" - ${user.username} (${user.name} ) - Email: ${user.email}"); } - - // Lister tous les rôles + final roles = await getRoles(); print("Rôles:"); for (var role in roles) { print(" - ${role.designation} (ID: ${role.id})"); } - - // Lister toutes les permissions + final permissions = await getAllPermissions(); print("Permissions:"); for (var permission in permissions) { print(" - ${permission.name} (ID: ${permission.id})"); } - + print("========================================="); } + + Future> getPermissionsForRoleAndMenu(int roleId, int menuId) async { + final db = await database; + final result = await db.rawQuery(''' + SELECT p.id, p.name + FROM permissions p + JOIN role_menu_permissions rmp ON p.id = rmp.permission_id + WHERE rmp.role_id = ? AND rmp.menu_id = ? + ORDER BY p.name ASC + ''', [roleId, menuId]); + + return result.map((map) => Permission.fromMap(map)).toList(); +} + // Ajoutez cette méthode temporaire pour supprimer la DB corrompue +Future deleteDatabaseFile() async { + final documentsDirectory = await getApplicationDocumentsDirectory(); + final path = join(documentsDirectory.path, 'app_database.db'); + final file = File(path); + if (await file.exists()) { + await file.delete(); + print("Base de données supprimée"); + } +} +Future assignRoleMenuPermission(int roleId, int menuId, int permissionId) async { + final db = await database; + await db.insert('role_menu_permissions', { + 'role_id': roleId, + 'menu_id': menuId, + 'permission_id': permissionId, + }, conflictAlgorithm: ConflictAlgorithm.ignore); +} + + + +Future removeRoleMenuPermission(int roleId, int menuId, int permissionId) async { + final db = await database; + await db.delete( + 'role_menu_permissions', + where: 'role_id = ? AND menu_id = ? AND permission_id = ?', + whereArgs: [roleId, menuId, permissionId], + ); +} + } \ No newline at end of file diff --git a/lib/Views/RoleListPage.dart b/lib/Views/RoleListPage.dart new file mode 100644 index 0000000..5a1d40c --- /dev/null +++ b/lib/Views/RoleListPage.dart @@ -0,0 +1,215 @@ +import 'package:flutter/material.dart'; +import 'package:youmazgestion/Components/app_bar.dart'; +import 'package:youmazgestion/Models/Permission.dart'; +import 'package:youmazgestion/Services/app_database.dart'; +import 'package:youmazgestion/Models/role.dart'; +import 'package:youmazgestion/Views/RolePermissionPage.dart'; + +class RoleListPage extends StatefulWidget { + const RoleListPage({super.key}); + + @override + State createState() => _RoleListPageState(); +} + +class _RoleListPageState extends State { + final db = AppDatabase.instance; + final TextEditingController _roleController = TextEditingController(); + List roles = []; + + @override + void initState() { + super.initState(); + _loadRoles(); + } + + Future _loadRoles() async { + final roleList = await db.getRoles(); + setState(() { + roles = roleList; + }); + } + + Future _addRole() async { + String designation = _roleController.text.trim(); + if (designation.isEmpty) return; + + await db.createRole(Role(designation: designation)); + _roleController.clear(); + await _loadRoles(); + } + + Future _deleteRole(int roleId) async { + await db.deleteRole(roleId); + await _loadRoles(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: const CustomAppBar(title: "Gestion des rôles"), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + // Section d'ajout de rôle + Card( + elevation: 4, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + Text( + 'Ajouter un nouveau rôle', + style: Theme.of(context).textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 10), + Row( + children: [ + Expanded( + child: TextField( + controller: _roleController, + decoration: InputDecoration( + labelText: 'Nom du rôle', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 12), + ), + ), + ), + const SizedBox(width: 10), + ElevatedButton( + onPressed: _addRole, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + // padding: const EdgeInsets.symmetric( + // horizontal: 20, vertical: 15), + ), + + ), + child: const Text('Ajouter'), + ) + ], + ), + ], + ), + ), + ), + const SizedBox(height: 20), + // Liste des rôles existants + Expanded( + child: Card( + elevation: 4, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Liste des rôles', + style: Theme.of(context).textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 10), + Expanded( + child: roles.isEmpty + ? const Center( + child: Text('Aucun rôle créé'), + ) + : ListView.builder( + itemCount: roles.length, + itemBuilder: (context, index) { + final role = roles[index]; + return Card( + margin: const EdgeInsets.only(bottom: 8), + elevation: 2, + child: ListTile( + title: Text(role.designation), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.edit, + color: Colors.blue), + onPressed: () { + _navigateToRolePermissions( + context, role); + }, + ), + IconButton( + icon: const Icon(Icons.delete, + color: Colors.red), + onPressed: () { + _showDeleteDialog(role); + }, + ), + ], + ), + onTap: () { + _navigateToRolePermissions( + context, role); + }, + ), + ); + }, + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + ); + } + + void _navigateToRolePermissions(BuildContext context, Role role) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => RolePermissionsPage(role: role), + ), + ); + } + + void _showDeleteDialog(Role role) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Confirmer la suppression'), + content: Text( + 'Êtes-vous sûr de vouloir supprimer le rôle "${role.designation}" ?'), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('Annuler'), + ), + TextButton( + onPressed: () async { + Navigator.of(context).pop(); + await _deleteRole(role.id!); + }, + child: const Text('Supprimer', style: TextStyle(color: Colors.red)), + ), + ], + ); + }, + ); + } +} \ No newline at end of file diff --git a/lib/Views/RolePermissionPage.dart b/lib/Views/RolePermissionPage.dart new file mode 100644 index 0000000..58a6511 --- /dev/null +++ b/lib/Views/RolePermissionPage.dart @@ -0,0 +1,177 @@ +import 'package:flutter/material.dart'; +import 'package:youmazgestion/Components/app_bar.dart'; +import 'package:youmazgestion/Models/Permission.dart'; +import 'package:youmazgestion/Services/app_database.dart'; +import 'package:youmazgestion/Models/role.dart'; + +class RolePermissionsPage extends StatefulWidget { + final Role role; + + const RolePermissionsPage({super.key, required this.role}); + + @override + State createState() => _RolePermissionsPageState(); +} + +class _RolePermissionsPageState extends State { + final db = AppDatabase.instance; + List permissions = []; + List> menus = []; + Map> menuPermissionsMap = {}; + + @override + void initState() { + super.initState(); + _initData(); + } + + Future _initData() async { + final perms = await db.getAllPermissions(); + final menuList = await db.database.then((db) => db.query('menu')); + + Map> tempMenuPermissionsMap = {}; + + for (var menu in menuList) { + final menuId = menu['id'] as int; + final menuPerms = await db.getPermissionsForRoleAndMenu( + widget.role.id!, menuId); + + tempMenuPermissionsMap[menuId] = { + for (var perm in perms) + perm.name: menuPerms.any((mp) => mp.name == perm.name) + }; + } + + setState(() { + permissions = perms; + menus = menuList; + menuPermissionsMap = tempMenuPermissionsMap; + }); + } + + Future _onPermissionToggle( + int menuId, String permission, bool enabled) async { + final perm = permissions.firstWhere((p) => p.name == permission); + + if (enabled) { + await db.assignRoleMenuPermission( + widget.role.id!, menuId, perm.id!); + } else { + await db.removeRoleMenuPermission( + widget.role.id!, menuId, perm.id!); + } + + setState(() { + menuPermissionsMap[menuId]![permission] = enabled; + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + title: "Permissions - ${widget.role.designation}", + // showBackButton: true, + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Gestion des permissions pour le rôle: ${widget.role.designation}', + style: Theme.of(context).textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 10), + const Text( + 'Sélectionnez les permissions pour chaque menu:', + style: TextStyle(fontSize: 14, color: Colors.grey), + ), + const SizedBox(height: 20), + if (permissions.isNotEmpty && menus.isNotEmpty) + Expanded( + child: ListView.builder( + itemCount: menus.length, + itemBuilder: (context, index) { + final menu = menus[index]; + final menuId = menu['id'] as int; + final menuName = menu['name'] as String; + + return Card( + margin: const EdgeInsets.only(bottom: 15), + elevation: 3, + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + menuName, + style: const TextStyle( + fontWeight: FontWeight.bold, fontSize: 16), + ), + const SizedBox(height: 8), + Wrap( + spacing: 10, + runSpacing: 10, + children: permissions.map((perm) { + final isChecked = menuPermissionsMap[menuId]?[perm.name] ?? false; + return FilterChip( + label: perm.name, + selected: isChecked, + onSelected: (bool value) { + _onPermissionToggle(menuId, perm.name, value); + }, + ); + }).toList(), + ), + ], + ), + ), + ); + }, + ), + ) + else + const Expanded( + child: Center( + child: CircularProgressIndicator(), + ), + ), + ], + ), + ), + ); + } +} + +class FilterChip extends StatelessWidget { + final String label; + final bool selected; + final ValueChanged onSelected; + + const FilterChip({ + super.key, + required this.label, + required this.selected, + required this.onSelected, + }); + + @override + Widget build(BuildContext context) { + return ChoiceChip( + label: Text(label), + selected: selected, + onSelected: onSelected, + selectedColor: Colors.blue, + labelStyle: TextStyle( + color: selected ? Colors.white : Colors.black, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + ); + } +} \ No newline at end of file diff --git a/lib/Views/gestionRole.dart b/lib/Views/gestionRole.dart index 399d455..5b32de0 100644 --- a/lib/Views/gestionRole.dart +++ b/lib/Views/gestionRole.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:youmazgestion/Components/app_bar.dart'; +import 'package:youmazgestion/Models/Permission.dart'; import 'package:youmazgestion/Services/app_database.dart'; import 'package:youmazgestion/Models/role.dart'; @@ -13,9 +14,10 @@ class HandleUserRole extends StatefulWidget { class _HandleUserRoleState extends State { final db = AppDatabase.instance; - List> roles = []; - List permissions = []; - Map> rolePermissionsMap = {}; + List roles = []; + List permissions = []; + List> menus = []; + Map>> roleMenuPermissionsMap = {}; final TextEditingController _roleController = TextEditingController(); @@ -26,25 +28,32 @@ class _HandleUserRoleState extends State { } Future _initData() async { - final roleList = await db.database.then((db) => db.query('roles')); + final roleList = await db.getRoles(); final perms = await db.getAllPermissions(); + final menuList = await db.database.then((db) => db.query('menu')); - Map> tempRolePermissionsMap = {}; + Map>> tempRoleMenuPermissionsMap = {}; for (var role in roleList) { - final roleId = role['id'] as int; - final rolePerms = await db.getPermissionsForRole(roleId); - - tempRolePermissionsMap[roleId] = { - for (var perm in perms) - perm.name: rolePerms.any((rp) => rp.name == perm.name) - }; + final roleId = role.id!; + tempRoleMenuPermissionsMap[roleId] = {}; + + for (var menu in menuList) { + final menuId = menu['id'] as int; + final menuPerms = await db.getPermissionsForRoleAndMenu(roleId, menuId); + + tempRoleMenuPermissionsMap[roleId]![menuId] = { + for (var perm in perms) + perm.name: menuPerms.any((mp) => mp.name == perm.name) + }; + } } setState(() { roles = roleList; permissions = perms; - rolePermissionsMap = tempRolePermissionsMap; + menus = menuList; + roleMenuPermissionsMap = tempRoleMenuPermissionsMap; }); } @@ -57,17 +66,17 @@ class _HandleUserRoleState extends State { await _initData(); } - Future _onPermissionToggle(int roleId, String permission, bool enabled) async { + Future _onPermissionToggle(int roleId, int menuId, String permission, bool enabled) async { final perm = permissions.firstWhere((p) => p.name == permission); if (enabled) { - await db.assignPermission(roleId, perm.id!); + await db.assignRoleMenuPermission(roleId, menuId, perm.id!); } else { - await db.removePermission(roleId, perm.id!); + await db.removeRoleMenuPermission(roleId, menuId, perm.id!); } setState(() { - rolePermissionsMap[roleId]![permission] = enabled; + roleMenuPermissionsMap[roleId]![menuId]![permission] = enabled; }); } @@ -117,7 +126,7 @@ class _HandleUserRoleState extends State { ), const SizedBox(height: 20), // Tableau des rôles et permissions - if (roles.isNotEmpty && permissions.isNotEmpty) + if (roles.isNotEmpty && permissions.isNotEmpty && menus.isNotEmpty) Expanded( child: Card( elevation: 6, @@ -132,38 +141,51 @@ class _HandleUserRoleState extends State { constraints: BoxConstraints( minWidth: MediaQuery.of(context).size.width - 32, ), - child: DataTable( - columnSpacing: 20, - columns: [ - const DataColumn( - label: Text( - 'Rôles', - style: TextStyle(fontWeight: FontWeight.bold), - ), - ), - ...permissions.map((perm) => DataColumn( - label: Text( - perm.name, - style: const TextStyle(fontWeight: FontWeight.bold), - ), - )).toList(), - ], - rows: roles.map((role) { - final roleId = role['id'] as int; - return DataRow( - cells: [ - DataCell(Text(role['designation'] ?? '')), - ...permissions.map((perm) { - final isChecked = rolePermissionsMap[roleId]?[perm.name] ?? false; - return DataCell( - Checkbox( - value: isChecked, - onChanged: (bool? value) { - _onPermissionToggle(roleId, perm.name, value ?? false); - }, + child: Column( + children: menus.map((menu) { + final menuId = menu['id'] as int; + return Column( + children: [ + Text( + menu['name'], + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16), + ), + DataTable( + columnSpacing: 20, + columns: [ + const DataColumn( + label: Text( + 'Rôles', + style: TextStyle(fontWeight: FontWeight.bold), + ), ), - ); - }).toList(), + ...permissions.map((perm) => DataColumn( + label: Text( + perm.name, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + )).toList(), + ], + rows: roles.map((role) { + final roleId = role.id!; + return DataRow( + cells: [ + DataCell(Text(role.designation)), + ...permissions.map((perm) { + final isChecked = roleMenuPermissionsMap[roleId]?[menuId]?[perm.name] ?? false; + return DataCell( + Checkbox( + value: isChecked, + onChanged: (bool? value) { + _onPermissionToggle(roleId, menuId, perm.name, value ?? false); + }, + ), + ); + }).toList(), + ], + ); + }).toList(), + ), ], ); }).toList(), @@ -176,7 +198,7 @@ class _HandleUserRoleState extends State { else const Expanded( child: Center( - child: Text('Aucun rôle ou permission trouvé'), + child: Text('Aucun rôle, permission ou menu trouvé'), ), ), ], diff --git a/lib/Views/loginPage.dart b/lib/Views/loginPage.dart index 83096b5..dff0539 100644 --- a/lib/Views/loginPage.dart +++ b/lib/Views/loginPage.dart @@ -129,7 +129,7 @@ class _LoginPageState extends State { print('ID: ${userCredentials['id']}'); // Sauvegarder dans le contrôleur - userController.setUser(user); + // userController.setUser(user); // Sauvegarder dans SharedPreferences await saveUser( diff --git a/lib/controller/userController.dart b/lib/controller/userController.dart index 5b5d358..d326843 100644 --- a/lib/controller/userController.dart +++ b/lib/controller/userController.dart @@ -1,4 +1,5 @@ import 'package:get/get.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:youmazgestion/Models/users.dart'; import 'package:youmazgestion/Services/app_database.dart'; @@ -17,9 +18,33 @@ class UserController extends GetxController { String get lastname => _lastname.value; String get password => _password.value; + @override + void onInit() { + super.onInit(); + loadUserData(); // Charger les données au démarrage + } + + // Charger les données depuis SharedPreferences + Future loadUserData() async { + try { + final prefs = await SharedPreferences.getInstance(); + + _username.value = prefs.getString('username') ?? ''; + _email.value = prefs.getString('email') ?? ''; + _role.value = prefs.getString('role') ?? ''; + _name.value = prefs.getString('name') ?? ''; + _lastname.value = prefs.getString('lastname') ?? ''; + + print("Données chargées - Username: ${_username.value}"); + print("Role: ${_role.value}"); + } catch (e) { + print('Erreur lors du chargement des données utilisateur: $e'); + } + } + void setUser(Users user) { _username.value = user.username; - print(_username.value); + print("username " + _username.value); _email.value = user.email; print(_email.value); _role.value = user.role; @@ -30,19 +55,73 @@ class UserController extends GetxController { print(_lastname.value); _password.value = user.password; print(_password.value); + + // Sauvegarder dans SharedPreferences + saveUserData(); + } + + // Sauvegarder les données dans SharedPreferences + Future saveUserData() async { + try { + final prefs = await SharedPreferences.getInstance(); + + await prefs.setString('username', _username.value); + await prefs.setString('email', _email.value); + await prefs.setString('role', _role.value); + await prefs.setString('name', _name.value); + await prefs.setString('lastname', _lastname.value); + + print("Données sauvegardées avec succès"); + } catch (e) { + print('Erreur lors de la sauvegarde des données utilisateur: $e'); + } } - Future hasPermission(String permissionName) async { - // Utilisez votre instance de AppDatabase pour vérifier la permission - return await AppDatabase.instance.hasPermission(username, permissionName); + // Méthode pour vider les données utilisateur + Future clearUserData() async { + try { + final prefs = await SharedPreferences.getInstance(); + + await prefs.remove('username'); + await prefs.remove('email'); + await prefs.remove('role'); + await prefs.remove('name'); + await prefs.remove('lastname'); + + // Vider les variables observables + _username.value = ''; + _email.value = ''; + _role.value = ''; + _name.value = ''; + _lastname.value = ''; + _password.value = ''; + + print("Données utilisateur effacées"); + } catch (e) { + print('Erreur lors de l\'effacement des données utilisateur: $e'); + } } - Future hasAnyPermission(List permissionNames) async { - for (String permissionName in permissionNames) { - if (await hasPermission(permissionName)) { - return true; + + Future hasPermission(String permission, String route) async { + try { + if (_username.value.isEmpty) { + print('Username vide, rechargement des données...'); + await loadUserData(); + } + + return await AppDatabase.instance.hasPermission(username, permission, route); + } catch (e) { + print('Erreur vérification permission: $e'); + return false; // Sécurité : refuser l'accès en cas d'erreur } } - return false; -} -} + Future hasAnyPermission(List permissionNames, String menuRoute) async { + for (String permissionName in permissionNames) { + if (await hasPermission(permissionName, menuRoute)) { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 718968e..4760cbb 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:youmazgestion/Services/app_database.dart'; +import 'package:youmazgestion/controller/userController.dart'; import 'Services/productDatabase.dart'; import 'my_app.dart'; import 'package:logging/logging.dart'; @@ -10,12 +11,14 @@ void main() async { try { // Initialiser les bases de données une seule fois + // await AppDatabase.instance.deleteDatabaseFile(); + await ProductDatabase.instance.initDatabase(); await AppDatabase.instance.initDatabase(); // Afficher les informations de la base (pour debug) await AppDatabase.instance.printDatabaseInfo(); - + Get.put(UserController()); // Ajoute ce code AVANT tout accès au UserController setupLogger(); runApp(const GetMaterialApp(