Browse Source

commit fonctionnalite impec

31052025_02
b.razafimandimbihery 6 months ago
parent
commit
9eafda610f
  1. BIN
      assets/Orange_money.png
  2. BIN
      assets/airtel_money.png
  3. BIN
      assets/mvola.jpg
  4. 7
      lib/Components/paymentType.dart
  5. 227
      lib/Views/commandManagement.dart
  6. 78
      lib/Views/mobilepage.dart
  7. 3
      pubspec.yaml

BIN
assets/Orange_money.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
assets/airtel_money.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
assets/mvola.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

7
lib/Components/paymentType.dart

@ -0,0 +1,7 @@
enum PaymentType {
cash,
card,
mvola,
orange,
airtel
}

227
lib/Views/commandManagement.dart

@ -1563,7 +1563,12 @@ class _CommandeActions extends StatelessWidget {
} }
} }
enum PaymentType { cash, card } enum PaymentType { cash, card ,
mvola,
orange,
airtel
}
class PaymentMethod { class PaymentMethod {
final PaymentType type; final PaymentType type;
@ -1584,7 +1589,28 @@ class PaymentMethodDialog extends StatefulWidget {
class _PaymentMethodDialogState extends State<PaymentMethodDialog> { class _PaymentMethodDialogState extends State<PaymentMethodDialog> {
PaymentType _selectedPayment = PaymentType.cash; PaymentType _selectedPayment = PaymentType.cash;
final _amountController = TextEditingController(); final _amountController = TextEditingController();
void _validatePayment() {
if (_selectedPayment == PaymentType.cash) {
final amountGiven = double.tryParse(_amountController.text) ?? 0;
if (amountGiven < widget.commande.montantTotal) {
Get.snackbar(
'Erreur',
'Le montant donné est insuffisant',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: Colors.red,
colorText: Colors.white,
);
return;
}
}
Navigator.pop(context, PaymentMethod(
type: _selectedPayment,
amountGiven: _selectedPayment == PaymentType.cash
? double.parse(_amountController.text)
: widget.commande.montantTotal,
));
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -1598,52 +1624,91 @@ class _PaymentMethodDialogState extends State<PaymentMethodDialog> {
super.dispose(); super.dispose();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final amount = double.tryParse(_amountController.text) ?? 0; final amount = double.tryParse(_amountController.text) ?? 0;
final change = amount - widget.commande.montantTotal; final change = amount - widget.commande.montantTotal;
return AlertDialog( return AlertDialog(
title: const Text('Méthode de paiement'), title: const Text('Méthode de paiement', style: TextStyle(fontWeight: FontWeight.bold)),
content: Column( content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
RadioListTile<PaymentType>( // Section Paiement mobile
title: const Text('Paiement en liquide'), const Align(
alignment: Alignment.centerLeft,
child: Text('Mobile Money', style: TextStyle(fontWeight: FontWeight.w500)),
),
const SizedBox(height: 8),
Row(
children: [
Expanded(
child: _buildMobileMoneyTile(
title: 'Mvola',
imagePath: 'assets/mvola.jpg',
value: PaymentType.mvola,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildMobileMoneyTile(
title: 'Orange Money',
imagePath: 'assets/Orange_money.png',
value: PaymentType.orange,
),
),
const SizedBox(width: 8),
Expanded(
child: _buildMobileMoneyTile(
title: 'Airtel Money',
imagePath: 'assets/airtel_money.png',
value: PaymentType.airtel,
),
),
],
),
const SizedBox(height: 16),
// Section Carte bancaire
const Align(
alignment: Alignment.centerLeft,
child: Text('Carte Bancaire', style: TextStyle(fontWeight: FontWeight.w500)),
),
const SizedBox(height: 8),
_buildPaymentMethodTile(
title: 'Carte bancaire',
icon: Icons.credit_card,
value: PaymentType.card,
),
const SizedBox(height: 16),
// Section Paiement en liquide
const Align(
alignment: Alignment.centerLeft,
child: Text('Espèces', style: TextStyle(fontWeight: FontWeight.w500)),
),
const SizedBox(height: 8),
_buildPaymentMethodTile(
title: 'Paiement en liquide',
icon: Icons.money,
value: PaymentType.cash, value: PaymentType.cash,
groupValue: _selectedPayment,
onChanged: (value) {
setState(() {
_selectedPayment = value!;
});
},
), ),
if (_selectedPayment == PaymentType.cash) if (_selectedPayment == PaymentType.cash) ...[
const SizedBox(height: 12),
TextField( TextField(
controller: _amountController, controller: _amountController,
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: 'Montant donné', labelText: 'Montant donné',
prefixText: 'MGA ', prefixText: 'MGA ',
border: OutlineInputBorder(),
), ),
keyboardType: TextInputType.number, keyboardType: TextInputType.numberWithOptions(decimal: true),
onChanged: (value) { onChanged: (value) => setState(() {}),
setState(() {}); // Rafraîchir l'UI pour calculer la monnaie
},
),
RadioListTile<PaymentType>(
title: const Text('Carte bancaire'),
value: PaymentType.card,
groupValue: _selectedPayment,
onChanged: (value) {
setState(() {
_selectedPayment = value!;
});
},
), ),
if (_selectedPayment == PaymentType.cash) const SizedBox(height: 8),
Padding( Text(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
'Monnaie à rendre: ${change.toStringAsFixed(2)} MGA', 'Monnaie à rendre: ${change.toStringAsFixed(2)} MGA',
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
@ -1651,41 +1716,97 @@ class _PaymentMethodDialogState extends State<PaymentMethodDialog> {
color: change >= 0 ? Colors.green : Colors.red, color: change >= 0 ? Colors.green : Colors.red,
), ),
), ),
),
], ],
],
),
), ),
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context),
child: const Text('Annuler'), child: const Text('Annuler', style: TextStyle(color: Colors.grey)),
), ),
ElevatedButton( ElevatedButton(
onPressed: () { style: ElevatedButton.styleFrom(
final amount = _selectedPayment == PaymentType.cash backgroundColor: Colors.blue.shade800,
? double.tryParse(_amountController.text) ?? 0 foregroundColor: Colors.white,
: widget.commande.montantTotal; ),
onPressed: _validatePayment,
if (_selectedPayment == PaymentType.cash && amount < widget.commande.montantTotal) { child: const Text('Confirmer'),
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Le montant donné est insuffisant'),
backgroundColor: Colors.red,
), ),
],
); );
return; }
}
Navigator.pop( Widget _buildMobileMoneyTile({
context, required String title,
PaymentMethod( required String imagePath,
type: _selectedPayment, required PaymentType value,
amountGiven: amount, }) {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(
color: _selectedPayment == value ? Colors.blue : Colors.grey.withOpacity(0.2),
width: 2,
),
),
child: InkWell(
borderRadius: BorderRadius.circular(8),
onTap: () => setState(() => _selectedPayment = value),
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
children: [
Image.asset(
imagePath,
height: 30,
width: 30,
fit: BoxFit.contain,
errorBuilder: (context, error, stackTrace) =>
const Icon(Icons.mobile_friendly, size: 30),
),
const SizedBox(height: 8),
Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 12),
),
],
),
),
), ),
); );
}, }
child: const Text('Confirmer'),
Widget _buildPaymentMethodTile({
required String title,
required IconData icon,
required PaymentType value,
}) {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(
color: _selectedPayment == value ? Colors.blue : Colors.grey.withOpacity(0.2),
width: 2,
),
), ),
child: InkWell(
borderRadius: BorderRadius.circular(8),
onTap: () => setState(() => _selectedPayment = value),
child: Padding(
padding: const EdgeInsets.all(12),
child: Row(
children: [
Icon(icon, size: 24),
const SizedBox(width: 12),
Text(title),
], ],
),
),
),
); );
} }
} }

78
lib/Views/mobilepage.dart

@ -260,12 +260,29 @@ class _NouvelleCommandePageState extends State<NouvelleCommandePage> {
void _showClientFormDialog() { void _showClientFormDialog() {
Get.dialog( Get.dialog(
AlertDialog( AlertDialog(
title: const Text('Informations Client'), title: Row(
content: SingleChildScrollView( children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.blue.shade100,
borderRadius: BorderRadius.circular(8),
),
child: Icon(Icons.person_add, color: Colors.blue.shade700),
),
const SizedBox(width: 12),
const Text('Informations Client'),
],
),
content: Container(
width: 600,
constraints: const BoxConstraints(maxHeight: 600),
child: SingleChildScrollView(
child: Form( child: Form(
key: _formKey, key: _formKey,
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_buildTextFormField( _buildTextFormField(
controller: _nomController, controller: _nomController,
@ -311,6 +328,7 @@ class _NouvelleCommandePageState extends State<NouvelleCommandePage> {
), ),
), ),
), ),
),
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Get.back(), onPressed: () => Get.back(),
@ -320,25 +338,21 @@ class _NouvelleCommandePageState extends State<NouvelleCommandePage> {
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue.shade800, backgroundColor: Colors.blue.shade800,
foregroundColor: Colors.white, foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
), ),
onPressed: () { onPressed: () {
if (_formKey.currentState!.validate()) { if (_formKey.currentState!.validate()) {
Get.back(); Get.back();
Get.snackbar( // Au lieu d'afficher juste un message, on valide directement la commande
'Succès', _submitOrder();
'Informations client enregistrées',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: Colors.green,
colorText: Colors.white,
);
} }
}, },
child: const Text('Enregistrer'), child: const Text('Valider la commande'), // Changement de texte ici
), ),
], ],
), ),
); );
} }
Widget _buildTextFormField({ Widget _buildTextFormField({
required TextEditingController controller, required TextEditingController controller,
@ -464,7 +478,7 @@ class _NouvelleCommandePageState extends State<NouvelleCommandePage> {
children: [ children: [
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
'${product.price.toStringAsFixed(2)} DA', '${product.price.toStringAsFixed(2)} MGA',
style: TextStyle( style: TextStyle(
color: Colors.green.shade700, color: Colors.green.shade700,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -626,9 +640,9 @@ class _NouvelleCommandePageState extends State<NouvelleCommandePage> {
child: const Icon(Icons.shopping_bag, size: 20), child: const Icon(Icons.shopping_bag, size: 20),
), ),
title: Text(product.name), title: Text(product.name),
subtitle: Text('${entry.value} x ${product.price.toStringAsFixed(2)} DA'), subtitle: Text('${entry.value} x ${product.price.toStringAsFixed(2)} MGA'),
trailing: Text( trailing: Text(
'${(entry.value * product.price).toStringAsFixed(2)} DA', '${(entry.value * product.price).toStringAsFixed(2)} MGA',
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Colors.blue.shade800, color: Colors.blue.shade800,
@ -658,7 +672,7 @@ class _NouvelleCommandePageState extends State<NouvelleCommandePage> {
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
), ),
Text( Text(
'${total.toStringAsFixed(2)} DA', '${total.toStringAsFixed(2)} MGA',
style: const TextStyle( style: const TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -708,32 +722,34 @@ class _NouvelleCommandePageState extends State<NouvelleCommandePage> {
} }
Future<void> _submitOrder() async { Future<void> _submitOrder() async {
if (_nomController.text.isEmpty || // Vérifier d'abord si le panier est vide
_prenomController.text.isEmpty || final itemsInCart = _quantites.entries.where((e) => e.value > 0).toList();
_emailController.text.isEmpty || if (itemsInCart.isEmpty) {
_telephoneController.text.isEmpty ||
_adresseController.text.isEmpty) {
Get.back(); // Ferme le bottom sheet
Get.snackbar( Get.snackbar(
'Informations manquantes', 'Panier vide',
'Veuillez remplir les informations client', 'Veuillez ajouter des produits à votre commande',
snackPosition: SnackPosition.BOTTOM, snackPosition: SnackPosition.BOTTOM,
backgroundColor: Colors.red, backgroundColor: Colors.red,
colorText: Colors.white, colorText: Colors.white,
); );
_showClientFormDialog(); _showCartBottomSheet(); // Ouvrir le panier pour montrer qu'il est vide
return; return;
} }
final itemsInCart = _quantites.entries.where((e) => e.value > 0).toList(); // Ensuite vérifier les informations client
if (itemsInCart.isEmpty) { if (_nomController.text.isEmpty ||
_prenomController.text.isEmpty ||
_emailController.text.isEmpty ||
_telephoneController.text.isEmpty ||
_adresseController.text.isEmpty) {
Get.snackbar( Get.snackbar(
'Panier vide', 'Informations manquantes',
'Veuillez ajouter des produits à votre commande', 'Veuillez remplir les informations client',
snackPosition: SnackPosition.BOTTOM, snackPosition: SnackPosition.BOTTOM,
backgroundColor: Colors.red, backgroundColor: Colors.red,
colorText: Colors.white, colorText: Colors.white,
); );
_showClientFormDialog();
return; return;
} }
@ -781,14 +797,12 @@ class _NouvelleCommandePageState extends State<NouvelleCommandePage> {
try { try {
await _appDatabase.createCommandeComplete(client, commande, details); await _appDatabase.createCommandeComplete(client, commande, details);
Get.back(); // Ferme le bottom sheet
// Afficher le dialogue de confirmation // Afficher le dialogue de confirmation
await showDialog( await showDialog(
context: context, context: context,
builder: (context) => AlertDialog( builder: (context) => AlertDialog(
title: const Text('Commande Validée'), title: const Text('Commande Validée'),
content: const Text('Votre commande a été enregistrée avec succès.'), content: const Text('Votre commande a été enregistrée et expédiée avec succès.'),
actions: [ actions: [
TextButton( TextButton(
onPressed: () { onPressed: () {
@ -823,7 +837,7 @@ class _NouvelleCommandePageState extends State<NouvelleCommandePage> {
colorText: Colors.white, colorText: Colors.white,
); );
} }
} }
@override @override
void dispose() { void dispose() {

3
pubspec.yaml

@ -102,6 +102,9 @@ flutter:
- assets/database/usersdb.db - assets/database/usersdb.db
- assets/database/work.db - assets/database/work.db
- assets/database/roles.db - assets/database/roles.db
- assets/airtel_money.png
- assets/mvola.jpg
- assets/Orange_money.png
# An image asset can refer to one or more resolution-specific "variants", see # An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware # https://flutter.dev/assets-and-images/#resolution-aware

Loading…
Cancel
Save