Browse Source

dernier mis a jour

31052025_01
b.razafimandimbihery 6 months ago
parent
commit
e739df3811
  1. 0
      assets/database/roles.db
  2. 207
      lib/Components/appDrawer.dart
  3. 16
      lib/Models/Permission.dart
  4. 20
      lib/Models/Role.dart
  5. 26
      lib/Models/users.dart
  6. 477
      lib/Services/app_database.dart
  7. 147
      lib/Services/authDatabase.dart
  8. 288
      lib/Views/addProduct.dart
  9. 352
      lib/Views/editUser.dart
  10. 2
      lib/Views/gestionProduct.dart
  11. 147
      lib/Views/gestionRole.dart
  12. 8
      lib/Views/listUser.dart
  13. 210
      lib/Views/loginPage.dart
  14. 449
      lib/Views/produitsCard.dart
  15. 494
      lib/Views/registrationPage.dart
  16. 285
      lib/accueil.dart
  17. 15
      lib/controller/userController.dart
  18. 34
      lib/main.dart
  19. 16
      pubspec.lock
  20. 1
      pubspec.yaml

0
assets/database/roles.db

207
lib/Components/appDrawer.dart

@ -2,24 +2,26 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:youmazgestion/Views/historique.dart';
import '../Views/addProduct.dart';
import '../Views/bilanMois.dart';
import '../Views/gestionProduct.dart';
import '../Views/gestionStock.dart';
import '../Views/listUser.dart';
import '../Views/loginPage.dart';
import '../Views/registrationPage.dart';
import '../accueil.dart';
import '../controller/userController.dart';
import 'package:youmazgestion/Views/addProduct.dart';
import 'package:youmazgestion/Views/bilanMois.dart';
import 'package:youmazgestion/Views/gestionProduct.dart';
import 'package:youmazgestion/Views/gestionStock.dart';
import 'package:youmazgestion/Views/listUser.dart';
import 'package:youmazgestion/Views/loginPage.dart';
import 'package:youmazgestion/Views/registrationPage.dart';
import 'package:youmazgestion/Views/gestionRole.dart';
import 'package:youmazgestion/accueil.dart';
import 'package:youmazgestion/controller/userController.dart';
class CustomDrawer extends StatelessWidget {
final UserController userController = Get.find<UserController>();
Future<void> clearUserData() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('username');
await prefs.remove('role');
}
Future<void> clearUserData() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('username');
await prefs.remove('role');
}
CustomDrawer({super.key});
@override
@ -30,25 +32,25 @@ Future<void> clearUserData() async {
children: [
GetBuilder<UserController>(
builder: (controller) => UserAccountsDrawerHeader(
accountEmail: Text(controller.email),
accountName: Text(controller.name),
currentAccountPicture: const CircleAvatar(
backgroundImage: AssetImage("assets/youmaz2.png"),
accountEmail: Text(controller.email),
accountName: Text(controller.name),
currentAccountPicture: const CircleAvatar(
backgroundImage: AssetImage("assets/youmaz2.png"),
),
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Colors.white, Color.fromARGB(255, 4, 54, 95)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Colors.white, const Color.fromARGB(255, 4, 54, 95)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
)),
),
),
),
ListTile(
leading: const Icon(Icons.home),
iconColor: Colors.lightBlueAccent,
title: const Text("Accueil"),
onTap: () {
// Action lorsque l'utilisateur clique sur "Accueil"
Get.to(const AccueilPage());
},
),
@ -56,18 +58,20 @@ Future<void> clearUserData() async {
leading: const Icon(Icons.person_add),
iconColor: Colors.green,
title: const Text("Ajouter un utilisateur"),
onTap: () {
if (userController.role == "admin") {
onTap: () async {
bool hasPermission = await userController.hasAnyPermission(['create']);
if (hasPermission) {
Get.to(const RegistrationPage());
} else {
Get.snackbar(
"Accés refusé",
backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(Icons.error),
duration: const Duration(seconds: 3),
snackPosition: SnackPosition.TOP,
"Vous n'avez pas les droits pour ajouter un utilisateur");
"Accès refusé",
"Vous n'avez pas les droits pour ajouter un utilisateur",
backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(Icons.error),
duration: const Duration(seconds: 3),
snackPosition: SnackPosition.TOP,
);
}
},
),
@ -75,19 +79,20 @@ Future<void> clearUserData() async {
leading: const Icon(Icons.supervised_user_circle),
iconColor: const Color.fromARGB(255, 4, 54, 95),
title: const Text("Modifier/Supprimer un utilisateur"),
onTap: () {
// Action lorsque l'utilisateur clique sur "Modifier/Supprimer un utilisateur"
if (userController.role == "admin") {
onTap: () async {
bool hasPermission = await userController.hasAnyPermission(['update', 'delete']);
if (hasPermission) {
Get.to(const ListUserPage());
} else {
Get.snackbar(
"Accés refusé",
backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(Icons.error),
duration: const Duration(seconds: 3),
snackPosition: SnackPosition.TOP,
"Vous n'avez pas les droits pour modifier/supprimer un utilisateur");
"Accès refusé",
"Vous n'avez pas les droits pour modifier/supprimer un utilisateur",
backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(Icons.error),
duration: const Duration(seconds: 3),
snackPosition: SnackPosition.TOP,
);
}
},
),
@ -95,19 +100,20 @@ Future<void> clearUserData() async {
leading: const Icon(Icons.add),
iconColor: Colors.indigoAccent,
title: const Text("Ajouter un produit"),
onTap: () {
if (userController.role == "admin") {
// Action lorsque l'utilisateur clique sur "Ajouter un produit"
onTap: () async {
bool hasPermission = await userController.hasAnyPermission(['create']);
if (hasPermission) {
Get.to(const AddProductPage());
} else {
Get.snackbar(
"Accés refusé",
backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(Icons.error),
duration: const Duration(seconds: 3),
snackPosition: SnackPosition.TOP,
"Vous n'avez pas les droits pour ajouter un produit");
"Accès refusé",
"Vous n'avez pas les droits pour ajouter un produit",
backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(Icons.error),
duration: const Duration(seconds: 3),
snackPosition: SnackPosition.TOP,
);
}
},
),
@ -115,37 +121,60 @@ Future<void> clearUserData() async {
leading: const Icon(Icons.edit),
iconColor: Colors.redAccent,
title: const Text("Modifier/Supprimer un produit"),
onTap: () {
if (userController.role == "admin") {
// Action lorsque l'utilisateur clique sur "Modifier/Supprimer un produit"
onTap: () async {
bool hasPermission = await userController.hasAnyPermission(['update', 'delete']);
if (hasPermission) {
Get.to(GestionProduit());
} else {
Get.snackbar(
"Accés refusé",
backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(Icons.error),
duration: const Duration(seconds: 3),
snackPosition: SnackPosition.TOP,
"Vous n'avez pas les droits pour modifier/supprimer un produit");
"Accès refusé",
"Vous n'avez pas les droits pour modifier/supprimer un produit",
backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(Icons.error),
duration: const Duration(seconds: 3),
snackPosition: SnackPosition.TOP,
);
}
},
),
ListTile(
leading: const Icon(Icons.bar_chart),
title: const Text("Bilan"),
onTap: () {
if (userController.role == "admin") {
onTap: () async {
bool hasPermission = await userController.hasAnyPermission(['read']);
if (hasPermission) {
Get.to(const BilanMois());
} else {
Get.snackbar(
"Accés refusé",
backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(Icons.error_outline_outlined),
duration: const Duration(seconds: 3),
snackPosition: SnackPosition.TOP,
"Vous n'avez pas les droits pour accéder au bilan");
"Accès refusé",
"Vous n'avez pas les droits pour accéder au bilan",
backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(Icons.error_outline_outlined),
duration: const Duration(seconds: 3),
snackPosition: SnackPosition.TOP,
);
}
},
),
ListTile(
leading: const Icon(Icons.warning_amber),
title: const Text("Gérer les rôles"),
onTap: () async {
bool hasPermission = await userController.hasAnyPermission(['update', 'delete']);
if (hasPermission) {
Get.to(const HandleUserRole());
} else {
Get.snackbar(
"Accès refusé",
"Vous n'avez pas les droits pour gérer les rôles",
backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(Icons.error_outline_outlined),
duration: const Duration(seconds: 3),
snackPosition: SnackPosition.TOP,
);
}
},
),
@ -153,19 +182,20 @@ Future<void> clearUserData() async {
leading: const Icon(Icons.inventory),
iconColor: Colors.blueAccent,
title: const Text("Gestion de stock"),
onTap: () {
if (userController.role == "admin") {
// Action lorsque l'utilisateur clique sur "Gestion de stock"
onTap: () async {
bool hasPermission = await userController.hasAnyPermission(['update']);
if (hasPermission) {
Get.to(const GestionStockPage());
} else {
Get.snackbar(
"Accés refusé",
backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(Icons.error),
duration: const Duration(seconds: 3),
snackPosition: SnackPosition.TOP,
"Vous n'avez pas les droits pour accéder à la gestion de stock");
"Accès refusé",
"Vous n'avez pas les droits pour accéder à la gestion de stock",
backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(Icons.error),
duration: const Duration(seconds: 3),
snackPosition: SnackPosition.TOP,
);
}
},
),
@ -174,7 +204,6 @@ Future<void> clearUserData() async {
iconColor: Colors.blue,
title: const Text("Historique"),
onTap: () {
// Action lorsque l'utilisateur clique sur "Historique"
Get.to(HistoryPage());
},
),
@ -183,8 +212,6 @@ Future<void> clearUserData() async {
iconColor: Colors.red,
title: const Text("Déconnexion"),
onTap: () {
// Action lorsque l'utilisateur clique sur "Déconnexion"
// display confirmation dialog
Get.defaultDialog(
title: "Déconnexion",
content: const Text("Voulez-vous vraiment vous déconnecter ?"),
@ -201,11 +228,11 @@ Future<void> clearUserData() async {
onPressed: () {
Get.back();
},
)
),
],
);
},
)
),
],
),
);

16
lib/Models/Permission.dart

@ -0,0 +1,16 @@
class Permission {
final int? id;
final String name;
Permission({this.id, required this.name});
factory Permission.fromMap(Map<String, dynamic> map) => Permission(
id: map['id'],
name: map['name'],
);
Map<String, dynamic> toMap() => {
'id': id,
'name': name,
};
}

20
lib/Models/Role.dart

@ -0,0 +1,20 @@
class Role {
final int? id;
final String designation;
Role({this.id, required this.designation});
Map<String, dynamic> toMap() {
return {
'id': id,
'designation': designation,
};
}
factory Role.fromMap(Map<String, dynamic> map) {
return Role(
id: map['id'],
designation: map['designation'],
);
}
}

26
lib/Models/users.dart

@ -1,34 +1,41 @@
class Users {
int id;
int? id;
String name;
String lastName;
String email;
String password;
String username;
String role;
int roleId;
String? roleName; // Optionnel, rempli lors des requêtes avec JOIN
Users({
required this.id,
this.id,
required this.name,
required this.lastName,
required this.email,
required this.password,
required this.username,
required this.role,
required this.roleId,
this.roleName,
});
Map<String, dynamic> toMap() {
return {
'id': id,
'name': name,
'lastName': lastName,
'lastname': lastName,
'email': email,
'password': password,
'username': username,
'role': role,
'role_id': roleId,
};
}
Map<String, dynamic> toMapWithId() {
final map = toMap();
if (id != null) map['id'] = id;
return map;
}
factory Users.fromMap(Map<String, dynamic> map) {
return Users(
id: map['id'],
@ -37,8 +44,11 @@ class Users {
email: map['email'],
password: map['password'],
username: map['username'],
role: map['role']
roleId: map['role_id'],
roleName: map['role_name'], // Depuis les requêtes avec JOIN
);
}
// Getter pour la compatibilité avec l'ancien code
String get role => roleName ?? '';
}

477
lib/Services/app_database.dart

@ -0,0 +1,477 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import '../Models/users.dart';
import '../Models/role.dart';
import '../Models/Permission.dart';
class AppDatabase {
static final AppDatabase instance = AppDatabase._init();
late Database _database;
AppDatabase._init() {
sqfliteFfiInit();
}
Future<Database> get database async {
if (_database.isOpen) return _database;
_database = await _initDB('app_database.db');
return _database;
}
Future<void> initDatabase() async {
_database = await _initDB('app_database.db');
await _createDB(_database, 1);
await insertDefaultPermissions();
await insertDefaultRoles();
await insertDefaultSuperAdmin();
}
Future<Database> _initDB(String filePath) async {
final documentsDirectory = await getApplicationDocumentsDirectory();
final path = join(documentsDirectory.path, filePath);
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<int> 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');
}
}
return await databaseFactoryFfi.openDatabase(path);
}
Future<void> _createDB(Database db, int version) async {
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 (
id INTEGER PRIMARY KEY AUTOINCREMENT,
designation TEXT NOT NULL UNIQUE
)
''');
print("Table 'roles' créée.");
}
// Table des permissions
if (!tableNames.contains('permissions')) {
await db.execute('''
CREATE TABLE permissions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE
)
''');
print("Table 'permissions' créée.");
}
// Table de liaison role_permissions
if (!tableNames.contains('role_permissions')) {
await db.execute('''
CREATE TABLE role_permissions (
role_id INTEGER,
permission_id INTEGER,
PRIMARY KEY (role_id, permission_id),
FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE,
FOREIGN KEY (permission_id) REFERENCES permissions(id) ON DELETE CASCADE
)
''');
print("Table 'role_permissions' créée.");
}
// Table des utilisateurs
if (!tableNames.contains('users')) {
await db.execute('''
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
lastname TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
password TEXT NOT NULL,
username TEXT NOT NULL UNIQUE,
role_id INTEGER NOT NULL,
FOREIGN KEY (role_id) REFERENCES roles(id)
)
''');
print("Table 'users' créée.");
}
}
// ========== INSERTION DES DONNÉES PAR DÉFAUT ==========
Future<void> insertDefaultPermissions() async {
final db = await database;
final existing = await db.query('permissions');
if (existing.isEmpty) {
await db.insert('permissions', {'name': 'view'});
await db.insert('permissions', {'name': 'create'});
await db.insert('permissions', {'name': 'update'});
await db.insert('permissions', {'name': 'delete'});
await db.insert('permissions', {'name': 'admin'});
print("Permissions par défaut insérées");
}
}
Future<void> insertDefaultRoles() 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');
for (var permission in permissions) {
await db.insert('role_permissions', {
'role_id': superAdminRoleId,
'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'],
});
}
print("Rôles par défaut créés et permissions assignées");
}
}
Future<void> 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 = ?',
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 !
'username': 'superadmin',
'role_id': superAdminRoleId,
});
print("Super Admin créé avec succès !");
print("Username: superadmin");
print("Password: admin123");
print("ATTENTION: Changez ce mot de passe après la première connexion !");
}
} else {
print("Super Admin existe déjà");
}
}
// ========== GESTION DES UTILISATEURS ==========
Future<int> createUser(Users user) async {
final db = await database;
return await db.insert('users', user.toMap());
}
Future<int> deleteUser(int id) async {
final db = await database;
return await db.delete('users', where: 'id = ?', whereArgs: [id]);
}
Future<int> updateUser(Users user) async {
final db = await database;
return await db.update('users', user.toMap(), where: 'id = ?', whereArgs: [user.id]);
}
Future<int> getUserCount() async {
final db = await database;
List<Map<String, dynamic>> result = await db.rawQuery('SELECT COUNT(*) as count FROM users');
return result.first['count'] as int;
}
Future<bool> verifyUser(String username, String password) async {
final db = await database;
final result = await db.rawQuery('''
SELECT users.id
FROM users
WHERE users.username = ? AND users.password = ?
''', [username, password]);
return result.isNotEmpty;
}
Future<Users> 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
WHERE users.username = ?
''', [username]);
if (result.isNotEmpty) {
return Users.fromMap(result.first);
} else {
throw Exception('User not found');
}
}
Future<Map<String, dynamic>?> getUserCredentials(String username, String password) async {
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
WHERE username = ? AND password = ?
''', [username, password]);
if (result.isNotEmpty) {
return {
'id': result.first['id'],
'username': result.first['username'] as String,
'role': result.first['role_name'] as String,
'role_id': result.first['role_id'],
};
} else {
return null;
}
}
Future<List<Users>> 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
ORDER BY users.id ASC
''');
return result.map((json) => Users.fromMap(json)).toList();
}
// ========== GESTION DES RÔLES ==========
Future<int> createRole(Role role) async {
final db = await database;
return await db.insert('roles', role.toMap());
}
Future<List<Role>> getRoles() async {
final db = await database;
final maps = await db.query('roles', orderBy: 'designation ASC');
return List.generate(maps.length, (i) => Role.fromMap(maps[i]));
}
Future<int> updateRole(Role role) async {
final db = await database;
return await db.update(
'roles',
role.toMap(),
where: 'id = ?',
whereArgs: [role.id],
);
}
Future<int> deleteRole(int? id) async {
final db = await database;
return await db.delete(
'roles',
where: 'id = ?',
whereArgs: [id],
);
}
// ========== GESTION DES PERMISSIONS ==========
Future<List<Permission>> getAllPermissions() async {
final db = await database;
final result = await db.query('permissions', orderBy: 'name ASC');
return result.map((e) => Permission.fromMap(e)).toList();
}
Future<List<Permission>> getPermissionsForRole(int roleId) async {
final db = await database;
final result = await db.rawQuery('''
SELECT p.id, p.name
FROM permissions p
JOIN role_permissions rp ON p.id = rp.permission_id
WHERE rp.role_id = ?
ORDER BY p.name ASC
''', [roleId]);
return result.map((map) => Permission.fromMap(map)).toList();
}
Future<List<Permission>> getPermissionsForUser(String username) async {
final db = await database;
final result = await db.rawQuery('''
SELECT DISTINCT p.id, p.name
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 = ?
ORDER BY p.name ASC
''', [username]);
return result.map((map) => Permission.fromMap(map)).toList();
}
Future<void> assignPermission(int roleId, int permissionId) async {
final db = await database;
await db.insert('role_permissions', {
'role_id': roleId,
'permission_id': permissionId,
}, conflictAlgorithm: ConflictAlgorithm.ignore);
}
Future<void> removePermission(int roleId, int permissionId) async {
final db = await database;
await db.delete(
'role_permissions',
where: 'role_id = ? AND permission_id = ?',
whereArgs: [roleId, permissionId],
);
}
// ========== MÉTHODES UTILITAIRES POUR LA SÉCURITÉ ==========
Future<bool> isSuperAdmin(String username) async {
final db = await database;
final result = await db.rawQuery('''
SELECT COUNT(*) as count
FROM users u
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<void> 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},
where: 'username = ?',
whereArgs: [username],
);
}
// ========== UTILITAIRES ==========
Future<void> close() async {
if (_database.isOpen) {
await _database.close();
}
}
Future<bool> 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<void> 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("=========================================");
}
}

147
lib/Services/authDatabase.dart

@ -1,147 +0,0 @@
import 'package:flutter/services.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart' as sqflite_ffi;
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:youmazgestion/Models/users.dart';
import 'dart:io';
class AuthDatabase {
static final AuthDatabase instance = AuthDatabase._init();
late Database _database;
AuthDatabase._init() {
sqflite_ffi.sqfliteFfiInit();
}
Future<void> initDatabase() async {
_database = await _initDB('usersDb.db');
await _createDB(_database, 1);
}
Future<Database> get database async {
if (_database.isOpen) return _database;
_database = await _initDB('usersDb.db');
return _database;
}
Future<Database> _initDB(String filePath) async {
// Obtenez le répertoire de stockage local de l'application
final documentsDirectory = await getApplicationDocumentsDirectory();
final path = join(documentsDirectory.path, filePath);
// Vérifiez si le fichier de base de données existe déjà dans le répertoire de stockage local
bool dbExists = await File(path).exists();
if (!dbExists) {
// Si le fichier n'existe pas, copiez-le depuis le dossier assets/database
ByteData data = await rootBundle.load('assets/database/$filePath');
List<int> bytes =
data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
await File(path).writeAsBytes(bytes);
}
// Ouvrez la base de données
return await databaseFactoryFfi.openDatabase(path);
}
Future<void> _createDB(Database db, int version) async {
final resultUsers = await db.rawQuery(
"SELECT name FROM sqlite_master WHERE type='table' AND name='users'");
if (resultUsers.isEmpty) {
await db.execute('''
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
lastname TEXT,
email TEXT,
password TEXT,
username TEXT,
role TEXT
)
''');
}
}
Future<int> createUser(Users user) async {
final db = await database;
return await db.insert('users', user.toMap());
}
Future<int> deleteUser(int id) async {
final db = await database;
return await db.delete('users', where: 'id = ?', whereArgs: [id]);
}
Future<int> updateUser(Users user) async {
final db = await database;
return await db
.update('users', user.toMap(), where: 'id = ?', whereArgs: [user.id]);
}
Future<int> getUserCount() async {
final db = await database;
List<Map<String, dynamic>> x =
await db.rawQuery('SELECT COUNT (*) from users');
int result = Sqflite.firstIntValue(x)!;
return result;
}
// verify username and password existe
Future<bool> verifyUser(String username, String password) async {
final db = await database;
List<Map<String, dynamic>> x = await db.rawQuery(
'SELECT COUNT (*) from users WHERE username = ? AND password = ?',
[username, password]);
int result = Sqflite.firstIntValue(x)!;
if (result == 1) {
return true;
} else {
return false;
}
}
//recuperer un user grace a son username
Future<Users> getUser(String username) async {
try {
final db = await database;
List<Map<String, dynamic>> x = await db
.rawQuery('SELECT * from users WHERE username = ?', [username]);
print(x.first);
Users user = Users.fromMap(x.first);
print(user);
return user;
} catch (e) {
print(e);
rethrow;
}
}
Future<Map<String, String>?> getUserCredentials(String username, String password) async {
final db = await database;
List<Map<String, dynamic>> result = await db.rawQuery(
'SELECT username, role FROM users WHERE username = ? AND password = ?',
[username, password],
);
if (result.isNotEmpty) {
print('username '+result[0]['username']);
return {
'username': result[0]['username'],
'role': result[0]['role'],
};
} else {
return null; // Aucun utilisateur trouvé
}
}
Future<List<Users>> getAllUsers() async {
final db = await database;
const orderBy = 'id ASC';
final result = await db.query('users', orderBy: orderBy);
return result.map((json) => Users.fromMap(json)).toList();
}
}

288
lib/Views/addProduct.dart

@ -19,17 +19,16 @@ class AddProductPage extends StatefulWidget {
}
class _AddProductPageState extends State<AddProductPage> {
// Controllers for text fields
final TextEditingController _nameController = TextEditingController();
final TextEditingController _priceController = TextEditingController();
final TextEditingController _imageController = TextEditingController();
final TextEditingController _descriptionController = TextEditingController();
String? _qrData; // Variable to store QR code data
final List<String> _categories = ['Sucré', 'Salé', 'Jus', 'Gateaux']; // List of product categories
String? _selectedCategory; // Selected category
File? _pickedImage; // Variable to store the selected image file
late ProductDatabase _productDatabase; // Database instance
final List<String> _categories = ['Sucré', 'Salé', 'Jus', 'Gateaux'];
String? _selectedCategory;
File? _pickedImage;
String? _qrData;
late ProductDatabase _productDatabase;
@override
void initState() {
@ -43,52 +42,12 @@ class _AddProductPageState extends State<AddProductPage> {
void dispose() {
_nameController.removeListener(_updateQrData);
_nameController.dispose();
_priceController.dispose();
_imageController.dispose();
_descriptionController.dispose();
super.dispose();
}
// Function to select an image from files or drop
void _selectImage() async {
final action = await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Sélectionner une image'),
content: const Text('Choisissez comment sélectionner une image'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop('pick');
},
child: const Text('Choisir depuis les fichiers'),
),
TextButton(
onPressed: () {
Navigator.of(context).pop('drop');
},
child: const Text('Déposer une image'),
),
],
);
},
);
if (action == 'pick') {
final result = await FilePicker.platform.pickFiles(
type: FileType.image,
);
if (result != null) {
setState(() {
_pickedImage = File(result.files.single.path!);
_imageController.text = _pickedImage!.path;
});
}
} else if (action == 'drop') {
// Code to handle image drop
}
}
// Function to update QR data based on product name
void _updateQrData() {
if (_nameController.text.isNotEmpty) {
final reference = 'PROD_PREVIEW_${_nameController.text}_${DateTime.now().millisecondsSinceEpoch}';
@ -98,80 +57,82 @@ class _AddProductPageState extends State<AddProductPage> {
}
}
// Function to get the database location
Future<void> _getDatabaseLocation() async {
final directory = await getApplicationDocumentsDirectory();
final dbPath = directory.path;
print('Emplacement de la base de données : $dbPath');
Future<void> _selectImage() async {
final result = await FilePicker.platform.pickFiles(type: FileType.image);
if (result != null && result.files.single.path != null) {
setState(() {
_pickedImage = File(result.files.single.path!);
_imageController.text = _pickedImage!.path;
});
}
}
// Function to generate and save QR code
Future<String> _generateAndSaveQRCode(String reference) async {
final qrValidationResult = QrValidator.validate(
final validation = QrValidator.validate(
data: 'https://tonsite.com/$reference',
version: QrVersions.auto,
errorCorrectionLevel: QrErrorCorrectLevel.L,
);
final qrCode = qrValidationResult.qrCode;
final qrCode = validation.qrCode!;
final painter = QrPainter.withQr(
qr: qrCode!,
qr: qrCode,
color: Colors.black,
emptyColor: Colors.white,
gapless: true,
);
final tempDir = await getApplicationDocumentsDirectory();
final file = File('${tempDir.path}/$reference.png');
final directory = await getApplicationDocumentsDirectory();
final path = '${directory.path}/$reference.png';
final picData = await painter.toImageData(2048, format: ImageByteFormat.png);
await file.writeAsBytes(picData!.buffer.asUint8List());
await File(path).writeAsBytes(picData!.buffer.asUint8List());
return file.path;
return path;
}
// Function to add a product to the database
void _addProduct() async {
final name = _nameController.text;
final price = double.tryParse(_priceController.text) ?? 0.0;
final image = _imageController.text;
final name = _nameController.text.trim();
final price = double.tryParse(_priceController.text.trim()) ?? 0.0;
final image = _imageController.text.trim();
final category = _selectedCategory;
final description = _descriptionController.text;
final description = _descriptionController.text.trim();
if (name.isNotEmpty && price > 0 && image.isNotEmpty && category != null) {
final reference = 'PROD_${DateTime.now().millisecondsSinceEpoch}';
final qrPath = await _generateAndSaveQRCode(reference);
if (name.isEmpty || price <= 0 || image.isEmpty || category == null) {
Get.snackbar('Erreur', 'Veuillez remplir tous les champs requis');
return;
}
final product = Product(
name: name,
price: price,
image: image,
category: category,
description: description,
qrCode: qrPath,
reference: reference,
);
final reference = 'PROD_${DateTime.now().millisecondsSinceEpoch}';
final qrPath = await _generateAndSaveQRCode(reference);
final product = Product(
name: name,
price: price,
image: image,
category: category,
description: description,
qrCode: qrPath,
reference: reference,
);
try {
await _productDatabase.createProduct(product);
Get.snackbar('Succès', 'Produit ajouté avec succès');
_productDatabase.createProduct(product).then((_) {
Get.snackbar('Succès', 'Produit ajouté avec succès');
setState(() {
_nameController.clear();
_priceController.clear();
_imageController.clear();
_descriptionController.clear();
_selectedCategory = null;
_pickedImage = null;
});
}).catchError((error) {
Get.snackbar('Erreur', 'Impossible d\'ajouter le produit : $error');
setState(() {
_nameController.clear();
_priceController.clear();
_imageController.clear();
_descriptionController.clear();
_selectedCategory = null;
_pickedImage = null;
_qrData = null;
});
} else {
Get.snackbar(
'Saisie invalide',
'Veuillez entrer tous les champs requis.',
);
} catch (e) {
Get.snackbar('Erreur', 'Ajout du produit échoué : $e');
}
}
// Function to display the selected image
Widget _displayImage() {
if (_pickedImage != null) {
return ClipRRect(
@ -184,33 +145,14 @@ class _AddProductPageState extends State<AddProductPage> {
),
);
} else {
return Stack(
alignment: Alignment.center,
children: [
Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(8.0),
),
),
Icon(
Icons.image,
size: 48,
color: Colors.grey[400],
),
Positioned(
bottom: 4,
child: Text(
'Aucune image',
style: TextStyle(
fontSize: 12,
color: Colors.grey[400],
),
),
),
],
return Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(8.0),
),
child: const Icon(Icons.image, size: 48, color: Colors.grey),
);
}
}
@ -219,35 +161,23 @@ class _AddProductPageState extends State<AddProductPage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: const CustomAppBar(title: 'Ajouter un produit'),
drawer: CustomDrawer(),
body: Padding(
padding: const EdgeInsets.all(16.0),
drawer: CustomDrawer(),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Ajouter un produit',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const Text('Ajouter un produit', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
const SizedBox(height: 16),
TextField(
controller: _nameController,
decoration: const InputDecoration(
labelText: 'Nom du produit',
border: OutlineInputBorder(),
),
decoration: const InputDecoration(labelText: 'Nom du produit', border: OutlineInputBorder()),
),
const SizedBox(height: 16),
TextField(
controller: _priceController,
decoration: const InputDecoration(
labelText: 'Prix',
border: OutlineInputBorder(),
),
keyboardType: const TextInputType.numberWithOptions(decimal: true),
keyboardType: TextInputType.numberWithOptions(decimal: true),
decoration: const InputDecoration(labelText: 'Prix', border: OutlineInputBorder()),
),
const SizedBox(height: 16),
Row(
@ -256,17 +186,12 @@ class _AddProductPageState extends State<AddProductPage> {
Expanded(
child: TextField(
controller: _imageController,
decoration: const InputDecoration(
labelText: 'Image',
border: OutlineInputBorder(),
),
decoration: const InputDecoration(labelText: 'Chemin de l\'image', border: OutlineInputBorder()),
readOnly: true,
),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: _selectImage,
child: const Text('Sélectionner une image'),
),
ElevatedButton(onPressed: _selectImage, child: const Text('Sélectionner')),
],
),
const SizedBox(height: 16),
@ -274,48 +199,37 @@ class _AddProductPageState extends State<AddProductPage> {
const SizedBox(height: 16),
DropdownButtonFormField<String>(
value: _selectedCategory,
onChanged: (newValue) {
setState(() {
_selectedCategory = newValue;
});
},
decoration: const InputDecoration(
labelText: 'Catégorie',
border: OutlineInputBorder(),
),
items: _categories.map((category) {
return DropdownMenuItem<String>(
value: category,
child: Text(category),
);
}).toList(),
items: _categories
.map((c) => DropdownMenuItem(value: c, child: Text(c)))
.toList(),
onChanged: (value) => setState(() => _selectedCategory = value),
decoration: const InputDecoration(labelText: 'Catégorie', border: OutlineInputBorder()),
),
const SizedBox(height: 16),
if (_qrData != null)
Column(
children: [
const SizedBox(height: 16),
const Text('Aperçu du QR Code :'),
QrImageView(
data: _qrData!,
version: QrVersions.auto,
size: 120.0,
),
],
),
const SizedBox(height: 16),
TextField(
controller: _descriptionController,
decoration: const InputDecoration(
labelText: 'Description',
border: OutlineInputBorder(),
),
maxLines: 3,
decoration: const InputDecoration(labelText: 'Description', border: OutlineInputBorder()),
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _addProduct,
child: const Text('Ajouter le produit'),
if (_qrData != null) ...[
const Text('Aperçu du QR Code :'),
const SizedBox(height: 8),
Center(
child: QrImageView(
data: _qrData!,
version: QrVersions.auto,
size: 120,
),
),
],
const SizedBox(height: 24),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _addProduct,
child: const Text('Ajouter le produit'),
),
),
],
),

352
lib/Views/editUser.dart

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:youmazgestion/Models/users.dart';
import '../Services/authDatabase.dart';
import 'package:youmazgestion/Models/role.dart';
import '../Services/app_database.dart';
class EditUserPage extends StatefulWidget {
final Users user;
@ -17,7 +18,11 @@ class _EditUserPageState extends State<EditUserPage> {
late TextEditingController _emailController;
late TextEditingController _usernameController;
late TextEditingController _passwordController;
String _selectedRole = '';
List<Role> _roles = [];
Role? _selectedRole;
bool _isLoading = false;
bool _isLoadingRoles = true;
@override
void initState() {
@ -26,9 +31,31 @@ class _EditUserPageState extends State<EditUserPage> {
_lastNameController = TextEditingController(text: widget.user.lastName);
_emailController = TextEditingController(text: widget.user.email);
_usernameController = TextEditingController(text: widget.user.username);
_passwordController =
TextEditingController(); // Leave password field empty initially
_selectedRole = widget.user.role;
_passwordController = TextEditingController();
_loadRoles();
}
Future<void> _loadRoles() async {
try {
final roles = await AppDatabase.instance.getRoles();
final currentRole = roles.firstWhere(
(r) => r.id == widget.user.roleId,
orElse: () => Role(id: widget.user.roleId, designation: widget.user.roleName ?? 'Inconnu'),
);
setState(() {
_roles = roles;
_selectedRole = currentRole;
_isLoadingRoles = false;
});
} catch (e) {
print('Erreur lors du chargement des rôles: $e');
setState(() {
_isLoadingRoles = false;
});
_showErrorDialog('Erreur', 'Impossible de charger les rôles.');
}
}
@override
@ -41,144 +68,213 @@ class _EditUserPageState extends State<EditUserPage> {
super.dispose();
}
void _updateUser() {
final String name = _nameController.text;
final String lastName = _lastNameController.text;
final String email = _emailController.text;
final String username = _usernameController.text;
final String password =
_passwordController.text; // Get the entered password
final String role = _selectedRole;
final Users updatedUser = Users(
id: widget.user.id,
name: name,
lastName: lastName,
email: email,
password: password.isNotEmpty
? password
: widget.user
.password, // Use entered password if not empty, otherwise keep the existing password
username: username,
role: role,
);
bool _validateFields() {
if (_nameController.text.trim().isEmpty ||
_lastNameController.text.trim().isEmpty ||
_emailController.text.trim().isEmpty ||
_usernameController.text.trim().isEmpty ||
_selectedRole == null) {
_showErrorDialog('Champs manquants', 'Veuillez remplir tous les champs requis.');
return false;
}
AuthDatabase.instance.updateUser(updatedUser).then((value) {
// User update successful
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Update Successful'),
content: const Text('User information has been updated.'),
actions: <Widget>[
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
Navigator.pop(context,
true); // Return true to indicate successful update
},
child: const Text('OK'),
),
],
);
},
);
}).catchError((error) {
print(error);
// Update failed
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Update Failed'),
content:
const Text('An error occurred during user information update.'),
actions: <Widget>[
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('OK'),
),
],
);
},
);
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$')
.hasMatch(_emailController.text.trim())) {
_showErrorDialog('Email invalide', 'Veuillez saisir un email valide.');
return false;
}
if (_passwordController.text.isNotEmpty &&
_passwordController.text.length < 6) {
_showErrorDialog('Mot de passe trop court', 'Minimum 6 caractères.');
return false;
}
return true;
}
Future<void> _updateUser() async {
if (!_validateFields() || _isLoading) return;
setState(() {
_isLoading = true;
});
try {
final updatedUser = Users(
id: widget.user.id,
name: _nameController.text.trim(),
lastName: _lastNameController.text.trim(),
email: _emailController.text.trim(),
username: _usernameController.text.trim(),
password: _passwordController.text.isNotEmpty
? _passwordController.text
: widget.user.password,
roleId: _selectedRole!.id!,
roleName: _selectedRole!.designation,
);
await AppDatabase.instance.updateUser(updatedUser);
if (mounted) _showSuccessDialog();
} catch (e) {
print('Erreur de mise à jour: $e');
if (mounted) {
_showErrorDialog('Échec', 'Une erreur est survenue lors de la mise à jour.');
}
} finally {
if (mounted) {
setState(() {
_isLoading = false;
});
}
}
}
void _showSuccessDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Mise à jour réussie'),
content: const Text('Les informations de l\'utilisateur ont été mises à jour.'),
actions: [
ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('OK'),
)
],
),
);
}
void _showErrorDialog(String title, String message) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(title),
content: Text(message),
actions: [
ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('OK'),
)
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Edit User'),
title: const Text('Modifier Utilisateur', style: TextStyle(color: Colors.white)),
backgroundColor: const Color.fromARGB(255, 4, 54, 95),
iconTheme: const IconThemeData(color: Colors.white),
centerTitle: true,
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextField(
controller: _nameController,
decoration: const InputDecoration(
labelText: 'First Name',
),
),
const SizedBox(height: 16.0),
TextField(
controller: _lastNameController,
decoration: const InputDecoration(
labelText: 'Last Name',
),
),
const SizedBox(height: 16.0),
TextField(
controller: _emailController,
decoration: const InputDecoration(
labelText: 'Email',
),
keyboardType: TextInputType.emailAddress,
),
const SizedBox(height: 16.0),
TextField(
controller: _usernameController,
decoration: const InputDecoration(
labelText: 'Username',
body: _isLoadingRoles
? const Center(child: CircularProgressIndicator())
: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
const Icon(Icons.edit, size: 64, color: Colors.blue),
const SizedBox(height: 16),
_buildTextField(_nameController, 'Prénom', Icons.person),
const SizedBox(height: 12),
_buildTextField(_lastNameController, 'Nom', Icons.person_outline),
const SizedBox(height: 12),
_buildTextField(_emailController, 'Email', Icons.email, keyboardType: TextInputType.emailAddress),
const SizedBox(height: 12),
_buildTextField(_usernameController, 'Nom d\'utilisateur', Icons.account_circle),
const SizedBox(height: 12),
_buildTextField(
_passwordController,
'Mot de passe (laisser vide si inchangé)',
Icons.lock,
obscureText: true,
),
const SizedBox(height: 12),
_buildDropdown(),
const SizedBox(height: 20),
SizedBox(
width: double.infinity,
height: 48,
child: ElevatedButton(
onPressed: _isLoading ? null : _updateUser,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF0015B7),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: _isLoading
? const CircularProgressIndicator(color: Colors.white)
: const Text('Mettre à jour', style: TextStyle(color: Colors.white, fontSize: 16)),
),
)
],
),
),
),
const SizedBox(height: 16.0),
TextField(
controller: _passwordController,
decoration: const InputDecoration(
labelText: 'Password',
),
obscureText: true,
),
const SizedBox(height: 16.0),
DropdownButton<String>(
value: _selectedRole,
onChanged: (String? newValue) {
),
);
}
Widget _buildTextField(
TextEditingController controller,
String label,
IconData icon, {
TextInputType keyboardType = TextInputType.text,
bool obscureText = false,
}) {
return TextField(
controller: controller,
decoration: InputDecoration(
labelText: label,
prefixIcon: Icon(icon),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
),
keyboardType: keyboardType,
obscureText: obscureText,
);
}
Widget _buildDropdown() {
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<Role>(
value: _selectedRole,
isExpanded: true,
hint: const Text('Sélectionner un rôle'),
onChanged: _isLoading
? null
: (Role? newValue) {
setState(() {
_selectedRole = newValue!;
_selectedRole = newValue;
});
},
items: <String>['admin', 'user']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
const SizedBox(height: 16.0),
ElevatedButton(
onPressed: _updateUser,
child: const Text('Update'),
items: _roles.map((role) {
return DropdownMenuItem<Role>(
value: role,
child: Row(
children: [
const Icon(Icons.badge, size: 20),
const SizedBox(width: 8),
Text(role.designation),
],
),
],
),
);
}).toList(),
),
),
);

2
lib/Views/gestionProduct.dart

@ -11,7 +11,7 @@ import 'dart:io';
class GestionProduit extends StatelessWidget {
final ProductDatabase _productDatabase = ProductDatabase.instance;
const GestionProduit({super.key});
GestionProduit({super.key});
@override
Widget build(BuildContext context) {

147
lib/Views/gestionRole.dart

@ -0,0 +1,147 @@
import 'package:flutter/material.dart';
import 'package:youmazgestion/Components/app_bar.dart';
import 'package:youmazgestion/Services/app_database.dart';
import 'package:youmazgestion/Models/role.dart';
class HandleUserRole extends StatefulWidget {
const HandleUserRole({super.key});
@override
State<HandleUserRole> createState() => _HandleUserRoleState();
}
class _HandleUserRoleState extends State<HandleUserRole> {
final db = AppDatabase.instance;
List<Map<String, dynamic>> roles = [];
Map<String, bool> permissionsMap = {};
int? selectedRoleId;
final TextEditingController _roleController = TextEditingController();
@override
void initState() {
super.initState();
_initData();
}
Future<void> _initData() async {
final roleList = await db.database.then((db) => db.query('roles'));
final perms = await db.getAllPermissions();
setState(() {
roles = roleList;
for (var perm in perms) {
permissionsMap[perm.name] = false;
}
});
}
Future<void> _addRole() async {
String designation = _roleController.text.trim();
if (designation.isEmpty) return;
await db.createRole(Role(designation: designation));
_roleController.clear();
await _initData();
}
Future<void> _loadRolePermissions(int roleId) async {
final rolePerms = await db.getPermissionsForRole(roleId);
final allPerms = await db.getAllPermissions();
setState(() {
selectedRoleId = roleId;
permissionsMap = {
for (var perm in allPerms)
perm.name: rolePerms.any((rp) => rp.name == perm.name)
};
});
}
Future<void> _onPermissionToggle(String permission, bool enabled) async {
if (selectedRoleId == null) return;
final allPerms = await db.getAllPermissions();
final perm = allPerms.firstWhere((p) => p.name == permission);
if (enabled) {
await db.assignPermission(selectedRoleId!, perm.id!);
} else {
await db.removePermission(selectedRoleId!, perm.id!);
}
setState(() {
permissionsMap[permission] = enabled;
});
}
@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: [
// Champ pour saisir un nouveau rôle
Row(
children: [
Expanded(
child: TextField(
controller: _roleController,
decoration: const InputDecoration(
labelText: 'Nouveau rôle',
border: OutlineInputBorder(),
),
),
),
const SizedBox(width: 10),
ElevatedButton(
onPressed: _addRole,
child: const Text('Créer'),
),
],
),
const SizedBox(height: 20),
// 🔽 Sélection d'un rôle
DropdownButton<int>(
isExpanded: true,
hint: const Text("Choisir un rôle"),
value: selectedRoleId,
items: roles.map((role) {
return DropdownMenuItem<int>(
value: role['id'] as int,
child: Text(role['designation'] ?? ''),
);
}).toList(),
onChanged: (value) {
if (value != null) _loadRolePermissions(value);
},
),
const SizedBox(height: 16),
// Permissions associées
if (selectedRoleId != null)
Expanded(
child: ListView(
children: permissionsMap.entries.map((entry) {
return CheckboxListTile(
title: Text(entry.key),
value: entry.value,
onChanged: (bool? value) {
_onPermissionToggle(entry.key, value ?? false);
},
);
}).toList(),
),
),
],
),
),
);
}
}

8
lib/Views/listUser.dart

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:youmazgestion/Models/users.dart';
import '../Components/app_bar.dart';
import '../Services/authDatabase.dart';
import '../Services/app_database.dart';
import 'editUser.dart';
class ListUserPage extends StatefulWidget {
@ -23,7 +23,7 @@ class _ListUserPageState extends State<ListUserPage> {
Future<void> getUsersFromDatabase() async {
try {
List<Users> users = await AuthDatabase.instance.getAllUsers();
List<Users> users = await AppDatabase.instance.getAllUsers();
setState(() {
userList = users;
});
@ -89,8 +89,8 @@ class _ListUserPageState extends State<ListUserPage> {
),
TextButton(
onPressed: () async {
await AuthDatabase.instance
.deleteUser(user.id);
await AppDatabase.instance
.deleteUser(user.id!);
Navigator.of(context).pop();
setState(() {
userList.removeAt(index);

210
lib/Views/loginPage.dart

@ -3,7 +3,7 @@ import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:youmazgestion/Views/particles.dart' show ParticleBackground;
import 'package:youmazgestion/accueil.dart';
import 'package:youmazgestion/Services/authDatabase.dart';
import 'package:youmazgestion/Services/app_database.dart';
import '../Models/users.dart';
import '../controller/userController.dart';
@ -20,6 +20,8 @@ class _LoginPageState extends State<LoginPage> {
late TextEditingController _passwordController;
final UserController userController = Get.put(UserController());
bool _isErrorVisible = false;
bool _isLoading = false;
String _errorMessage = 'Nom d\'utilisateur ou mot de passe invalide';
@override
void initState() {
@ -30,13 +32,27 @@ class _LoginPageState extends State<LoginPage> {
}
void checkUserCount() async {
final userCount = await AuthDatabase.instance.getUserCount();
if (userCount == 0) {
// No user found, redirect to home page
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const AccueilPage()),
);
try {
final userCount = await AppDatabase.instance.getUserCount();
print('Nombre d\'utilisateurs trouvés: $userCount'); // Debug
// Commentez cette partie pour permettre le login même sans utilisateurs
/*
if (userCount == 0) {
if (mounted) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const AccueilPage()),
);
}
}
*/
} catch (error) {
print('Erreur lors de la vérification du nombre d\'utilisateurs: $error');
setState(() {
_errorMessage = 'Erreur de connexion à la base de données';
_isErrorVisible = true;
});
}
}
@ -46,47 +62,111 @@ class _LoginPageState extends State<LoginPage> {
_passwordController.dispose();
super.dispose();
}
Future<void> saveUser(String? username,String? role)async{
Future<void> saveUser(String username, String role, int userId) async {
try {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('username', username!);
await prefs.setString('role', role!);
}
await prefs.setString('username', username);
await prefs.setString('role', role);
await prefs.setInt('user_id', userId);
print('Utilisateur sauvegardé: $username, rôle: $role, id: $userId');
} catch (error) {
print('Erreur lors de la sauvegarde: $error');
throw Exception('Erreur lors de la sauvegarde des données utilisateur');
}
}
void _login() async {
final String username = _usernameController.text;
final String password = _passwordController.text;
print(username);
print(password);
if (_isLoading) return;
final String username = _usernameController.text.trim();
final String password = _passwordController.text.trim();
// Validation basique
if (username.isEmpty || password.isEmpty) {
setState(() {
_errorMessage = 'Veuillez saisir le nom d\'utilisateur et le mot de passe';
_isErrorVisible = true;
});
return;
}
setState(() {
_isLoading = true;
_isErrorVisible = false;
});
try {
bool isValidUser =
await AuthDatabase.instance.verifyUser(username, password);
Users user = await AuthDatabase.instance.getUser(username);
print('Tentative de connexion pour: $username');
// Vérification de la connexion à la base de données
final dbInstance = AppDatabase.instance;
// Test de connexion à la base
try {
final userCount = await dbInstance.getUserCount();
print('Base de données accessible, $userCount utilisateurs trouvés');
} catch (dbError) {
throw Exception('Impossible d\'accéder à la base de données: $dbError');
}
// Vérifier les identifiants
bool isValidUser = await dbInstance.verifyUser(username, password);
print('Résultat de la vérification: $isValidUser');
Map<String, String>? getUserCredentials = await AuthDatabase.instance.getUserCredentials(username,password);
print(isValidUser);
if (isValidUser) {
print('User is valid');
print(user);
userController.setUser(user);
// Récupérer les informations complètes de l'utilisateur
Users user = await dbInstance.getUser(username);
print('Utilisateur récupéré: ${user.username}');
setState(() {
_isErrorVisible = false;
});
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const AccueilPage()),
);
saveUser(getUserCredentials?['username'], getUserCredentials?['role']);
// Récupérer les credentials
Map<String, dynamic>? userCredentials =
await dbInstance.getUserCredentials(username, password);
if (userCredentials != null) {
print('Connexion réussie pour: ${user.username}');
print('Rôle: ${userCredentials['role']}');
print('ID: ${userCredentials['id']}');
// Sauvegarder dans le contrôleur
userController.setUser(user);
// Sauvegarder dans SharedPreferences
await saveUser(
userCredentials['username'] as String,
userCredentials['role'] as String,
userCredentials['id'] as int,
);
// Navigation
if (mounted) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const AccueilPage()),
);
}
} else {
throw Exception('Erreur lors de la récupération des credentials');
}
} else {
print('Identifiants invalides pour: $username');
setState(() {
_errorMessage = 'Nom d\'utilisateur ou mot de passe invalide';
_isErrorVisible = true;
});
}
} catch (error) {
print(error);
print('Erreur lors de la connexion: $error');
setState(() {
_errorMessage = 'Erreur de connexion: ${error.toString()}';
_isErrorVisible = true;
});
} finally {
if (mounted) {
setState(() {
_isLoading = false;
});
}
}
}
@ -96,8 +176,8 @@ Future<void> saveUser(String? username,String? role)async{
appBar: AppBar(
title: const Text(
'Login',
style: TextStyle(color:Colors.white),
),
style: TextStyle(color: Colors.white),
),
backgroundColor: const Color.fromARGB(255, 4, 54, 95),
centerTitle: true,
),
@ -125,10 +205,10 @@ Future<void> saveUser(String? username,String? role)async{
),
TextField(
controller: _usernameController,
enabled: !_isLoading,
decoration: InputDecoration(
labelText: 'Username',
prefixIcon:
const Icon(Icons.person, color: Colors.blueAccent),
prefixIcon: const Icon(Icons.person, color: Colors.blueAccent),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
),
@ -137,6 +217,7 @@ Future<void> saveUser(String? username,String? role)async{
const SizedBox(height: 16.0),
TextField(
controller: _passwordController,
enabled: !_isLoading,
decoration: InputDecoration(
labelText: 'Password',
prefixIcon: const Icon(Icons.lock, color: Colors.redAccent),
@ -145,34 +226,67 @@ Future<void> saveUser(String? username,String? role)async{
),
),
obscureText: true,
onSubmitted: (_) => _login(),
),
const SizedBox(height: 16.0),
Visibility(
visible: _isErrorVisible,
child: const Text(
'Invalid username or password',
style: TextStyle(
child: Text(
_errorMessage,
style: const TextStyle(
color: Colors.red,
fontSize: 14,
),
textAlign: TextAlign.center,
),
),
const SizedBox(height: 16.0),
ElevatedButton(
onPressed: _login,
onPressed: _isLoading ? null : _login,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF0015B7), // Nouvelle couleur
backgroundColor: const Color(0xFF0015B7),
elevation: 5.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30.0),
),
minimumSize: const Size(double.infinity, 48),
),
child: const Text(
'Login',
style: TextStyle(
color: Colors.white,
),
),
child: _isLoading
? const SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
),
)
: const Text(
'Se connecter',
style: TextStyle(
color: Colors.white,
fontSize: 16,
),
),
),
// Bouton de debug (à supprimer en production)
if (_isErrorVisible)
TextButton(
onPressed: () async {
try {
final count = await AppDatabase.instance.getUserCount();
print('Debug: $count utilisateurs dans la base');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('$count utilisateurs trouvés')),
);
} catch (e) {
print('Debug error: $e');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Erreur: $e')),
);
}
},
child: const Text('Debug: Vérifier BDD'),
),
],
),
),

449
lib/Views/produitsCard.dart

@ -0,0 +1,449 @@
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:quantity_input/quantity_input.dart';
import 'package:youmazgestion/Models/produit.dart';
class ProductCard extends StatefulWidget {
final Product product;
final void Function(Product, int) onAddToCart;
const ProductCard({
Key? key,
required this.product,
required this.onAddToCart,
}) : super(key: key);
@override
State<ProductCard> createState() => _ProductCardState();
}
class _ProductCardState extends State<ProductCard> with TickerProviderStateMixin {
int selectedQuantity = 1;
late AnimationController _scaleController;
late AnimationController _fadeController;
late Animation<double> _scaleAnimation;
late Animation<double> _fadeAnimation;
@override
void initState() {
super.initState();
// Animations pour les interactions
_scaleController = AnimationController(
duration: const Duration(milliseconds: 200),
vsync: this,
);
_fadeController = AnimationController(
duration: const Duration(milliseconds: 300),
vsync: this,
)..forward();
_scaleAnimation = Tween<double>(
begin: 1.0,
end: 0.95,
).animate(CurvedAnimation(
parent: _scaleController,
curve: Curves.easeInOut,
));
_fadeAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _fadeController,
curve: Curves.easeOut,
));
}
@override
void dispose() {
_scaleController.dispose();
_fadeController.dispose();
super.dispose();
}
void _onTapDown(TapDownDetails details) {
_scaleController.forward();
}
void _onTapUp(TapUpDetails details) {
_scaleController.reverse();
}
void _onTapCancel() {
_scaleController.reverse();
}
@override
Widget build(BuildContext context) {
return FadeTransition(
opacity: _fadeAnimation,
child: AnimatedBuilder(
animation: _scaleAnimation,
builder: (context, child) {
return Transform.scale(
scale: _scaleAnimation.value,
child: Container(
margin: const EdgeInsets.all(8),
height: 280,
child: Material(
elevation: 8,
shadowColor: Colors.black.withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Colors.white,
Colors.grey.shade50,
],
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Stack(
children: [
Positioned.fill(
child: Container(
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(20),
),
child: widget.product.image != null
? Image.file(
File(widget.product.image),
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return _buildPlaceholderImage();
},
)
: _buildPlaceholderImage(),
),
),
Positioned.fill(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.transparent,
Colors.transparent,
Colors.black.withOpacity(0.3),
Colors.black.withOpacity(0.7),
],
stops: const [0.0, 0.4, 0.7, 1.0],
),
),
),
),
if (widget.product.isStockDefined())
Positioned(
top: 12,
right: 12,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
decoration: BoxDecoration(
color: Colors.green.withOpacity(0.9),
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.green.withOpacity(0.3),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.check_circle,
color: Colors.white,
size: 12,
),
const SizedBox(width: 4),
const Text(
'En stock',
style: TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
Positioned(
left: 0,
right: 0,
bottom: 0,
child: Container(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.product.name,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: Colors.white,
shadows: [
Shadow(
offset: Offset(1, 1),
blurRadius: 3,
color: Colors.black54,
),
],
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 4),
Text(
'${widget.product.price.toStringAsFixed(2)} FCFA',
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 13,
color: Colors.white,
shadows: [
Shadow(
offset: Offset(1, 1),
blurRadius: 3,
color: Colors.black54,
),
],
),
),
],
),
),
const SizedBox(height: 12),
Row(
children: [
Container(
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.95),
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
_buildQuantityButton(
icon: Icons.remove,
onPressed: selectedQuantity > 1
? () {
setState(() {
selectedQuantity--;
});
}
: null,
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
child: Text(
'$selectedQuantity',
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
),
_buildQuantityButton(
icon: Icons.add,
onPressed: selectedQuantity < 100
? () {
setState(() {
selectedQuantity++;
});
}
: null,
),
],
),
),
const SizedBox(width: 8),
Expanded(
child: GestureDetector(
onTapDown: _onTapDown,
onTapUp: _onTapUp,
onTapCancel: _onTapCancel,
onTap: () {
widget.onAddToCart(widget.product, selectedQuantity);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
const Icon(
Icons.shopping_cart,
color: Colors.white,
),
const SizedBox(width: 8),
Expanded(
child: Text(
'${widget.product.name} (x$selectedQuantity) ajouté au panier',
overflow: TextOverflow.ellipsis,
),
),
],
),
backgroundColor: Colors.green,
duration: const Duration(seconds: 1),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
);
},
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 10,
horizontal: 12,
),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [
Color.fromARGB(255, 4, 54, 95),
Color.fromARGB(255, 6, 80, 140),
],
),
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: const Color.fromARGB(255, 4, 54, 95).withOpacity(0.3),
blurRadius: 6,
offset: const Offset(0, 3),
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.add_shopping_cart,
color: Colors.white,
size: 16,
),
const SizedBox(width: 6),
const Flexible(
child: Text(
'Ajouter',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 12,
),
overflow: TextOverflow.ellipsis,
),
),
],
),
),
),
),
],
),
],
),
),
),
],
),
),
),
),
),
);
},
),
);
}
Widget _buildPlaceholderImage() {
return Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
color: Colors.grey.shade200,
borderRadius: BorderRadius.circular(20),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.image_outlined,
size: 40,
color: Colors.grey.shade400,
),
const SizedBox(height: 8),
Text(
'Image non disponible',
style: TextStyle(
color: Colors.grey.shade500,
fontSize: 12,
),
),
],
),
);
}
Widget _buildQuantityButton({
required IconData icon,
required VoidCallback? onPressed,
}) {
return Material(
color: Colors.transparent,
child: InkWell(
onTap: onPressed,
borderRadius: BorderRadius.circular(15),
child: Container(
padding: const EdgeInsets.all(6),
child: Icon(
icon,
size: 16,
color: onPressed != null ? const Color.fromARGB(255, 4, 54, 95) : Colors.grey,
),
),
),
);
}
}

494
lib/Views/registrationPage.dart

@ -1,8 +1,9 @@
import 'package:flutter/material.dart';
import 'package:youmazgestion/Models/users.dart';
import 'package:youmazgestion/Models/role.dart';
import 'package:youmazgestion/accueil.dart';
import '../Services/authDatabase.dart';
import '../Services/app_database.dart'; // Changé de authDatabase.dart
class RegistrationPage extends StatefulWidget {
const RegistrationPage({super.key});
@ -17,7 +18,11 @@ class _RegistrationPageState extends State<RegistrationPage> {
late TextEditingController _emailController;
late TextEditingController _usernameController;
late TextEditingController _passwordController;
String _selectedRole = 'user'; // Default role is 'user'
List<Role> _availableRoles = [];
Role? _selectedRole;
bool _isLoading = false;
bool _isLoadingRoles = true;
@override
void initState() {
@ -27,7 +32,59 @@ class _RegistrationPageState extends State<RegistrationPage> {
_emailController = TextEditingController();
_usernameController = TextEditingController();
_passwordController = TextEditingController();
AuthDatabase.instance.initDatabase();
_initializeDatabase();
}
Future<void> _initializeDatabase() async {
try {
await AppDatabase.instance.initDatabase();
await _loadRoles();
} catch (error) {
print('Erreur lors de l\'initialisation: $error');
if (mounted) {
_showErrorDialog('Erreur d\'initialisation',
'Impossible d\'initialiser l\'application. Veuillez redémarrer.');
}
}
}
Future<void> _loadRoles() async {
try {
final roles = await AppDatabase.instance.getRoles();
if (mounted) {
setState(() {
_availableRoles = roles;
_selectedRole = roles.isNotEmpty ? roles.first : null;
_isLoadingRoles = false;
});
}
// Si aucun rôle n'existe, créer des rôles par défaut
if (roles.isEmpty) {
await _createDefaultRoles();
await _loadRoles(); // Recharger après création
}
} catch (error) {
print('Erreur lors du chargement des rôles: $error');
if (mounted) {
setState(() {
_isLoadingRoles = false;
});
}
}
}
Future<void> _createDefaultRoles() async {
try {
await AppDatabase.instance.createRole(Role(designation: 'Admin'));
await AppDatabase.instance.createRole(Role(designation: 'Utilisateur'));
await AppDatabase.instance.createRole(Role(designation: 'Gestionnaire'));
print('Rôles par défaut créés');
} catch (error) {
print('Erreur lors de la création des rôles par défaut: $error');
}
}
@override
@ -40,148 +97,319 @@ class _RegistrationPageState extends State<RegistrationPage> {
super.dispose();
}
void _register() {
// Get the entered user information
final String name = _nameController.text;
final String lastName = _lastNameController.text;
final String email = _emailController.text;
final String username = _usernameController.text;
final String password = _passwordController.text;
final String role = _selectedRole;
// Create a new user object
final Users user = Users(
id: 0, // The id will be assigned automatically by the database
name: name,
lastName: lastName,
email: email,
password: password,
username: username,
role: role,
);
bool _validateFields() {
if (_nameController.text.trim().isEmpty ||
_lastNameController.text.trim().isEmpty ||
_emailController.text.trim().isEmpty ||
_usernameController.text.trim().isEmpty ||
_passwordController.text.trim().isEmpty ||
_selectedRole == null) {
_showErrorDialog('Champs manquants', 'Veuillez remplir tous les champs.');
return false;
}
// Save the user in the database
AuthDatabase.instance.createUser(user).then((value) {
// Registration successful
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Registration Successful'),
content: const Text('You have successfully registered.'),
actions: <Widget>[
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
// Navigate to the login page
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const AccueilPage()),
);
},
child: const Text('OK'),
),
],
);
},
);
}).catchError((error) {
print(error);
// Registration failed
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Registration Failed'),
content: const Text('An error occurred during registration.'),
actions: <Widget>[
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('OK'),
),
],
);
},
);
// Validation basique de l'email
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(_emailController.text.trim())) {
_showErrorDialog('Email invalide', 'Veuillez entrer un email valide.');
return false;
}
// Validation basique du mot de passe
if (_passwordController.text.length < 6) {
_showErrorDialog('Mot de passe trop court', 'Le mot de passe doit contenir au moins 6 caractères.');
return false;
}
return true;
}
void _register() async {
if (_isLoading) return;
if (!_validateFields()) return;
setState(() {
_isLoading = true;
});
try {
// Créer l'objet utilisateur avec le nouveau modèle
final Users user = Users(
name: _nameController.text.trim(),
lastName: _lastNameController.text.trim(),
email: _emailController.text.trim(),
password: _passwordController.text.trim(),
username: _usernameController.text.trim(),
roleId: _selectedRole!.id!, // Utiliser l'ID du rôle
roleName: _selectedRole!.designation, // Pour l'affichage
);
// Sauvegarder l'utilisateur dans la base de données
final int userId = await AppDatabase.instance.createUser(user);
print('Utilisateur créé avec l\'ID: $userId');
// Inscription réussie
if (mounted) {
_showSuccessDialog();
}
} catch (error) {
print('Erreur lors de l\'inscription: $error');
String errorMessage = 'Une erreur est survenue lors de l\'inscription.';
// Personnaliser le message d'erreur selon le type d'erreur
if (error.toString().contains('UNIQUE constraint failed: users.email')) {
errorMessage = 'Cet email est déjà utilisé.';
} else if (error.toString().contains('UNIQUE constraint failed: users.username')) {
errorMessage = 'Ce nom d\'utilisateur est déjà pris.';
}
if (mounted) {
_showErrorDialog('Inscription échouée', errorMessage);
}
} finally {
if (mounted) {
setState(() {
_isLoading = false;
});
}
}
}
void _showSuccessDialog() {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Inscription réussie'),
content: const Text('Vous vous êtes inscrit avec succès.'),
actions: <Widget>[
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const AccueilPage()),
);
},
child: const Text('OK'),
),
],
);
},
);
}
void _showErrorDialog(String title, String message) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(title),
content: Text(message),
actions: <Widget>[
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('OK'),
),
],
);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Registration'),
title: const Text(
'Inscription',
style: TextStyle(color: Colors.white),
),
backgroundColor: const Color.fromARGB(255, 4, 54, 95),
centerTitle: true,
iconTheme: const IconThemeData(color: Colors.white),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextField(
controller: _nameController,
decoration: const InputDecoration(
labelText: 'First Name',
),
),
const SizedBox(height: 16.0),
TextField(
controller: _lastNameController,
decoration: const InputDecoration(
labelText: 'Last Name',
),
body: _isLoadingRoles
? const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text('Chargement des rôles...'),
],
),
const SizedBox(height: 16.0),
TextField(
controller: _emailController,
decoration: const InputDecoration(
labelText: 'Email',
)
: Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
const Icon(
Icons.person_add,
size: 64,
color: Color.fromARGB(255, 4, 54, 95),
),
const SizedBox(height: 16),
TextField(
controller: _nameController,
enabled: !_isLoading,
decoration: InputDecoration(
labelText: 'Prénom',
prefixIcon: const Icon(Icons.person),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
const SizedBox(height: 16.0),
TextField(
controller: _lastNameController,
enabled: !_isLoading,
decoration: InputDecoration(
labelText: 'Nom',
prefixIcon: const Icon(Icons.person_outline),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
const SizedBox(height: 16.0),
TextField(
controller: _emailController,
enabled: !_isLoading,
decoration: InputDecoration(
labelText: 'Email',
prefixIcon: const Icon(Icons.email),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
keyboardType: TextInputType.emailAddress,
),
const SizedBox(height: 16.0),
TextField(
controller: _usernameController,
enabled: !_isLoading,
decoration: InputDecoration(
labelText: 'Nom d\'utilisateur',
prefixIcon: const Icon(Icons.account_circle),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
const SizedBox(height: 16.0),
TextField(
controller: _passwordController,
enabled: !_isLoading,
decoration: InputDecoration(
labelText: 'Mot de passe',
prefixIcon: const Icon(Icons.lock),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
helperText: 'Au moins 6 caractères',
),
obscureText: true,
),
const SizedBox(height: 16.0),
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<Role>(
value: _selectedRole,
hint: const Text('Sélectionner un rôle'),
isExpanded: true,
onChanged: _isLoading
? null
: (Role? newValue) {
setState(() {
_selectedRole = newValue;
});
},
items: _availableRoles
.map<DropdownMenuItem<Role>>((Role role) {
return DropdownMenuItem<Role>(
value: role,
child: Row(
children: [
const Icon(Icons.badge, size: 20),
const SizedBox(width: 8),
Text(role.designation),
],
),
);
}).toList(),
),
),
),
const SizedBox(height: 24.0),
SizedBox(
width: double.infinity,
height: 48,
child: ElevatedButton(
onPressed: _isLoading ? null : _register,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF0015B7),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: _isLoading
? const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
),
),
SizedBox(width: 12),
Text(
'Inscription en cours...',
style: TextStyle(color: Colors.white),
),
],
)
: const Text(
'S\'inscrire',
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
),
],
),
),
),
],
),
keyboardType: TextInputType.emailAddress,
),
const SizedBox(height: 16.0),
TextField(
controller: _usernameController,
decoration: const InputDecoration(
labelText: 'Username',
),
),
const SizedBox(height: 16.0),
TextField(
controller: _passwordController,
decoration: const InputDecoration(
labelText: 'Password',
),
obscureText: true,
),
const SizedBox(height: 16.0),
DropdownButton<String>(
value: _selectedRole,
onChanged: (String? newValue) {
setState(() {
_selectedRole = newValue!;
});
},
items: <String>['admin', 'user']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
const SizedBox(height: 16.0),
ElevatedButton(
onPressed: _register,
child: const Text('Register'),
),
],
),
),
),
),
);
}
}

285
lib/accueil.dart

@ -5,6 +5,7 @@ import 'package:get/get.dart';
import 'package:quantity_input/quantity_input.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:youmazgestion/Views/particles.dart' show ParticleBackground;
import 'package:youmazgestion/Views/produitsCard.dart';
import 'Components/appDrawer.dart';
import 'Components/app_bar.dart';
import 'Components/cartItem.dart';
@ -35,7 +36,7 @@ class _AccueilPageState extends State<AccueilPage> {
int orderId = 0;
List<CartItem> selectedProducts = [];
int selectedQuantity = 1; // Quantité sélectionnée par défaut
int selectedQuantity = 1;
double totalCartPrice = 0;
double amountPaid = 0;
@ -55,16 +56,15 @@ class _AccueilPageState extends State<AccueilPage> {
role = prefs.getString('role') ?? 'Rôle inconnu';
});
}
Future<void> saveOrderToDatabase() async {
final totalPrice = calculateTotalPrice();
final dateTime = DateTime.now().toString();
String user = userController.username;
// Insert the order into the database
orderId = await orderDatabase.insertOrder(
totalPrice, dateTime, MyApp.startDate!, user);
// Insert order items into the database
for (final cartItem in selectedProducts) {
final product = cartItem.product;
final quantity = cartItem.quantity;
@ -73,7 +73,6 @@ class _AccueilPageState extends State<AccueilPage> {
await orderDatabase.insertOrderItem(
orderId, product.name, quantity, price);
// Mettre à jour le stock du produit
final updatedStock = product.stock! - quantity;
await productDatabase.updateStock(product.id!, updatedStock);
}
@ -85,7 +84,6 @@ class _AccueilPageState extends State<AccueilPage> {
final categories = await productDatabase.getCategories();
final productsByCategory = <String, List<Product>>{};
// Trier les catégories selon votre préférence
categories.sort();
for (final categoryName in categories) {
@ -113,47 +111,34 @@ class _AccueilPageState extends State<AccueilPage> {
return totalPrice;
}
void addToCartWithDetails(Product product) {
void addToCartWithDetails(Product product, int quantity) {
setState(() {
final existingCartItem = selectedProducts.firstWhere(
(cartItem) => cartItem.product == product,
(cartItem) => cartItem.product.id == product.id,
orElse: () => CartItem(product, 0),
);
if (existingCartItem.quantity == 0) {
selectedProducts.add(CartItem(product, selectedQuantity));
selectedProducts.add(CartItem(product, quantity));
} else {
existingCartItem.quantity += selectedQuantity;
existingCartItem.quantity += quantity;
}
});
// Afficher une notification
Get.snackbar(
'Produit ajouté',
'Le produit ${product.name} a été ajouté au panier',
'${product.name} (x$quantity) ajouté au panier',
snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 1),
backgroundColor: Colors.green,
colorText: Colors.white,
);
resetQuantityAfterDelay();
}
Future<void> resetQuantityAfterDelay() async {
await Future.delayed(const Duration(seconds: 1));
setState(() {
selectedQuantity = 1;
});
}
void showTicketPage() {
// Calculer la somme totale du panier
final double totalCartPrice = calculateTotalPrice();
// Vérifier si des produits sont présents dans le panier
if (selectedProducts.isNotEmpty) {
// Vérifier si la somme payée est suffisante et supérieure ou égale au total du panier
if (amountPaid >= totalCartPrice) {
// Passer les produits et les informations de l'entreprise à la page du ticket
Get.offAll(TicketPage(
businessName: 'Youmaz',
businessAddress:
@ -164,10 +149,9 @@ class _AccueilPageState extends State<AccueilPage> {
amountPaid: amountPaid,
));
} else {
// Afficher un message d'erreur si la somme payée est insuffisante
Get.snackbar(
'Paiement incomplet',
'Le montant payé est insuffisant. Veuillez payer le montant total du panier.',
'Le montant payé est insuffisant.',
snackPosition: SnackPosition.BOTTOM,
duration: const Duration(seconds: 3),
backgroundColor: Colors.red,
@ -175,10 +159,9 @@ class _AccueilPageState extends State<AccueilPage> {
);
}
} else {
// Afficher un message d'erreur si le panier est vide
Get.snackbar(
'Panier vide',
'Le panier est vide. Veuillez ajouter des produits avant de passer commande.',
'Ajoutez des produits avant de passer commande.',
snackPosition: SnackPosition.BOTTOM,
duration: const Duration(seconds: 3),
backgroundColor: Colors.red,
@ -198,7 +181,7 @@ class _AccueilPageState extends State<AccueilPage> {
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Colors.white, const Color.fromARGB(255, 4, 54, 95)],
colors: [Colors.white, Color.fromARGB(255, 4, 54, 95)],
),
),
child: FutureBuilder<Map<String, List<Product>>>(
@ -207,35 +190,26 @@ class _AccueilPageState extends State<AccueilPage> {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
print('erreur:${snapshot.error}');
return const Center(
child: Text("Erreur lors du chargement des produits"));
return const Center(child: Text("Erreur de chargement"));
} else if (snapshot.hasData) {
final Map<String, List<Product>> productsByCategory =
snapshot.data!;
final productsByCategory = snapshot.data!;
final categories = productsByCategory.keys.toList();
if (!MyApp.isRegisterOpen) {
// Afficher le bouton "Démarrer la caisse" si la variable isRegisterOpen est false
return Column(
children: [
Text('welcome $username ! votre role est $role'),
Text('Bienvenue $username ! (Rôle: $role)'),
Center(
child: ElevatedButton(
onPressed: () {
setState(() {
MyApp.isRegisterOpen = true;
// mettre startDate à la date actuelle et au format yyyy-MM-dd
String formattedDate =
DateFormat('yyyy-MM-dd').format(DateTime.now());
startDate =
DateFormat('yyyy-MM-dd').parse(formattedDate);
MyApp.startDate = startDate;
var datee = DateFormat('yyyy-MM-dd')
.format(startDate!)
.toString();
workDatabase.insertDate(datee);
workDatabase.insertDate(formattedDate);
});
},
child: const Text('Démarrer la caisse'),
@ -244,7 +218,6 @@ class _AccueilPageState extends State<AccueilPage> {
],
);
} else {
// Afficher le contenu de la page d'accueil
return Row(
children: [
Expanded(
@ -266,10 +239,7 @@ class _AccueilPageState extends State<AccueilPage> {
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
fontStyle: FontStyle.italic,
decorationThickness: 2,
),
textAlign: TextAlign.center,
),
),
),
@ -284,125 +254,11 @@ class _AccueilPageState extends State<AccueilPage> {
itemCount: products.length,
itemBuilder: (context, index) {
final product = products[index];
return Card(
elevation: 7,
shadowColor: Colors.redAccent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
child: InkWell(
onTap: () {},
child: Container(
padding: const EdgeInsets.all(8),
child: Align(
alignment: Alignment.center,
child: Column(
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
Expanded(
child: AspectRatio(
aspectRatio: 14,
child: product.image != null
? Image.file(
File(product.image),
fit: BoxFit.cover,
)
: Image.asset(
'assets/placeholder_image.png',
fit: BoxFit.cover,
),
),
),
Center(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Text(
product.name,
style: const TextStyle(
fontWeight:
FontWeight.bold,
fontSize: 16,
),
),
const SizedBox(height: 4),
Text(
'${product.price.toStringAsFixed(2)} fcfa',
style: const TextStyle(
fontWeight:
FontWeight.bold,
fontSize: 14,
color: Colors.green,
),
),
if (product
.isStockDefined()) ...[
const SizedBox(height: 8),
const Text(
'En stock',
style: TextStyle(
fontWeight:
FontWeight.bold,
fontSize: 12,
color: Colors.green,
),
),
],
const SizedBox(height: 8),
QuantityInput(
value: selectedQuantity,
minValue: 1,
maxValue: 100,
step: 1,
inputWidth: 60,
buttonColor:
Colors.redAccent,
onChanged:
(String value) {
setState(() {
selectedQuantity =
int.parse(value);
});
},
),
const SizedBox(height: 8),
ElevatedButton(
onPressed: () {
addToCartWithDetails(
product);
},
style: ElevatedButton
.styleFrom(
backgroundColor:
const Color.fromARGB(255, 4, 54, 95),
onPrimary: Colors.white,
shape:
RoundedRectangleBorder(
borderRadius:
BorderRadius
.circular(18),
),
),
child: const Text(
'Ajouter au panier'
,
style: TextStyle(color: Colors.white),
),
),
],
),
),
],
),
),
),
),
return ProductCard(
product: product,
onAddToCart: (product, quantity) {
addToCartWithDetails(product, quantity);
},
);
},
),
@ -419,93 +275,58 @@ class _AccueilPageState extends State<AccueilPage> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Panier',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: const Color.fromARGB(255, 4, 54, 95),
),
),
Icon(
Icons.shopping_cart,
color: Colors.red,
),
],
const Text(
'Panier',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
Expanded(
child: ListView.builder(
itemCount: selectedProducts.length,
itemBuilder: (context, index) {
final cartItem = selectedProducts[index];
final product = cartItem.product;
final quantity = cartItem.quantity;
return ListTile(
title: Text(product.name),
subtitle: Text(product.category),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'${product.price.toStringAsFixed(2)} fcfa'),
const SizedBox(width: 8),
Text('x $quantity'),
const SizedBox(width: 8),
IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
setState(() {
selectedProducts.removeAt(index);
});
},
color: Colors.redAccent,
),
],
child: selectedProducts.isEmpty
? const Center(
child: Text("Votre panier est vide"),
)
: ListView.builder(
itemCount: selectedProducts.length,
itemBuilder: (context, index) {
final cartItem = selectedProducts[index];
return ListTile(
title: Text(cartItem.product.name),
subtitle: Text(
'${cartItem.product.price} FCFA x ${cartItem.quantity}'),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
setState(() {
selectedProducts.removeAt(index);
});
},
),
);
},
),
);
},
),
),
const SizedBox(height: 16),
Text(
'Total: ${calculateTotalPrice().toStringAsFixed(2)} fcfa',
textAlign: TextAlign.end,
'Total: ${calculateTotalPrice().toStringAsFixed(2)} FCFA',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
TextFormField(
TextField(
keyboardType: TextInputType.number,
decoration: const InputDecoration(
labelText: 'Montant payé',
),
onChanged: (value) {
setState(() {
amountPaid = double.parse(value);
});
amountPaid = double.tryParse(value) ?? 0;
},
),
const SizedBox(height: 8),
ElevatedButton(
onPressed: () {
saveOrderToDatabase();
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color.fromARGB(255, 4, 54, 95),
onPrimary: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18),
),
),
child: const Text('Payer'),
onPressed: saveOrderToDatabase,
child: const Text('Valider la commande'),
),
],
),

15
lib/controller/userController.dart

@ -1,5 +1,6 @@
import 'package:get/get.dart';
import 'package:youmazgestion/Models/users.dart';
import 'package:youmazgestion/Services/app_database.dart';
class UserController extends GetxController {
final _username = ''.obs;
@ -30,4 +31,18 @@ class UserController extends GetxController {
_password.value = user.password;
print(_password.value);
}
Future<bool> hasPermission(String permissionName) async {
// Utilisez votre instance de AppDatabase pour vérifier la permission
return await AppDatabase.instance.hasPermission(username, permissionName);
}
Future<bool> hasAnyPermission(List<String> permissionNames) async {
for (String permissionName in permissionNames) {
if (await hasPermission(permissionName)) {
return true;
}
}
return false;
}
}

34
lib/main.dart

@ -1,22 +1,38 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:youmazgestion/Services/authDatabase.dart';
import 'package:youmazgestion/Services/app_database.dart';
import 'Services/productDatabase.dart';
import 'my_app.dart';
import 'package:logging/logging.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await ProductDatabase.instance.initDatabase();
await AuthDatabase.instance.initDatabase();
setupLogger(); // Appel à la fonction setupLogger()
try {
// Initialiser les bases de données une seule fois
await ProductDatabase.instance.initDatabase();
await AppDatabase.instance.initDatabase();
//await OrderDatabase.instance.initDatabase();
runApp(const GetMaterialApp(
debugShowCheckedModeBanner: false,
home: MyApp(),
));
// Afficher les informations de la base (pour debug)
await AppDatabase.instance.printDatabaseInfo();
setupLogger();
runApp(const GetMaterialApp(
debugShowCheckedModeBanner: false,
home: MyApp(),
));
} catch (e) {
print('Erreur lors de l\'initialisation: $e');
// Vous pourriez vouloir afficher une page d'erreur ici
runApp(MaterialApp(
home: Scaffold(
body: Center(
child: Text('Erreur d\'initialisation: $e'),
),
),
));
}
}
void setupLogger() {

16
pubspec.lock

@ -37,10 +37,10 @@ packages:
dependency: transitive
description:
name: async
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
url: "https://pub.dev"
source: hosted
version: "2.12.0"
version: "2.13.0"
barcode:
dependency: transitive
description:
@ -173,10 +173,10 @@ packages:
dependency: transitive
description:
name: fake_async
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev"
source: hosted
version: "1.3.2"
version: "1.3.3"
ffi:
dependency: transitive
description:
@ -500,10 +500,10 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
url: "https://pub.dev"
source: hosted
version: "10.0.8"
version: "10.0.9"
leak_tracker_flutter_testing:
dependency: transitive
description:
@ -1161,10 +1161,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
url: "https://pub.dev"
source: hosted
version: "14.3.1"
version: "15.0.0"
web:
dependency: transitive
description:

1
pubspec.yaml

@ -99,6 +99,7 @@ flutter:
- assets/database/products2.db
- assets/database/usersdb.db
- assets/database/work.db
- assets/database/roles.db
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware

Loading…
Cancel
Save