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 {
final PaymentType type;
@ -1584,7 +1589,28 @@ class PaymentMethodDialog extends StatefulWidget {
class _PaymentMethodDialogState extends State<PaymentMethodDialog> {
PaymentType _selectedPayment = PaymentType.cash;
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
void initState() {
super.initState();
@ -1598,52 +1624,91 @@ class _PaymentMethodDialogState extends State<PaymentMethodDialog> {
super.dispose();
}
@override
Widget build(BuildContext context) {
Widget build(BuildContext context) {
final amount = double.tryParse(_amountController.text) ?? 0;
final change = amount - widget.commande.montantTotal;
return AlertDialog(
title: const Text('Méthode de paiement'),
content: Column(
title: const Text('Méthode de paiement', style: TextStyle(fontWeight: FontWeight.bold)),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
RadioListTile<PaymentType>(
title: const Text('Paiement en liquide'),
// Section Paiement mobile
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,
groupValue: _selectedPayment,
onChanged: (value) {
setState(() {
_selectedPayment = value!;
});
},
),
if (_selectedPayment == PaymentType.cash)
if (_selectedPayment == PaymentType.cash) ...[
const SizedBox(height: 12),
TextField(
controller: _amountController,
decoration: const InputDecoration(
labelText: 'Montant donné',
prefixText: 'MGA ',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
onChanged: (value) {
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!;
});
},
keyboardType: TextInputType.numberWithOptions(decimal: true),
onChanged: (value) => setState(() {}),
),
if (_selectedPayment == PaymentType.cash)
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
const SizedBox(height: 8),
Text(
'Monnaie à rendre: ${change.toStringAsFixed(2)} MGA',
style: TextStyle(
fontSize: 16,
@ -1651,41 +1716,97 @@ class _PaymentMethodDialogState extends State<PaymentMethodDialog> {
color: change >= 0 ? Colors.green : Colors.red,
),
),
),
],
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Annuler'),
child: const Text('Annuler', style: TextStyle(color: Colors.grey)),
),
ElevatedButton(
onPressed: () {
final amount = _selectedPayment == PaymentType.cash
? double.tryParse(_amountController.text) ?? 0
: widget.commande.montantTotal;
if (_selectedPayment == PaymentType.cash && amount < widget.commande.montantTotal) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Le montant donné est insuffisant'),
backgroundColor: Colors.red,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue.shade800,
foregroundColor: Colors.white,
),
onPressed: _validatePayment,
child: const Text('Confirmer'),
),
],
);
return;
}
}
Navigator.pop(
context,
PaymentMethod(
type: _selectedPayment,
amountGiven: amount,
Widget _buildMobileMoneyTile({
required String title,
required String imagePath,
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: 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() {
Get.dialog(
AlertDialog(
title: const Text('Informations Client'),
content: SingleChildScrollView(
title: Row(
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(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildTextFormField(
controller: _nomController,
@ -311,6 +328,7 @@ class _NouvelleCommandePageState extends State<NouvelleCommandePage> {
),
),
),
),
actions: [
TextButton(
onPressed: () => Get.back(),
@ -320,25 +338,21 @@ class _NouvelleCommandePageState extends State<NouvelleCommandePage> {
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue.shade800,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: () {
if (_formKey.currentState!.validate()) {
Get.back();
Get.snackbar(
'Succès',
'Informations client enregistrées',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: Colors.green,
colorText: Colors.white,
);
// Au lieu d'afficher juste un message, on valide directement la commande
_submitOrder();
}
},
child: const Text('Enregistrer'),
child: const Text('Valider la commande'), // Changement de texte ici
),
],
),
);
}
}
Widget _buildTextFormField({
required TextEditingController controller,
@ -464,7 +478,7 @@ class _NouvelleCommandePageState extends State<NouvelleCommandePage> {
children: [
const SizedBox(height: 4),
Text(
'${product.price.toStringAsFixed(2)} DA',
'${product.price.toStringAsFixed(2)} MGA',
style: TextStyle(
color: Colors.green.shade700,
fontWeight: FontWeight.w600,
@ -626,9 +640,9 @@ class _NouvelleCommandePageState extends State<NouvelleCommandePage> {
child: const Icon(Icons.shopping_bag, size: 20),
),
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(
'${(entry.value * product.price).toStringAsFixed(2)} DA',
'${(entry.value * product.price).toStringAsFixed(2)} MGA',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.blue.shade800,
@ -658,7 +672,7 @@ class _NouvelleCommandePageState extends State<NouvelleCommandePage> {
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
Text(
'${total.toStringAsFixed(2)} DA',
'${total.toStringAsFixed(2)} MGA',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
@ -708,32 +722,34 @@ class _NouvelleCommandePageState extends State<NouvelleCommandePage> {
}
Future<void> _submitOrder() async {
if (_nomController.text.isEmpty ||
_prenomController.text.isEmpty ||
_emailController.text.isEmpty ||
_telephoneController.text.isEmpty ||
_adresseController.text.isEmpty) {
Get.back(); // Ferme le bottom sheet
// Vérifier d'abord si le panier est vide
final itemsInCart = _quantites.entries.where((e) => e.value > 0).toList();
if (itemsInCart.isEmpty) {
Get.snackbar(
'Informations manquantes',
'Veuillez remplir les informations client',
'Panier vide',
'Veuillez ajouter des produits à votre commande',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: Colors.red,
colorText: Colors.white,
);
_showClientFormDialog();
_showCartBottomSheet(); // Ouvrir le panier pour montrer qu'il est vide
return;
}
final itemsInCart = _quantites.entries.where((e) => e.value > 0).toList();
if (itemsInCart.isEmpty) {
// Ensuite vérifier les informations client
if (_nomController.text.isEmpty ||
_prenomController.text.isEmpty ||
_emailController.text.isEmpty ||
_telephoneController.text.isEmpty ||
_adresseController.text.isEmpty) {
Get.snackbar(
'Panier vide',
'Veuillez ajouter des produits à votre commande',
'Informations manquantes',
'Veuillez remplir les informations client',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: Colors.red,
colorText: Colors.white,
);
_showClientFormDialog();
return;
}
@ -781,14 +797,12 @@ class _NouvelleCommandePageState extends State<NouvelleCommandePage> {
try {
await _appDatabase.createCommandeComplete(client, commande, details);
Get.back(); // Ferme le bottom sheet
// Afficher le dialogue de confirmation
await showDialog(
context: context,
builder: (context) => AlertDialog(
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: [
TextButton(
onPressed: () {
@ -823,7 +837,7 @@ class _NouvelleCommandePageState extends State<NouvelleCommandePage> {
colorText: Colors.white,
);
}
}
}
@override
void dispose() {

3
pubspec.yaml

@ -102,6 +102,9 @@ flutter:
- assets/database/usersdb.db
- assets/database/work.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
# https://flutter.dev/assets-and-images/#resolution-aware

Loading…
Cancel
Save