From b415b9f50142e7d57e02b3ca6e5a6b897d4d7f82 Mon Sep 17 00:00:00 2001 From: Stephane Date: Thu, 24 Jul 2025 21:46:09 +0300 Subject: [PATCH] qr code scanner --- lib/Components/appDrawer.dart | 129 +++++++++++------- .../demande_sortie_personnelle_page.dart | 66 ++++----- lib/config/DatabaseConfig.dart | 2 +- lib/controller/HistoryController.dart | 31 ++--- lib/controller/userController.dart | 14 +- lib/main.dart | 32 ++--- 6 files changed, 147 insertions(+), 127 deletions(-) diff --git a/lib/Components/appDrawer.dart b/lib/Components/appDrawer.dart index 8da51b6..c83c965 100644 --- a/lib/Components/appDrawer.dart +++ b/lib/Components/appDrawer.dart @@ -47,10 +47,10 @@ class CustomDrawer extends StatelessWidget { children: [ // Header utilisateur _buildUserHeader(controller), - + // ✅ CORRIGÉ: Construction avec gestion des valeurs null ..._buildDrawerItemsFromSessionCache(), - + // Déconnexion const Divider(), _buildLogoutItem(), @@ -64,7 +64,7 @@ class CustomDrawer extends StatelessWidget { /// ✅ CORRIGÉ: Construction avec validation robuste des données List _buildDrawerItemsFromSessionCache() { List drawerItems = []; - + // Vérifier si le cache est prêt if (!userController.isCacheReady) { return [ @@ -90,17 +90,17 @@ class CustomDrawer extends StatelessWidget { // Récupérer les menus depuis le cache de session final rawUserMenus = userController.getUserMenus(); - + // 🛡️ VALIDATION: Filtrer les menus valides final validMenus = >[]; final invalidMenus = >[]; - + for (var menu in rawUserMenus) { // Vérifier que les champs essentiels ne sont pas null final name = menu['name']; final route = menu['route']; final id = menu['id']; - + if (name != null && route != null && route.toString().isNotEmpty) { validMenus.add({ 'id': id, @@ -109,33 +109,35 @@ class CustomDrawer extends StatelessWidget { }); } else { invalidMenus.add(menu); - print("⚠️ Menu invalide ignoré dans CustomDrawer: id=$id, name='$name', route='$route'"); + // print("⚠️ Menu invalide ignoré dans CustomDrawer: id=$id, name='$name', route='$route'"); } } - + // ✅ NOUVEAU: Ajouter le menu Demande Assignation pour Super Admin uniquement if (userController.role == 'Super Admin') { // Vérifier si le menu n'existe pas déjà - final bool demandeAssignationExists = validMenus.any((menu) => menu['route'] == '/demande-assignation'); - + final bool demandeAssignationExists = + validMenus.any((menu) => menu['route'] == '/demande-assignation'); + if (!demandeAssignationExists) { validMenus.add({ 'id': 'super_admin_demande_assignation', 'name': 'Demande Assignation', 'route': '/demande-assignation', }); - print("✅ Menu 'Demande Assignation' ajouté pour Super Admin"); + // print("✅ Menu 'Demande Assignation' ajouté pour Super Admin"); } } // ✅ NOUVEAU: Ajouter les menus de sorties personnelles _addSortiesPersonnellesMenus(validMenus); - + // Afficher les statistiques de validation if (invalidMenus.isNotEmpty) { - print("📊 CustomDrawer: ${validMenus.length} menus valides, ${invalidMenus.length} invalides"); + // print( + // "📊 CustomDrawer: ${validMenus.length} menus valides, ${invalidMenus.length} invalides"); } - + if (validMenus.isEmpty) { return [ const Padding( @@ -156,9 +158,10 @@ class CustomDrawer extends StatelessWidget { uniqueMenus[route] = menu; } final deduplicatedMenus = uniqueMenus.values.toList(); - + if (deduplicatedMenus.length != validMenus.length) { - print("🔧 CustomDrawer: ${validMenus.length - deduplicatedMenus.length} doublons supprimés"); + // print( + // "🔧 CustomDrawer: ${validMenus.length - deduplicatedMenus.length} doublons supprimés"); } // Organiser les menus par catégories @@ -173,7 +176,9 @@ class CustomDrawer extends StatelessWidget { }; // Accueil toujours en premier - final accueilMenu = deduplicatedMenus.where((menu) => menu['route'] == '/accueil').firstOrNull; + final accueilMenu = deduplicatedMenus + .where((menu) => menu['route'] == '/accueil') + .firstOrNull; if (accueilMenu != null) { drawerItems.add(_buildDrawerItemFromMenu(accueilMenu)); } @@ -181,13 +186,13 @@ class CustomDrawer extends StatelessWidget { // Catégoriser les autres menus avec validation supplémentaire for (var menu in deduplicatedMenus) { final route = menu['route'] as String; - + // ✅ Validation supplémentaire avant categorisation if (route.isEmpty) { - print("⚠️ Route vide ignorée: ${menu['name']}"); + // print("⚠️ Route vide ignorée: ${menu['name']}"); continue; } - + switch (route) { case '/accueil': // Déjà traité @@ -224,7 +229,7 @@ class CustomDrawer extends StatelessWidget { break; default: // Menu non catégorisé - print("⚠️ Menu non catégorisé: $route"); + // print("⚠️ Menu non catégorisé: $route"); break; } } @@ -233,10 +238,11 @@ class CustomDrawer extends StatelessWidget { categorizedMenus.forEach((categoryName, menus) { if (menus.isNotEmpty) { // ✅ Afficher la catégorie SUPER ADMIN seulement pour les Super Admin - if (categoryName == 'SUPER ADMIN' && userController.role != 'Super Admin') { + if (categoryName == 'SUPER ADMIN' && + userController.role != 'Super Admin') { return; // Skip cette catégorie } - + drawerItems.add(_buildCategoryHeader(categoryName)); for (var menu in menus) { drawerItems.add(_buildDrawerItemFromMenu(menu)); @@ -250,37 +256,42 @@ class CustomDrawer extends StatelessWidget { /// ✅ NOUVEAU: Méthode pour ajouter les menus de sorties personnelles void _addSortiesPersonnellesMenus(List> validMenus) { // Menu Demande Sortie Personnelle - Accessible à tous les utilisateurs connectés - final bool demandeSortieExists = validMenus.any((menu) => menu['route'] == '/demande-sortie-personnelle'); + final bool demandeSortieExists = validMenus + .any((menu) => menu['route'] == '/demande-sortie-personnelle'); if (!demandeSortieExists) { validMenus.add({ 'id': 'demande_sortie_personnelle', 'name': 'Demande sortie personnelle', 'route': '/demande-sortie-personnelle', }); - print("✅ Menu 'Demande sortie personnelle' ajouté pour tous les utilisateurs"); + // print( + // "✅ Menu 'Demande sortie personnelle' ajouté pour tous les utilisateurs"); } // Menu Historique Sorties Personnelles - Accessible à tous les utilisateurs connectés - final bool historiqueSortiesExists = validMenus.any((menu) => menu['route'] == '/historique-sorties-personnelles'); + final bool historiqueSortiesExists = validMenus + .any((menu) => menu['route'] == '/historique-sorties-personnelles'); if (!historiqueSortiesExists) { validMenus.add({ 'id': 'historique_sorties_personnelles', 'name': 'Historique sorties personnelles', 'route': '/historique-sorties-personnelles', }); - print("✅ Menu 'Historique sorties personnelles' ajouté pour tous les utilisateurs"); + // print( + // "✅ Menu 'Historique sorties personnelles' ajouté pour tous les utilisateurs"); } // Menu Approbation Sorties - Accessible SEULEMENT aux Super Admin if (userController.role == 'Super Admin') { - final bool approbationSortiesExists = validMenus.any((menu) => menu['route'] == '/approbation-sorties'); + final bool approbationSortiesExists = + validMenus.any((menu) => menu['route'] == '/approbation-sorties'); if (!approbationSortiesExists) { validMenus.add({ 'id': 'approbation_sorties', 'name': 'Approuver sorties', 'route': '/approbation-sorties', }); - print("✅ Menu 'Approuver sorties' ajouté pour Super Admin"); + // print("✅ Menu 'Approuver sorties' ajouté pour Super Admin"); } } } @@ -290,20 +301,21 @@ class CustomDrawer extends StatelessWidget { // 🛡️ VALIDATION: Vérification des types avec gestion des null final nameObj = menu['name']; final routeObj = menu['route']; - + if (nameObj == null || routeObj == null) { - print("⚠️ Menu invalide dans _buildDrawerItemFromMenu: name=$nameObj, route=$routeObj"); + // print( + // "⚠️ Menu invalide dans _buildDrawerItemFromMenu: name=$nameObj, route=$routeObj"); return const SizedBox.shrink(); } - + final String name = nameObj.toString(); final String route = routeObj.toString(); - + if (name.isEmpty || route.isEmpty) { - print("⚠️ Menu avec valeurs vides: name='$name', route='$route'"); + // print("⚠️ Menu avec valeurs vides: name='$name', route='$route'"); return const SizedBox.shrink(); } - + // Mapping des routes vers les widgets et icônes final Map> routeMapping = { '/accueil': { @@ -391,11 +403,12 @@ class CustomDrawer extends StatelessWidget { final routeData = routeMapping[route]; if (routeData == null) { - print("⚠️ Route non reconnue: '$route' pour le menu '$name'"); + // print("⚠️ Route non reconnue: '$route' pour le menu '$name'"); return ListTile( leading: const Icon(Icons.help_outline, color: Colors.grey), title: Text(name), - subtitle: Text("Route: $route", style: const TextStyle(fontSize: 10, color: Colors.grey)), + subtitle: Text("Route: $route", + style: const TextStyle(fontSize: 10, color: Colors.grey)), onTap: () { Get.snackbar( "Route non configurée", @@ -417,7 +430,8 @@ class CustomDrawer extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ // ✅ NOUVEAU: Badge Super Admin pour les menus réservés - if ((route == '/demande-assignation' || route == '/approbation-sorties') && + if ((route == '/demande-assignation' || + route == '/approbation-sorties') && userController.role == 'Super Admin') ...[ Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), @@ -437,7 +451,8 @@ class CustomDrawer extends StatelessWidget { const SizedBox(width: 4), ], // ✅ NOUVEAU: Badge "Tous" pour les menus accessibles à tous - if (route == '/demande-sortie-personnelle' || route == '/historique-sorties-personnelles') ...[ + if (route == '/demande-sortie-personnelle' || + route == '/historique-sorties-personnelles') ...[ Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( @@ -544,7 +559,9 @@ class CustomDrawer extends StatelessWidget { Row( children: [ Text( - controller.role.isNotEmpty ? controller.role : 'Aucun rôle', + controller.role.isNotEmpty + ? controller.role + : 'Aucun rôle', style: const TextStyle( color: Colors.white70, fontSize: 12, @@ -554,7 +571,8 @@ class CustomDrawer extends StatelessWidget { if (controller.role == 'Super Admin') ...[ const SizedBox(width: 8), Container( - padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), + padding: const EdgeInsets.symmetric( + horizontal: 6, vertical: 2), decoration: BoxDecoration( color: Colors.red.shade600, borderRadius: BorderRadius.circular(8), @@ -586,8 +604,12 @@ class CustomDrawer extends StatelessWidget { Row( children: [ Icon( - controller.isCacheReady ? Icons.check_circle : Icons.hourglass_empty, - color: controller.isCacheReady ? Colors.green : Colors.orange, + controller.isCacheReady + ? Icons.check_circle + : Icons.hourglass_empty, + color: controller.isCacheReady + ? Colors.green + : Colors.orange, size: 12, ), const SizedBox(width: 4), @@ -604,7 +626,8 @@ class CustomDrawer extends StatelessWidget { ), ), // ✅ Bouton de rafraîchissement pour les admins - if (controller.role == 'Super Admin' || controller.role == 'Admin') ...[ + if (controller.role == 'Super Admin' || + controller.role == 'Admin') ...[ IconButton( icon: const Icon(Icons.refresh, color: Colors.white70, size: 20), onPressed: () async { @@ -630,28 +653,32 @@ class CustomDrawer extends StatelessWidget { // 🔧 Bouton de debug (à supprimer en production) if (controller.role == 'Super Admin') ...[ IconButton( - icon: const Icon(Icons.bug_report, color: Colors.white70, size: 18), + icon: + const Icon(Icons.bug_report, color: Colors.white70, size: 18), onPressed: () { // Debug des menus final menus = controller.getUserMenus(); String debugInfo = "MENUS DEBUG:\n"; for (var i = 0; i < menus.length; i++) { final menu = menus[i]; - debugInfo += "[$i] ID:${menu['id']}, Name:'${menu['name']}', Route:'${menu['route']}'\n"; + debugInfo += + "[$i] ID:${menu['id']}, Name:'${menu['name']}', Route:'${menu['route']}'\n"; } - + // ✅ NOUVEAU: Ajouter info sur les menus sorties personnelles debugInfo += "\n--- MENUS SORTIES PERSONNELLES ---\n"; debugInfo += "Rôle actuel: ${controller.role}\n"; debugInfo += "Menu Demande Sortie: Accessible à tous\n"; debugInfo += "Menu Historique: Accessible à tous\n"; - debugInfo += "Menu Approbation: ${controller.role == 'Super Admin' ? 'Accessible (Super Admin)' : 'Non accessible'}\n"; - + debugInfo += + "Menu Approbation: ${controller.role == 'Super Admin' ? 'Accessible (Super Admin)' : 'Non accessible'}\n"; + Get.dialog( AlertDialog( title: const Text("Debug Menus"), content: SingleChildScrollView( - child: Text(debugInfo, style: const TextStyle(fontSize: 12)), + child: + Text(debugInfo, style: const TextStyle(fontSize: 12)), ), actions: [ TextButton( @@ -797,4 +824,4 @@ class CustomDrawer extends StatelessWidget { }, ); } -} \ No newline at end of file +} diff --git a/lib/Views/demande_sortie_personnelle_page.dart b/lib/Views/demande_sortie_personnelle_page.dart index 54963c5..75b6867 100644 --- a/lib/Views/demande_sortie_personnelle_page.dart +++ b/lib/Views/demande_sortie_personnelle_page.dart @@ -56,47 +56,44 @@ class _DemandeSortiePersonnellePageState } void _scanQrOrBarcode() async { - // Open the mobile scanner as a modal widget await showDialog( context: context, builder: (context) { return AlertDialog( content: Container( width: double.maxFinite, - height: 400, // Adjust according to your needs + height: 400, child: MobileScanner( - onDetect: (barcodeCapture) { - String scanResult = barcodeCapture.rawValue ?? ''; - Navigator.of(context).pop(); // Close dialog after scanning - - if (scanResult.isEmpty) return; - - setState(() { - _searchController.text = scanResult; // Show scanned text - }); - - // Assume IMEI is always 15 digits, adjust if necessary - Product? found; - if (scanResult.length == 15 && - int.tryParse(scanResult) != null) { - found = - _products.firstWhereOrNull((p) => p.imei == scanResult); + onDetect: (BarcodeCapture barcodeCap) { + print("BarcodeCapture: $barcodeCap"); + // Now accessing the barcodes attribute + final List barcodes = barcodeCap.barcodes; + + if (barcodes.isNotEmpty) { + // Get the first detected barcode value + String? scanResult = barcodes.first.rawValue; + + print("Scanned Result: $scanResult"); + + if (scanResult != null && scanResult.isNotEmpty) { + setState(() { + _searchController.text = scanResult; + print( + "Updated Search Controller: ${_searchController.text}"); + }); + + // Close dialog after scanning + Navigator.of(context).pop(); + + // Refresh product list based on new search input + _filterProducts(); + } else { + print("Scan result was empty or null."); + Navigator.of(context).pop(); + } } else { - found = _products - .firstWhereOrNull((p) => p.reference == scanResult); - } - - if (found != null) { - setState(() { - _selectedProduct = found; - _filteredProducts = [found!]; - }); - } else { - _showErrorSnackbar('Aucun produit trouvé avec ce code.'); - setState(() { - _filteredProducts = []; - _selectedProduct = null; - }); + print("No barcodes detected."); + Navigator.of(context).pop(); } }, ), @@ -403,6 +400,9 @@ class _DemandeSortiePersonnellePageState hintText: 'Rechercher un produit...', prefixIcon: Icon(Icons.search, color: Colors.grey.shade600), ), + onChanged: (value) { + _filterProducts(); // Call to filter products + }, ), ), IconButton( diff --git a/lib/config/DatabaseConfig.dart b/lib/config/DatabaseConfig.dart index c5dd7e3..c21e9f7 100644 --- a/lib/config/DatabaseConfig.dart +++ b/lib/config/DatabaseConfig.dart @@ -80,7 +80,7 @@ class DatabaseConfig { config['database']?.toString().isNotEmpty == true && config['user'] != null; } catch (e) { - print("Erreur de validation de la configuration: $e"); + // print("Erreur de validation de la configuration: $e"); return false; } } diff --git a/lib/controller/HistoryController.dart b/lib/controller/HistoryController.dart index ed50ae3..c6bcdca 100644 --- a/lib/controller/HistoryController.dart +++ b/lib/controller/HistoryController.dart @@ -27,8 +27,6 @@ class HistoryController extends GetxController { getTotalSumOrdersByMonth(DateTime.now()); getOrdersByMonth(DateTime.now()); getOrderCountByMonth(DateTime.now()); - - } Future fetchOrders() async { @@ -60,9 +58,6 @@ class HistoryController extends GetxController { } } - - - List getDistinctDates() { final currentDate = DateTime.now(); final dates = [...orders.map((order) => order.startDate), currentDate] @@ -73,7 +68,9 @@ class HistoryController extends GetxController { List filterOrdersByDateRange(DateTime startDate) { final filteredList = orders - .where((order) => order.startDate != null && order.startDate!.isAtSameMomentAs(startDate)) + .where((order) => + order.startDate != null && + order.startDate!.isAtSameMomentAs(startDate)) .toList(); filteredOrders.value = filteredList; return filteredList; @@ -81,7 +78,8 @@ class HistoryController extends GetxController { Future> getOrdersByStartDate(DateTime startDate) async { try { - final ordersByStartDate = await _orderDatabase.getOrdersByStartDate(startDate); + final ordersByStartDate = + await _orderDatabase.getOrdersByStartDate(startDate); return ordersByStartDate; } catch (e) { print(e); @@ -89,22 +87,23 @@ class HistoryController extends GetxController { } } - double getTotalSumOrdersByStartDate (DateTime startDate) { + double getTotalSumOrdersByStartDate(DateTime startDate) { final filteredOrders = filterOrdersByDateRange(startDate); - double totalSum = filteredOrders.fold(0, (sum, order) => sum + order.totalPrice); + double totalSum = + filteredOrders.fold(0, (sum, order) => sum + order.totalPrice); return totalSum; } Future getTotalSumOrdersByMonth(DateTime date) async { final filteredOrders = await getOrdersByMonth(date); - double totalsum = filteredOrders.fold(0, (sum, order) => sum + order.totalPrice); + double totalsum = + filteredOrders.fold(0, (sum, order) => sum + order.totalPrice); totalSum.value = totalsum; - print(totalSum.value); + // print(totalSum.value); return totalSum; - } - Future> getProductQuantitiesByDate(DateTime date) async { + Future> getProductQuantitiesByDate(DateTime date) async { try { return await _orderDatabase.getProductQuantitiesByDate(date); } catch (e) { @@ -153,16 +152,10 @@ class HistoryController extends GetxController { } } - - Future getOrderCountByMonth(DateTime month) async { List orders = await getOrdersByMonth(month); int orderCount = orders.length; orderQuantity.value = orderCount; return orderCount; } - - - - } diff --git a/lib/controller/userController.dart b/lib/controller/userController.dart index 20f1cbf..36eb474 100644 --- a/lib/controller/userController.dart +++ b/lib/controller/userController.dart @@ -69,7 +69,7 @@ class UserController extends GetxController { // ✅ Précharger les permissions en arrière-plan (non bloquant) _preloadPermissionsInBackground(); } catch (dbError) { - print("❌ Erreur BDD, utilisation du fallback: $dbError"); + // print("❌ Erreur BDD, utilisation du fallback: $dbError"); _username.value = storedUsername; _email.value = prefs.getString('email') ?? ''; _role.value = storedRole; @@ -84,7 +84,7 @@ class UserController extends GetxController { } } } catch (e) { - print('❌ Erreur lors du chargement des données utilisateur: $e'); + // print('❌ Erreur lors du chargement des données utilisateur: $e'); } } @@ -129,10 +129,10 @@ class UserController extends GetxController { _userId.value = userId; _pointDeVenteId.value = user.pointDeVenteId ?? 0; - print("✅ Utilisateur mis à jour avec credentials:"); - print(" Username: ${_username.value}"); - print(" Role: ${_role.value}"); - print(" UserID: ${_userId.value}"); + // print("✅ Utilisateur mis à jour avec credentials:"); + // print(" Username: ${_username.value}"); + // print(" Role: ${_role.value}"); + // print(" UserID: ${_userId.value}"); saveUserData(); @@ -166,7 +166,7 @@ class UserController extends GetxController { await prefs.setString( 'point_de_vente_designation', _pointDeVenteDesignation.value); - print("✅ Données sauvegardées avec succès"); + // print("✅ Données sauvegardées avec succès"); } catch (e) { print('❌ Erreur lors de la sauvegarde: $e'); } diff --git a/lib/main.dart b/lib/main.dart index 4490c50..6b2718c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,35 +9,35 @@ void main() async { WidgetsFlutterBinding.ensureInitialized(); try { - print("Initialisation de l'application..."); - - // Pour le développement : supprimer toutes les tables (équivalent à deleteDatabaseFile) - // ATTENTION: Décommentez seulement si vous voulez réinitialiser la base - // await AppDatabase.instance.deleteDatabaseFile(); + // print("Initialisation de l'application..."); - // Initialiser la base de données MySQL - print("Connexion à la base de données MySQL..."); - // await AppDatabase.instance.initDatabase(); - print("Base de données initialisée avec succès !"); + // // Pour le développement : supprimer toutes les tables (équivalent à deleteDatabaseFile) + // // ATTENTION: Décommentez seulement si vous voulez réinitialiser la base + // // await AppDatabase.instance.deleteDatabaseFile(); + + // // Initialiser la base de données MySQL + // print("Connexion à la base de données MySQL..."); + // // await AppDatabase.instance.initDatabase(); + // print("Base de données initialisée avec succès !"); // Afficher les informations de la base (pour debug) await AppDatabase.instance.printDatabaseInfo(); - + // Initialiser le contrôleur utilisateur Get.put(UserController()); - print("Contrôleur utilisateur initialisé"); + // print("Contrôleur utilisateur initialisé"); // Configurer le logger setupLogger(); - print("Lancement de l'application..."); + // print("Lancement de l'application..."); runApp(const GetMaterialApp( debugShowCheckedModeBanner: false, home: MyApp(), )); } catch (e) { - print('Erreur lors de l\'initialisation: $e'); - + // print('Erreur lors de l\'initialisation: $e'); + // Afficher une page d'erreur avec plus de détails runApp(MaterialApp( debugShowCheckedModeBanner: false, @@ -129,6 +129,6 @@ void main() async { void setupLogger() { Logger.root.level = Level.ALL; Logger.root.onRecord.listen((record) { - print('${record.level.name}: ${record.time}: ${record.message}'); + // print('${record.level.name}: ${record.time}: ${record.message}'); }); -} \ No newline at end of file +}