// widgets/pin_verification_dialog.dart - Version mise à jour import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; class PinVerificationDialog extends StatefulWidget { final String agentName; final String serviceName; final double baseAmount; final double fees; final double totalAmount; final double agentBalance; // Nouveau paramètre final Function(String) onPinConfirmed; final VoidCallback onCancel; const PinVerificationDialog({ super.key, required this.agentName, required this.serviceName, required this.baseAmount, required this.fees, required this.totalAmount, required this.agentBalance, // Nouveau paramètre requis required this.onPinConfirmed, required this.onCancel, }); @override _PinVerificationDialogState createState() => _PinVerificationDialogState(); } class _PinVerificationDialogState extends State { final TextEditingController _pinController = TextEditingController(); bool _isObscured = true; bool _isLoading = false; String? _errorMessage; // Formatter pour les montants String _formatAmount(double amount) { final formatter = NumberFormat('#,###', 'fr_FR'); return formatter.format(amount); } // Vérifier si l'agent a suffisamment de solde bool get _hasSufficientBalance { return widget.agentBalance >= widget.totalAmount; } @override void dispose() { _pinController.dispose(); super.dispose(); } Future _handlePinConfirmation() async { if (_pinController.text.length < 4) { setState(() { _errorMessage = 'Le PIN doit contenir au moins 4 chiffres'; }); return; } if (!_hasSufficientBalance) { setState(() { _errorMessage = 'Solde insuffisant pour effectuer cette transaction'; }); return; } setState(() { _isLoading = true; _errorMessage = null; }); try { // Simuler une vérification (remplacez par votre logique de vérification) await Future.delayed(const Duration(seconds: 1)); widget.onPinConfirmed(_pinController.text); } catch (e) { setState(() { _errorMessage = 'Erreur lors de la vérification du PIN'; _isLoading = false; }); } } @override Widget build(BuildContext context) { final size = MediaQuery.of(context).size; final isSmallScreen = size.width < 360; return Dialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), elevation: 0, backgroundColor: Colors.transparent, child: Container( constraints: BoxConstraints( maxWidth: size.width * 0.9, maxHeight: size.height * 0.8, ), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), spreadRadius: 0, blurRadius: 10, offset: const Offset(0, 4), ), ], ), child: SingleChildScrollView( child: Padding( padding: EdgeInsets.all(isSmallScreen ? 16 : 24), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ // En-tête avec icône de sécurité Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF006699).withOpacity(0.1), shape: BoxShape.circle, ), child: const Icon( Icons.security, size: 32, color: Color(0xFF006699), ), ), const SizedBox(height: 16), // Titre Text( 'Confirmation sécurisée', style: TextStyle( fontSize: isSmallScreen ? 20 : 24, fontWeight: FontWeight.bold, color: Colors.black87, ), textAlign: TextAlign.center, ), const SizedBox(height: 8), Text( 'Vérifiez les détails et confirmez avec votre PIN', style: TextStyle( fontSize: isSmallScreen ? 14 : 16, color: Colors.black54, ), textAlign: TextAlign.center, ), const SizedBox(height: 24), // Informations de la transaction Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.grey[50], borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey[200]!), ), child: Column( children: [ // Service _buildDetailRow( icon: Icons.business_center, label: 'Service', value: widget.serviceName, isSmallScreen: isSmallScreen, ), if (widget.baseAmount > 0) ...[ const SizedBox(height: 12), _buildDetailRow( icon: Icons.account_balance_wallet, label: 'Montant à payer', value: '${_formatAmount(widget.baseAmount)} FCFA', isSmallScreen: isSmallScreen, ), ], if (widget.fees > 0) ...[ const SizedBox(height: 12), _buildDetailRow( icon: Icons.receipt_long, label: 'Frais', value: '${_formatAmount(widget.fees)} FCFA', isSmallScreen: isSmallScreen, ), ], const SizedBox(height: 12), Divider(color: Colors.grey[300]), const SizedBox(height: 12), // Total _buildDetailRow( icon: Icons.payments, label: 'Total', value: '${_formatAmount(widget.totalAmount)} FCFA', isTotal: true, isSmallScreen: isSmallScreen, ), const SizedBox(height: 16), // Solde agent avec indicateur de suffisance Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: _hasSufficientBalance ? Colors.green[50] : Colors.red[50], borderRadius: BorderRadius.circular(8), border: Border.all( color: _hasSufficientBalance ? Colors.green[200]! : Colors.red[200]!, ), ), child: Row( children: [ Icon( _hasSufficientBalance ? Icons.check_circle : Icons.warning, color: _hasSufficientBalance ? Colors.green[600] : Colors.red[600], size: 20, ), const SizedBox(width: 8), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Votre solde', style: TextStyle( fontSize: 12, color: Colors.grey[600], fontWeight: FontWeight.w500, ), ), Text( '${_formatAmount(widget.agentBalance)} FCFA', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: _hasSufficientBalance ? Colors.green[700] : Colors.red[700], ), ), if (!_hasSufficientBalance) Text( 'Solde insuffisant', style: TextStyle( fontSize: 12, color: Colors.red[600], fontStyle: FontStyle.italic, ), ), ], ), ), ], ), ), ], ), ), const SizedBox(height: 24), // Champ PIN Text( 'PIN de sécurité', style: TextStyle( fontSize: isSmallScreen ? 14 : 16, fontWeight: FontWeight.w600, color: Colors.black87, ), ), const SizedBox(height: 8), TextField( controller: _pinController, keyboardType: TextInputType.number, obscureText: _isObscured, maxLength: 6, enabled: !_isLoading && _hasSufficientBalance, decoration: InputDecoration( hintText: 'Saisissez votre PIN', prefixIcon: const Icon( Icons.lock, color: Color(0xFF006699), ), suffixIcon: IconButton( icon: Icon( _isObscured ? Icons.visibility : Icons.visibility_off, color: Colors.grey, ), onPressed: () { setState(() { _isObscured = !_isObscured; }); }, ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: Color(0xFFE0E7FF)), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: Color(0xFFE0E7FF)), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide( color: Color(0xFF006699), width: 2, ), ), counterText: '', ), onSubmitted: _hasSufficientBalance ? (_) => _handlePinConfirmation() : null, ), if (_errorMessage != null) ...[ const SizedBox(height: 8), Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Colors.red[50], borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.red[200]!), ), child: Row( children: [ Icon( Icons.error_outline, color: Colors.red[600], size: 16, ), const SizedBox(width: 8), Expanded( child: Text( _errorMessage!, style: TextStyle( color: Colors.red[600], fontSize: 12, ), ), ), ], ), ), ], const SizedBox(height: 24), // Boutons Row( children: [ Expanded( child: TextButton( onPressed: _isLoading ? null : widget.onCancel, style: TextButton.styleFrom( padding: EdgeInsets.symmetric( vertical: isSmallScreen ? 12 : 16, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), child: Text( 'Annuler', style: TextStyle( fontSize: isSmallScreen ? 14 : 16, color: Colors.grey[600], ), ), ), ), const SizedBox(width: 12), Expanded( child: ElevatedButton( onPressed: (_isLoading || !_hasSufficientBalance) ? null : _handlePinConfirmation, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF006699), foregroundColor: Colors.white, padding: EdgeInsets.symmetric( vertical: isSmallScreen ? 12 : 16, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), elevation: 0, ), child: _isLoading ? SizedBox( height: isSmallScreen ? 16 : 20, width: isSmallScreen ? 16 : 20, child: const CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation( Colors.white, ), ), ) : Text( 'CONFIRMER', style: TextStyle( fontSize: isSmallScreen ? 14 : 16, fontWeight: FontWeight.w600, ), ), ), ), ], ), ], ), ), ), ), ); } Widget _buildDetailRow({ required IconData icon, required String label, required String value, bool isTotal = false, required bool isSmallScreen, }) { return Row( children: [ Icon( icon, size: isSmallScreen ? 16 : 18, color: isTotal ? const Color(0xFF006699) : Colors.grey[600], ), const SizedBox(width: 8), Expanded( child: Text( label, style: TextStyle( fontSize: isSmallScreen ? 13 : 14, fontWeight: isTotal ? FontWeight.w600 : FontWeight.w500, color: isTotal ? const Color(0xFF006699) : Colors.grey[700], ), ), ), Text( value, style: TextStyle( fontSize: isSmallScreen ? 13 : 14, fontWeight: isTotal ? FontWeight.bold : FontWeight.w600, color: isTotal ? const Color(0xFF006699) : Colors.black87, ), ), ], ); } }