Refactoring
This commit is contained in:
@@ -0,0 +1,697 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import '../../../../core/di/injection.dart';
|
||||
import '../../../../core/models/cotisation_model.dart';
|
||||
import '../../../../core/models/payment_model.dart';
|
||||
import '../../../../core/models/wave_checkout_session_model.dart';
|
||||
import '../../../../core/services/wave_payment_service.dart';
|
||||
import '../../../../shared/theme/app_theme.dart';
|
||||
import '../../../../shared/widgets/buttons/primary_button.dart';
|
||||
import '../../../../shared/widgets/common/unified_page_layout.dart';
|
||||
import '../bloc/cotisations_bloc.dart';
|
||||
import '../bloc/cotisations_event.dart';
|
||||
import '../bloc/cotisations_state.dart';
|
||||
|
||||
/// Page dédiée aux paiements Wave Money
|
||||
/// Interface moderne et sécurisée pour les paiements mobiles
|
||||
class WavePaymentPage extends StatefulWidget {
|
||||
final CotisationModel cotisation;
|
||||
|
||||
const WavePaymentPage({
|
||||
super.key,
|
||||
required this.cotisation,
|
||||
});
|
||||
|
||||
@override
|
||||
State<WavePaymentPage> createState() => _WavePaymentPageState();
|
||||
}
|
||||
|
||||
class _WavePaymentPageState extends State<WavePaymentPage>
|
||||
with TickerProviderStateMixin {
|
||||
late CotisationsBloc _cotisationsBloc;
|
||||
late WavePaymentService _wavePaymentService;
|
||||
late AnimationController _animationController;
|
||||
late AnimationController _pulseController;
|
||||
late Animation<double> _fadeAnimation;
|
||||
late Animation<double> _slideAnimation;
|
||||
late Animation<double> _pulseAnimation;
|
||||
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _phoneController = TextEditingController();
|
||||
final _nameController = TextEditingController();
|
||||
final _emailController = TextEditingController();
|
||||
|
||||
bool _isProcessing = false;
|
||||
bool _termsAccepted = false;
|
||||
WaveCheckoutSessionModel? _currentSession;
|
||||
String? _paymentUrl;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_cotisationsBloc = getIt<CotisationsBloc>();
|
||||
_wavePaymentService = getIt<WavePaymentService>();
|
||||
|
||||
// Animations
|
||||
_animationController = AnimationController(
|
||||
duration: const Duration(milliseconds: 800),
|
||||
vsync: this,
|
||||
);
|
||||
_pulseController = AnimationController(
|
||||
duration: const Duration(milliseconds: 1500),
|
||||
vsync: this,
|
||||
);
|
||||
|
||||
_fadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
|
||||
CurvedAnimation(parent: _animationController, curve: Curves.easeOut),
|
||||
);
|
||||
_slideAnimation = Tween<double>(begin: 50.0, end: 0.0).animate(
|
||||
CurvedAnimation(parent: _animationController, curve: Curves.easeOutCubic),
|
||||
);
|
||||
_pulseAnimation = Tween<double>(begin: 1.0, end: 1.1).animate(
|
||||
CurvedAnimation(parent: _pulseController, curve: Curves.easeInOut),
|
||||
);
|
||||
|
||||
_animationController.forward();
|
||||
_pulseController.repeat(reverse: true);
|
||||
|
||||
// Pré-remplir les champs si disponible
|
||||
_nameController.text = widget.cotisation.nomMembre;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_phoneController.dispose();
|
||||
_nameController.dispose();
|
||||
_emailController.dispose();
|
||||
_animationController.dispose();
|
||||
_pulseController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider.value(
|
||||
value: _cotisationsBloc,
|
||||
child: UnifiedPageLayout(
|
||||
title: 'Paiement Wave Money',
|
||||
subtitle: 'Paiement sécurisé et instantané',
|
||||
showBackButton: true,
|
||||
backgroundColor: AppTheme.backgroundLight,
|
||||
child: BlocConsumer<CotisationsBloc, CotisationsState>(
|
||||
listener: _handleBlocState,
|
||||
builder: (context, state) {
|
||||
return FadeTransition(
|
||||
opacity: _fadeAnimation,
|
||||
child: Transform.translate(
|
||||
offset: Offset(0, _slideAnimation.value),
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildWaveHeader(),
|
||||
const SizedBox(height: 24),
|
||||
_buildCotisationSummary(),
|
||||
const SizedBox(height: 24),
|
||||
_buildPaymentForm(),
|
||||
const SizedBox(height: 24),
|
||||
_buildSecurityInfo(),
|
||||
const SizedBox(height: 24),
|
||||
_buildPaymentButton(state),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildWaveHeader() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
gradient: const LinearGradient(
|
||||
colors: [Color(0xFF00D4FF), Color(0xFF0099CC)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: const Color(0xFF00D4FF).withOpacity(0.3),
|
||||
blurRadius: 12,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
ScaleTransition(
|
||||
scale: _pulseAnimation,
|
||||
child: Container(
|
||||
width: 60,
|
||||
height: 60,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.waves,
|
||||
size: 32,
|
||||
color: Color(0xFF00D4FF),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Wave Money',
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
const Text(
|
||||
'Paiement mobile sécurisé',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.white70,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.2),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: const Text(
|
||||
'🇨🇮 Côte d\'Ivoire',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCotisationSummary() {
|
||||
final remainingAmount = widget.cotisation.montantDu - widget.cotisation.montantPaye;
|
||||
final fees = _wavePaymentService.calculateWaveFees(remainingAmount);
|
||||
final total = remainingAmount + fees;
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: AppTheme.borderLight),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Résumé de la cotisation',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppTheme.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildSummaryRow('Type', widget.cotisation.typeCotisation),
|
||||
_buildSummaryRow('Membre', widget.cotisation.nomMembre),
|
||||
_buildSummaryRow('Référence', widget.cotisation.numeroReference),
|
||||
const Divider(height: 24),
|
||||
_buildSummaryRow('Montant', '${remainingAmount.toStringAsFixed(0)} XOF'),
|
||||
_buildSummaryRow('Frais Wave', '${fees.toStringAsFixed(0)} XOF'),
|
||||
const Divider(height: 24),
|
||||
_buildSummaryRow(
|
||||
'Total à payer',
|
||||
'${total.toStringAsFixed(0)} XOF',
|
||||
isTotal: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSummaryRow(String label, String value, {bool isTotal = false}) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: isTotal ? 16 : 14,
|
||||
fontWeight: isTotal ? FontWeight.bold : FontWeight.normal,
|
||||
color: AppTheme.textSecondary,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
value,
|
||||
style: TextStyle(
|
||||
fontSize: isTotal ? 16 : 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: isTotal ? AppTheme.primaryColor : AppTheme.textPrimary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPaymentForm() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: AppTheme.borderLight),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Informations de paiement',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppTheme.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildPhoneField(),
|
||||
const SizedBox(height: 16),
|
||||
_buildNameField(),
|
||||
const SizedBox(height: 16),
|
||||
_buildEmailField(),
|
||||
const SizedBox(height: 16),
|
||||
_buildTermsCheckbox(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPhoneField() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Numéro Wave Money *',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppTheme.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
TextFormField(
|
||||
controller: _phoneController,
|
||||
keyboardType: TextInputType.phone,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
LengthLimitingTextInputFormatter(10),
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
hintText: '77 123 45 67',
|
||||
prefixIcon: const Icon(Icons.phone_android, color: Color(0xFF00D4FF)),
|
||||
prefixText: '+225 ',
|
||||
prefixStyle: const TextStyle(
|
||||
color: AppTheme.textSecondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: const BorderSide(color: AppTheme.borderLight),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: const BorderSide(color: Color(0xFF00D4FF), width: 2),
|
||||
),
|
||||
filled: true,
|
||||
fillColor: AppTheme.backgroundLight,
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Veuillez saisir votre numéro Wave Money';
|
||||
}
|
||||
if (value.length < 8) {
|
||||
return 'Numéro invalide (minimum 8 chiffres)';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildNameField() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Nom complet *',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppTheme.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
TextFormField(
|
||||
controller: _nameController,
|
||||
textCapitalization: TextCapitalization.words,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Votre nom complet',
|
||||
prefixIcon: const Icon(Icons.person, color: Color(0xFF00D4FF)),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: const BorderSide(color: AppTheme.borderLight),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: const BorderSide(color: Color(0xFF00D4FF), width: 2),
|
||||
),
|
||||
filled: true,
|
||||
fillColor: AppTheme.backgroundLight,
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.trim().isEmpty) {
|
||||
return 'Veuillez saisir votre nom complet';
|
||||
}
|
||||
if (value.trim().length < 2) {
|
||||
return 'Le nom doit contenir au moins 2 caractères';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEmailField() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Email (optionnel)',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppTheme.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
TextFormField(
|
||||
controller: _emailController,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'votre.email@exemple.com',
|
||||
prefixIcon: const Icon(Icons.email, color: Color(0xFF00D4FF)),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: const BorderSide(color: AppTheme.borderLight),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: const BorderSide(color: Color(0xFF00D4FF), width: 2),
|
||||
),
|
||||
filled: true,
|
||||
fillColor: AppTheme.backgroundLight,
|
||||
),
|
||||
validator: (value) {
|
||||
if (value != null && value.isNotEmpty) {
|
||||
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) {
|
||||
return 'Format d\'email invalide';
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTermsCheckbox() {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Checkbox(
|
||||
value: _termsAccepted,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_termsAccepted = value ?? false;
|
||||
});
|
||||
},
|
||||
activeColor: const Color(0xFF00D4FF),
|
||||
),
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_termsAccepted = !_termsAccepted;
|
||||
});
|
||||
},
|
||||
child: const Text(
|
||||
'J\'accepte les conditions d\'utilisation de Wave Money et autorise le prélèvement du montant indiqué.',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppTheme.textSecondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSecurityInfo() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF0F9FF),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: const Color(0xFF00D4FF).withOpacity(0.2)),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF00D4FF).withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.security,
|
||||
color: Color(0xFF00D4FF),
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const Expanded(
|
||||
child: Text(
|
||||
'Paiement 100% sécurisé',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppTheme.textPrimary,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
const Text(
|
||||
'• Chiffrement SSL/TLS de bout en bout\n'
|
||||
'• Conformité aux standards PCI DSS\n'
|
||||
'• Aucune donnée bancaire stockée\n'
|
||||
'• Transaction instantanée et traçable',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppTheme.textSecondary,
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPaymentButton(CotisationsState state) {
|
||||
final isLoading = state is PaymentInProgress || _isProcessing;
|
||||
final canPay = _formKey.currentState?.validate() == true &&
|
||||
_termsAccepted &&
|
||||
_phoneController.text.isNotEmpty &&
|
||||
!isLoading;
|
||||
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: PrimaryButton(
|
||||
text: isLoading
|
||||
? 'Traitement en cours...'
|
||||
: 'Payer avec Wave Money',
|
||||
icon: isLoading ? null : Icons.waves,
|
||||
onPressed: canPay ? _processWavePayment : null,
|
||||
isLoading: isLoading,
|
||||
backgroundColor: const Color(0xFF00D4FF),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleBlocState(BuildContext context, CotisationsState state) {
|
||||
if (state is PaymentSuccess) {
|
||||
_showPaymentSuccessDialog(state.payment);
|
||||
} else if (state is PaymentFailure) {
|
||||
_showPaymentErrorDialog(state.errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
void _processWavePayment() async {
|
||||
if (!_formKey.currentState!.validate() || !_termsAccepted) {
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_isProcessing = true;
|
||||
});
|
||||
|
||||
try {
|
||||
final remainingAmount = widget.cotisation.montantDu - widget.cotisation.montantPaye;
|
||||
|
||||
// Initier le paiement Wave via le BLoC
|
||||
_cotisationsBloc.add(InitiatePayment(
|
||||
cotisationId: widget.cotisation.id,
|
||||
montant: remainingAmount,
|
||||
methodePaiement: 'WAVE',
|
||||
numeroTelephone: _phoneController.text.trim(),
|
||||
nomPayeur: _nameController.text.trim(),
|
||||
emailPayeur: _emailController.text.trim().isEmpty
|
||||
? null
|
||||
: _emailController.text.trim(),
|
||||
));
|
||||
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
_isProcessing = false;
|
||||
});
|
||||
_showPaymentErrorDialog('Erreur lors de l\'initiation du paiement: $e');
|
||||
}
|
||||
}
|
||||
|
||||
void _showPaymentSuccessDialog(PaymentModel payment) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Row(
|
||||
children: [
|
||||
Icon(Icons.check_circle, color: AppTheme.successColor, size: 28),
|
||||
SizedBox(width: 8),
|
||||
Text('Paiement réussi !'),
|
||||
],
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Votre paiement de ${payment.montant.toStringAsFixed(0)} XOF a été confirmé.'),
|
||||
const SizedBox(height: 12),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppTheme.backgroundLight,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Référence: ${payment.numeroReference}'),
|
||||
Text('Transaction: ${payment.numeroTransaction ?? 'N/A'}'),
|
||||
Text('Date: ${DateTime.now().toString().substring(0, 16)}'),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(context).pop(); // Retour à la liste
|
||||
},
|
||||
child: const Text('Fermer'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showPaymentErrorDialog(String errorMessage) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Row(
|
||||
children: [
|
||||
Icon(Icons.error, color: AppTheme.errorColor, size: 28),
|
||||
SizedBox(width: 8),
|
||||
Text('Erreur de paiement'),
|
||||
],
|
||||
),
|
||||
content: Text(errorMessage),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('OK'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user