diff --git a/lib/Components/QrScan.dart b/lib/Components/QrScan.dart index 0ad9243..bcaf4b1 100644 --- a/lib/Components/QrScan.dart +++ b/lib/Components/QrScan.dart @@ -25,13 +25,14 @@ class _ScanQRPageState extends State { } void _initializeController() { - if (!isMobile) { - setState(() { - _hasError = true; - _errorMessage = "Le scanner QR n'est pas disponible sur cette plateforme."; - }); - return; - } + if (!isMobile) { + setState(() { + _hasError = true; + _errorMessage = + "Le scanner QR n'est pas disponible sur cette plateforme."; + }); + return; + } try { cameraController = MobileScannerController( detectionSpeed: DetectionSpeed.noDuplicates, @@ -61,22 +62,25 @@ class _ScanQRPageState extends State { return Scaffold( appBar: AppBar( title: const Text('Scanner QR Code'), - actions: _hasError ? [] : [ - if (cameraController != null) ...[ - IconButton( - color: Colors.white, - icon: const Icon(Icons.flash_on, color: Colors.white), - iconSize: 32.0, - onPressed: () => cameraController!.toggleTorch(), - ), - IconButton( - color: Colors.white, - icon: const Icon(Icons.flip_camera_ios, color: Colors.white), - iconSize: 32.0, - onPressed: () => cameraController!.switchCamera(), - ), - ], - ], + actions: _hasError + ? [] + : [ + if (cameraController != null) ...[ + IconButton( + color: Colors.white, + icon: const Icon(Icons.flash_on, color: Colors.white), + iconSize: 32.0, + onPressed: () => cameraController!.toggleTorch(), + ), + IconButton( + color: Colors.white, + icon: + const Icon(Icons.flip_camera_ios, color: Colors.white), + iconSize: 32.0, + onPressed: () => cameraController!.switchCamera(), + ), + ], + ], ), body: _hasError ? _buildErrorWidget() : _buildScannerWidget(), ); @@ -150,7 +154,8 @@ class _ScanQRPageState extends State { children: [ const Icon(Icons.error, size: 64, color: Colors.red), const SizedBox(height: 16), - Text('Erreur: ${error.errorDetails?.message ?? 'Erreur inconnue'}'), + Text( + 'Erreur: ${error.errorDetails?.message ?? 'Erreur inconnue'}'), const SizedBox(height: 16), ElevatedButton( onPressed: () => _initializeController(), @@ -236,19 +241,25 @@ class QrScannerOverlay extends CustomPainter { ..style = PaintingStyle.stroke; // Coins du scanner - _drawCorner(canvas, borderPaint, areaLeft, areaTop, borderLength, true, true); - _drawCorner(canvas, borderPaint, areaLeft + areaSize, areaTop, borderLength, false, true); - _drawCorner(canvas, borderPaint, areaLeft, areaTop + areaSize, borderLength, true, false); - _drawCorner(canvas, borderPaint, areaLeft + areaSize, areaTop + areaSize, borderLength, false, false); + _drawCorner( + canvas, borderPaint, areaLeft, areaTop, borderLength, true, true); + _drawCorner(canvas, borderPaint, areaLeft + areaSize, areaTop, borderLength, + false, true); + _drawCorner(canvas, borderPaint, areaLeft, areaTop + areaSize, borderLength, + true, false); + _drawCorner(canvas, borderPaint, areaLeft + areaSize, areaTop + areaSize, + borderLength, false, false); } - void _drawCorner(Canvas canvas, Paint paint, double x, double y, double length, bool isLeft, bool isTop) { + void _drawCorner(Canvas canvas, Paint paint, double x, double y, + double length, bool isLeft, bool isTop) { final double horizontalStart = isLeft ? x : x - length; final double horizontalEnd = isLeft ? x + length : x; final double verticalStart = isTop ? y : y - length; final double verticalEnd = isTop ? y + length : y; - canvas.drawLine(Offset(horizontalStart, y), Offset(horizontalEnd, y), paint); + canvas.drawLine( + Offset(horizontalStart, y), Offset(horizontalEnd, y), paint); canvas.drawLine(Offset(x, verticalStart), Offset(x, verticalEnd), paint); } @@ -256,4 +267,4 @@ class QrScannerOverlay extends CustomPainter { bool shouldRepaint(covariant CustomPainter oldDelegate) { return false; } -} \ 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 e395637..54963c5 100644 --- a/lib/Views/demande_sortie_personnelle_page.dart +++ b/lib/Views/demande_sortie_personnelle_page.dart @@ -5,31 +5,33 @@ import 'package:youmazgestion/Components/appDrawer.dart'; import 'package:youmazgestion/Services/stock_managementDatabase.dart'; import 'package:youmazgestion/controller/userController.dart'; import '../Models/produit.dart'; +import 'package:mobile_scanner/mobile_scanner.dart'; class DemandeSortiePersonnellePage extends StatefulWidget { const DemandeSortiePersonnellePage({super.key}); @override - _DemandeSortiePersonnellePageState createState() => _DemandeSortiePersonnellePageState(); + _DemandeSortiePersonnellePageState createState() => + _DemandeSortiePersonnellePageState(); } -class _DemandeSortiePersonnellePageState extends State - with TickerProviderStateMixin { +class _DemandeSortiePersonnellePageState + extends State with TickerProviderStateMixin { final AppDatabase _database = AppDatabase.instance; final UserController _userController = Get.find(); - + final _formKey = GlobalKey(); final _quantiteController = TextEditingController(text: '1'); final _motifController = TextEditingController(); final _notesController = TextEditingController(); final _searchController = TextEditingController(); - + Product? _selectedProduct; List _products = []; List _filteredProducts = []; bool _isLoading = false; bool _isSearching = false; - + late AnimationController _animationController; late Animation _fadeAnimation; late Animation _slideAnimation; @@ -44,14 +46,66 @@ class _DemandeSortiePersonnellePageState extends State(begin: 0.0, end: 1.0).animate( CurvedAnimation(parent: _animationController, curve: Curves.easeInOut), ); - _slideAnimation = Tween(begin: const Offset(0, 0.3), end: Offset.zero).animate( + _slideAnimation = + Tween(begin: const Offset(0, 0.3), end: Offset.zero).animate( CurvedAnimation(parent: _animationController, curve: Curves.easeOutCubic), ); - + _loadProducts(); _searchController.addListener(_filterProducts); } + 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 + 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); + } 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; + }); + } + }, + ), + ), + ); + }, + ); + } + void _filterProducts() { final query = _searchController.text.toLowerCase(); setState(() { @@ -62,7 +116,7 @@ class _DemandeSortiePersonnellePageState extends State 0 ? _userController.pointDeVenteId : null, - notes: _notesController.text.trim().isNotEmpty ? _notesController.text.trim() : null, + pointDeVenteId: _userController.pointDeVenteId > 0 + ? _userController.pointDeVenteId + : null, + notes: _notesController.text.trim().isNotEmpty + ? _notesController.text.trim() + : null, ); - _showSuccessSnackbar('Votre demande de sortie personnelle a été soumise pour approbation'); + _showSuccessSnackbar( + 'Votre demande de sortie personnelle a été soumise pour approbation'); // Réinitialiser le formulaire avec animation _resetForm(); @@ -144,55 +204,58 @@ class _DemandeSortiePersonnellePageState extends State _showConfirmationDialog() async { return await showDialog( - context: context, - builder: (context) => AlertDialog( - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), - title: Row( - children: [ - Icon(Icons.help_outline, color: Colors.orange.shade700), - const SizedBox(width: 8), - const Text('Confirmer la demande'), - ], - ), - content: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text('Êtes-vous sûr de vouloir soumettre cette demande ?'), - const SizedBox(height: 16), - Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: Colors.grey.shade50, - borderRadius: BorderRadius.circular(8), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Produit: ${_selectedProduct?.name}'), - Text('Quantité: ${_quantiteController.text}'), - Text('Motif: ${_motifController.text}'), - ], - ), + context: context, + builder: (context) => AlertDialog( + shape: + RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), + title: Row( + children: [ + Icon(Icons.help_outline, color: Colors.orange.shade700), + const SizedBox(width: 8), + const Text('Confirmer la demande'), + ], ), - ], - ), - actions: [ - TextButton( - onPressed: () => Navigator.of(context).pop(false), - child: const Text('Annuler'), - ), - ElevatedButton( - onPressed: () => Navigator.of(context).pop(true), - style: ElevatedButton.styleFrom( - backgroundColor: Colors.orange.shade700, - foregroundColor: Colors.white, + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Êtes-vous sûr de vouloir soumettre cette demande ?'), + const SizedBox(height: 16), + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.grey.shade50, + borderRadius: BorderRadius.circular(8), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Produit: ${_selectedProduct?.name}'), + Text('Quantité: ${_quantiteController.text}'), + Text('Motif: ${_motifController.text}'), + ], + ), + ), + ], ), - child: const Text('Confirmer'), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(false), + child: const Text('Annuler'), + ), + ElevatedButton( + onPressed: () => Navigator.of(context).pop(true), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.orange.shade700, + foregroundColor: Colors.white, + ), + child: const Text('Confirmer'), + ), + ], ), - ], - ), - ) ?? false; + ) ?? + false; } void _showSuccessSnackbar(String message) { @@ -203,7 +266,9 @@ class _DemandeSortiePersonnellePageState extends State (_selectedProduct!.stock ?? 0)) { + if (_selectedProduct != null && + quantite > (_selectedProduct!.stock ?? 0)) { return 'Quantité supérieure au stock disponible'; } return null; @@ -538,7 +620,8 @@ class _DemandeSortiePersonnellePageState extends State 0) - _buildInfoRow(Icons.store, 'Point de vente', _userController.pointDeVenteDesignation), - _buildInfoRow(Icons.calendar_today, 'Date', DateTime.now().toLocal().toString().split(' ')[0]), + _buildInfoRow(Icons.store, 'Point de vente', + _userController.pointDeVenteDesignation), + _buildInfoRow(Icons.calendar_today, 'Date', + DateTime.now().toLocal().toString().split(' ')[0]), ], ), ); @@ -721,4 +807,8 @@ class _DemandeSortiePersonnellePageState extends State null; +}