9 changed files with 919 additions and 188 deletions
@ -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<String, dynamic> 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<String, dynamic> 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; |
||||
|
} |
||||
|
} |
||||
@ -26,6 +26,7 @@ class AppDatabase { |
|||||
_database = await _initDB('app_database.db'); |
_database = await _initDB('app_database.db'); |
||||
await _createDB(_database, 1); |
await _createDB(_database, 1); |
||||
await insertDefaultPermissions(); |
await insertDefaultPermissions(); |
||||
|
await insertDefaultMenus(); |
||||
await insertDefaultRoles(); |
await insertDefaultRoles(); |
||||
await insertDefaultSuperAdmin(); |
await insertDefaultSuperAdmin(); |
||||
} |
} |
||||
@ -36,13 +37,11 @@ class AppDatabase { |
|||||
|
|
||||
bool dbExists = await File(path).exists(); |
bool dbExists = await File(path).exists(); |
||||
if (!dbExists) { |
if (!dbExists) { |
||||
// Optionnel : copier depuis assets si vous avez une DB pré-remplie |
|
||||
try { |
try { |
||||
ByteData data = await rootBundle.load('assets/database/$filePath'); |
ByteData data = await rootBundle.load('assets/database/$filePath'); |
||||
List<int> bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); |
List<int> bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); |
||||
await File(path).writeAsBytes(bytes); |
await File(path).writeAsBytes(bytes); |
||||
} catch (e) { |
} 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'); |
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 tables = await db.rawQuery("SELECT name FROM sqlite_master WHERE type='table'"); |
||||
final tableNames = tables.map((row) => row['name'] as String).toList(); |
final tableNames = tables.map((row) => row['name'] as String).toList(); |
||||
|
|
||||
// Table des rôles |
|
||||
if (!tableNames.contains('roles')) { |
if (!tableNames.contains('roles')) { |
||||
await db.execute(''' |
await db.execute(''' |
||||
CREATE TABLE roles ( |
CREATE TABLE roles ( |
||||
@ -65,7 +63,6 @@ class AppDatabase { |
|||||
print("Table 'roles' créée."); |
print("Table 'roles' créée."); |
||||
} |
} |
||||
|
|
||||
// Table des permissions |
|
||||
if (!tableNames.contains('permissions')) { |
if (!tableNames.contains('permissions')) { |
||||
await db.execute(''' |
await db.execute(''' |
||||
CREATE TABLE permissions ( |
CREATE TABLE permissions ( |
||||
@ -76,7 +73,17 @@ class AppDatabase { |
|||||
print("Table 'permissions' créée."); |
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')) { |
if (!tableNames.contains('role_permissions')) { |
||||
await db.execute(''' |
await db.execute(''' |
||||
CREATE TABLE role_permissions ( |
CREATE TABLE role_permissions ( |
||||
@ -90,7 +97,19 @@ class AppDatabase { |
|||||
print("Table 'role_permissions' créée."); |
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')) { |
if (!tableNames.contains('users')) { |
||||
await db.execute(''' |
await db.execute(''' |
||||
CREATE TABLE users ( |
CREATE TABLE users ( |
||||
@ -106,9 +125,22 @@ class AppDatabase { |
|||||
'''); |
'''); |
||||
print("Table 'users' créée."); |
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<void> insertDefaultPermissions() async { |
Future<void> insertDefaultPermissions() async { |
||||
final db = await database; |
final db = await database; |
||||
@ -123,67 +155,118 @@ class AppDatabase { |
|||||
} |
} |
||||
} |
} |
||||
|
|
||||
|
Future<void> insertDefaultMenus() async { |
||||
|
final db = await database; |
||||
|
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<void> insertDefaultRoles() async { |
Future<void> insertDefaultRoles() async { |
||||
final db = await database; |
final db = await database; |
||||
final existingRoles = await db.query('roles'); |
final existingRoles = await db.query('roles'); |
||||
|
|
||||
if (existingRoles.isEmpty) { |
if (existingRoles.isEmpty) { |
||||
// Créer le rôle Super Admin |
|
||||
int superAdminRoleId = await db.insert('roles', {'designation': '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 adminRoleId = await db.insert('roles', {'designation': 'Admin'}); |
||||
int userRoleId = await db.insert('roles', {'designation': 'User'}); |
int userRoleId = await db.insert('roles', {'designation': 'User'}); |
||||
|
|
||||
// Assigner toutes les permissions au Super Admin |
|
||||
final permissions = await db.query('permissions'); |
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) { |
for (var permission in permissions) { |
||||
await db.insert('role_permissions', { |
await db.insert('role_menu_permissions', { |
||||
'role_id': superAdminRoleId, |
'role_id': superAdminRoleId, |
||||
|
'menu_id': menu['id'], |
||||
'permission_id': permission['id'], |
'permission_id': permission['id'], |
||||
}); |
}); |
||||
} |
} |
||||
|
} |
||||
|
|
||||
// Assigner quelques permissions à l'Admin |
// Assigner quelques permissions à l'Admin et à l'User |
||||
final viewPermission = await db.query('permissions', where: 'name = ?', whereArgs: ['view']); |
final viewPermission = await db.query('permissions', where: 'name = ?', whereArgs: ['view']); |
||||
final createPermission = await db.query('permissions', where: 'name = ?', whereArgs: ['create']); |
final createPermission = await db.query('permissions', where: 'name = ?', whereArgs: ['create']); |
||||
final updatePermission = await db.query('permissions', where: 'name = ?', whereArgs: ['update']); |
final updatePermission = await db.query('permissions', where: 'name = ?', whereArgs: ['update']); |
||||
|
|
||||
if (viewPermission.isNotEmpty) { |
if (viewPermission.isNotEmpty) { |
||||
await db.insert('role_permissions', { |
await db.insert('role_menu_permissions', { |
||||
'role_id': adminRoleId, |
'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'], |
'permission_id': viewPermission.first['id'], |
||||
}); |
}); |
||||
} |
} |
||||
|
|
||||
if (createPermission.isNotEmpty) { |
if (createPermission.isNotEmpty) { |
||||
await db.insert('role_permissions', { |
await db.insert('role_menu_permissions', { |
||||
'role_id': adminRoleId, |
'role_id': adminRoleId, |
||||
|
'menu_id': 1, // Assurez-vous que l'ID du menu est correct |
||||
'permission_id': createPermission.first['id'], |
'permission_id': createPermission.first['id'], |
||||
}); |
}); |
||||
} |
} |
||||
|
|
||||
if (updatePermission.isNotEmpty) { |
if (updatePermission.isNotEmpty) { |
||||
await db.insert('role_permissions', { |
await db.insert('role_menu_permissions', { |
||||
'role_id': adminRoleId, |
'role_id': adminRoleId, |
||||
|
'menu_id': 1, // Assurez-vous que l'ID du menu est correct |
||||
'permission_id': updatePermission.first['id'], |
'permission_id': updatePermission.first['id'], |
||||
}); |
}); |
||||
} |
} |
||||
|
|
||||
// Assigner seulement la permission view à User |
print("Rôles par défaut créés et permissions assignées"); |
||||
if (viewPermission.isNotEmpty) { |
} else { |
||||
await db.insert('role_permissions', { |
// Si les rôles existent déjà, vérifier et ajouter les permissions manquantes pour le Super Admin |
||||
'role_id': userRoleId, |
final superAdminRole = await db.query('roles', where: 'designation = ?', whereArgs: ['Super Admin']); |
||||
'permission_id': viewPermission.first['id'], |
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<void> insertDefaultSuperAdmin() async { |
Future<void> insertDefaultSuperAdmin() async { |
||||
final db = await database; |
final db = await database; |
||||
|
|
||||
// Vérifier si un super admin existe déjà |
|
||||
final existingSuperAdmin = await db.rawQuery(''' |
final existingSuperAdmin = await db.rawQuery(''' |
||||
SELECT u.* FROM users u |
SELECT u.* FROM users u |
||||
INNER JOIN roles r ON u.role_id = r.id |
INNER JOIN roles r ON u.role_id = r.id |
||||
@ -191,7 +274,6 @@ class AppDatabase { |
|||||
'''); |
'''); |
||||
|
|
||||
if (existingSuperAdmin.isEmpty) { |
if (existingSuperAdmin.isEmpty) { |
||||
// Récupérer l'ID du rôle Super Admin |
|
||||
final superAdminRole = await db.query('roles', |
final superAdminRole = await db.query('roles', |
||||
where: 'designation = ?', |
where: 'designation = ?', |
||||
whereArgs: ['Super Admin'] |
whereArgs: ['Super Admin'] |
||||
@ -200,12 +282,11 @@ class AppDatabase { |
|||||
if (superAdminRole.isNotEmpty) { |
if (superAdminRole.isNotEmpty) { |
||||
final superAdminRoleId = superAdminRole.first['id'] as int; |
final superAdminRoleId = superAdminRole.first['id'] as int; |
||||
|
|
||||
// Créer l'utilisateur Super Admin |
|
||||
await db.insert('users', { |
await db.insert('users', { |
||||
'name': 'Super', |
'name': 'Super', |
||||
'lastname': 'Admin', |
'lastname': 'Admin', |
||||
'email': '[email protected]', |
'email': '[email protected]', |
||||
'password': 'admin123', // CHANGEZ CE MOT DE PASSE ! |
'password': 'admin123', |
||||
'username': 'superadmin', |
'username': 'superadmin', |
||||
'role_id': superAdminRoleId, |
'role_id': superAdminRoleId, |
||||
}); |
}); |
||||
@ -220,8 +301,6 @@ class AppDatabase { |
|||||
} |
} |
||||
} |
} |
||||
|
|
||||
// ========== GESTION DES UTILISATEURS ========== |
|
||||
|
|
||||
Future<int> createUser(Users user) async { |
Future<int> createUser(Users user) async { |
||||
final db = await database; |
final db = await database; |
||||
return await db.insert('users', user.toMap()); |
return await db.insert('users', user.toMap()); |
||||
@ -301,8 +380,6 @@ class AppDatabase { |
|||||
return result.map((json) => Users.fromMap(json)).toList(); |
return result.map((json) => Users.fromMap(json)).toList(); |
||||
} |
} |
||||
|
|
||||
// ========== GESTION DES RÔLES ========== |
|
||||
|
|
||||
Future<int> createRole(Role role) async { |
Future<int> createRole(Role role) async { |
||||
final db = await database; |
final db = await database; |
||||
return await db.insert('roles', role.toMap()); |
return await db.insert('roles', role.toMap()); |
||||
@ -333,8 +410,6 @@ class AppDatabase { |
|||||
); |
); |
||||
} |
} |
||||
|
|
||||
// ========== GESTION DES PERMISSIONS ========== |
|
||||
|
|
||||
Future<List<Permission>> getAllPermissions() async { |
Future<List<Permission>> getAllPermissions() async { |
||||
final db = await database; |
final db = await database; |
||||
final result = await db.query('permissions', orderBy: 'name ASC'); |
final result = await db.query('permissions', orderBy: 'name ASC'); |
||||
@ -386,7 +461,22 @@ class AppDatabase { |
|||||
); |
); |
||||
} |
} |
||||
|
|
||||
// ========== MÉTHODES UTILITAIRES POUR LA SÉCURITÉ ========== |
Future<void> 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<void> 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<bool> isSuperAdmin(String username) async { |
Future<bool> isSuperAdmin(String username) async { |
||||
final db = await database; |
final db = await database; |
||||
@ -403,13 +493,11 @@ class AppDatabase { |
|||||
Future<void> changePassword(String username, String oldPassword, String newPassword) async { |
Future<void> changePassword(String username, String oldPassword, String newPassword) async { |
||||
final db = await database; |
final db = await database; |
||||
|
|
||||
// Vérifier l'ancien mot de passe |
|
||||
final isValidOldPassword = await verifyUser(username, oldPassword); |
final isValidOldPassword = await verifyUser(username, oldPassword); |
||||
if (!isValidOldPassword) { |
if (!isValidOldPassword) { |
||||
throw Exception('Ancien mot de passe incorrect'); |
throw Exception('Ancien mot de passe incorrect'); |
||||
} |
} |
||||
|
|
||||
// Changer le mot de passe |
|
||||
await db.update( |
await db.update( |
||||
'users', |
'users', |
||||
{'password': newPassword}, |
{'password': newPassword}, |
||||
@ -418,54 +506,48 @@ class AppDatabase { |
|||||
); |
); |
||||
} |
} |
||||
|
|
||||
// ========== UTILITAIRES ========== |
Future<bool> hasPermission(String username, String permissionName, String menuRoute) async { |
||||
|
|
||||
Future<void> close() async { |
|
||||
if (_database.isOpen) { |
|
||||
await _database.close(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Future<bool> hasPermission(String username, String permissionName) async { |
|
||||
final db = await database; |
final db = await database; |
||||
final result = await db.rawQuery(''' |
final result = await db.rawQuery(''' |
||||
SELECT COUNT(*) as count |
SELECT COUNT(*) as count |
||||
FROM permissions p |
FROM permissions p |
||||
JOIN role_permissions rp ON p.id = rp.permission_id |
JOIN role_menu_permissions rmp ON p.id = rmp.permission_id |
||||
JOIN roles r ON rp.role_id = r.id |
JOIN roles r ON rmp.role_id = r.id |
||||
JOIN users u ON u.role_id = r.id |
JOIN users u ON u.role_id = r.id |
||||
WHERE u.username = ? AND p.name = ? |
JOIN menu m ON m.route = ? |
||||
''', [username, permissionName]); |
WHERE u.username = ? AND p.name = ? AND rmp.menu_id = m.id |
||||
|
''', [menuRoute, username, permissionName]); |
||||
|
|
||||
return (result.first['count'] as int) > 0; |
return (result.first['count'] as int) > 0; |
||||
} |
} |
||||
|
|
||||
// ========== MÉTHODE DE DEBUG ========== |
|
||||
|
Future<void> close() async { |
||||
|
if (_database.isOpen) { |
||||
|
await _database.close(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
Future<void> printDatabaseInfo() async { |
Future<void> printDatabaseInfo() async { |
||||
final db = await database; |
final db = await database; |
||||
|
|
||||
print("=== INFORMATIONS DE LA BASE DE DONNÉES ==="); |
print("=== INFORMATIONS DE LA BASE DE DONNÉES ==="); |
||||
|
|
||||
// Compter les utilisateurs |
|
||||
final userCount = await getUserCount(); |
final userCount = await getUserCount(); |
||||
print("Nombre d'utilisateurs: $userCount"); |
print("Nombre d'utilisateurs: $userCount"); |
||||
|
|
||||
// Lister tous les utilisateurs |
|
||||
final users = await getAllUsers(); |
final users = await getAllUsers(); |
||||
print("Utilisateurs:"); |
print("Utilisateurs:"); |
||||
for (var user in users) { |
for (var user in users) { |
||||
print(" - ${user.username} (${user.name} ) - Email: ${user.email}"); |
print(" - ${user.username} (${user.name} ) - Email: ${user.email}"); |
||||
} |
} |
||||
|
|
||||
// Lister tous les rôles |
|
||||
final roles = await getRoles(); |
final roles = await getRoles(); |
||||
print("Rôles:"); |
print("Rôles:"); |
||||
for (var role in roles) { |
for (var role in roles) { |
||||
print(" - ${role.designation} (ID: ${role.id})"); |
print(" - ${role.designation} (ID: ${role.id})"); |
||||
} |
} |
||||
|
|
||||
// Lister toutes les permissions |
|
||||
final permissions = await getAllPermissions(); |
final permissions = await getAllPermissions(); |
||||
print("Permissions:"); |
print("Permissions:"); |
||||
for (var permission in permissions) { |
for (var permission in permissions) { |
||||
@ -474,4 +556,47 @@ class AppDatabase { |
|||||
|
|
||||
print("========================================="); |
print("========================================="); |
||||
} |
} |
||||
|
|
||||
|
Future<List<Permission>> 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<void> 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<void> 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<void> 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], |
||||
|
); |
||||
|
} |
||||
|
|
||||
} |
} |
||||
@ -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<RoleListPage> createState() => _RoleListPageState(); |
||||
|
} |
||||
|
|
||||
|
class _RoleListPageState extends State<RoleListPage> { |
||||
|
final db = AppDatabase.instance; |
||||
|
final TextEditingController _roleController = TextEditingController(); |
||||
|
List<Role> roles = []; |
||||
|
|
||||
|
@override |
||||
|
void initState() { |
||||
|
super.initState(); |
||||
|
_loadRoles(); |
||||
|
} |
||||
|
|
||||
|
Future<void> _loadRoles() async { |
||||
|
final roleList = await db.getRoles(); |
||||
|
setState(() { |
||||
|
roles = roleList; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
Future<void> _addRole() async { |
||||
|
String designation = _roleController.text.trim(); |
||||
|
if (designation.isEmpty) return; |
||||
|
|
||||
|
await db.createRole(Role(designation: designation)); |
||||
|
_roleController.clear(); |
||||
|
await _loadRoles(); |
||||
|
} |
||||
|
|
||||
|
Future<void> _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)), |
||||
|
), |
||||
|
], |
||||
|
); |
||||
|
}, |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -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<RolePermissionsPage> createState() => _RolePermissionsPageState(); |
||||
|
} |
||||
|
|
||||
|
class _RolePermissionsPageState extends State<RolePermissionsPage> { |
||||
|
final db = AppDatabase.instance; |
||||
|
List<Permission> permissions = []; |
||||
|
List<Map<String, dynamic>> menus = []; |
||||
|
Map<int, Map<String, bool>> menuPermissionsMap = {}; |
||||
|
|
||||
|
@override |
||||
|
void initState() { |
||||
|
super.initState(); |
||||
|
_initData(); |
||||
|
} |
||||
|
|
||||
|
Future<void> _initData() async { |
||||
|
final perms = await db.getAllPermissions(); |
||||
|
final menuList = await db.database.then((db) => db.query('menu')); |
||||
|
|
||||
|
Map<int, Map<String, bool>> 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<void> _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<bool> 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), |
||||
|
), |
||||
|
); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue