Compare commits
No commits in common. "12f9fd063ab45c58ddc1e75b331cfc69623ced3f" and "f9b6be710402fa55eef9c021dcbe9538330b9706" have entirely different histories.
12f9fd063a
...
f9b6be7104
@ -53,7 +53,7 @@ class PanierPage extends StatelessWidget {
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text('${product.price.toStringAsFixed(2)} MGA'),
|
||||
Text('${product.price.toStringAsFixed(2)} fcfa'),
|
||||
const SizedBox(width: 8),
|
||||
Text('x $quantity'),
|
||||
const SizedBox(width: 8),
|
||||
@ -78,7 +78,7 @@ class PanierPage extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Total: ${calculateTotalPrice().toStringAsFixed(2)} MGA',
|
||||
'Total: ${calculateTotalPrice().toStringAsFixed(2)} fcfa',
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
|
||||
@ -13,6 +13,7 @@ import '../Components/app_bar.dart';
|
||||
import '../Models/produit.dart';
|
||||
import '../Services/productDatabase.dart';
|
||||
|
||||
|
||||
class ProductManagementPage extends StatefulWidget {
|
||||
const ProductManagementPage({super.key});
|
||||
|
||||
@ -31,13 +32,7 @@ class _ProductManagementPageState extends State<ProductManagementPage> {
|
||||
|
||||
// Catégories prédéfinies pour l'ajout de produits
|
||||
final List<String> _predefinedCategories = [
|
||||
'Sucré',
|
||||
'Salé',
|
||||
'Jus',
|
||||
'Gateaux',
|
||||
'Snacks',
|
||||
'Boissons',
|
||||
'Non catégorisé'
|
||||
'Sucré', 'Salé', 'Jus', 'Gateaux', 'Snacks', 'Boissons', 'Non catégorisé'
|
||||
];
|
||||
|
||||
@override
|
||||
@ -53,6 +48,12 @@ class _ProductManagementPageState extends State<ProductManagementPage> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//======================================================================================================
|
||||
// Ajoutez ces variables à la classe _ProductManagementPageState
|
||||
bool _isImporting = false;
|
||||
@ -60,7 +61,6 @@ class _ProductManagementPageState extends State<ProductManagementPage> {
|
||||
String _importStatusText = '';
|
||||
|
||||
// Ajoutez ces méthodes à la classe _ProductManagementPageState
|
||||
|
||||
void _resetImportState() {
|
||||
setState(() {
|
||||
_isImporting = false;
|
||||
@ -182,137 +182,6 @@ Future<void> _downloadExcelTemplate() async {
|
||||
}
|
||||
|
||||
|
||||
Future<void> _importFromExcel() async {
|
||||
try {
|
||||
final result = await FilePicker.platform.pickFiles(
|
||||
type: FileType.custom,
|
||||
allowedExtensions: ['xlsx', 'xls','csv'],
|
||||
allowMultiple: false,
|
||||
);
|
||||
|
||||
if (result == null || result.files.isEmpty) {
|
||||
Get.snackbar('Annulé', 'Aucun fichier sélectionné');
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_isImporting = false;
|
||||
_importProgress = 0.0;
|
||||
_importStatusText = '';
|
||||
});
|
||||
}
|
||||
|
||||
void _showExcelCompatibilityError() {
|
||||
Get.dialog(
|
||||
AlertDialog(
|
||||
title: const Text('Fichier Excel incompatible'),
|
||||
content: const Text(
|
||||
'Ce fichier Excel contient des éléments qui ne sont pas compatibles avec notre système d\'importation.\n\n'
|
||||
'Solutions recommandées :\n'
|
||||
'• Téléchargez notre modèle Excel et copiez-y vos données\n'
|
||||
'• Ou exportez votre fichier en format simple: Classeur Excel .xlsx depuis Excel\n'
|
||||
'• Ou créez un nouveau fichier Excel simple sans formatage complexe'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Get.back(),
|
||||
child: const Text('Annuler'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
_downloadExcelTemplate();
|
||||
},
|
||||
child: const Text('Télécharger modèle'),
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: Colors.green,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _downloadExcelTemplate() async {
|
||||
try {
|
||||
final excel = Excel.createExcel();
|
||||
excel.delete('Sheet1');
|
||||
excel.copy('Sheet1', 'Produits');
|
||||
excel.delete('Sheet1');
|
||||
|
||||
final sheet = excel['Produits'];
|
||||
|
||||
final headers = ['Nom', 'Prix', 'Catégorie', 'Description', 'Stock'];
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
final cell =
|
||||
sheet.cell(CellIndex.indexByColumnRow(columnIndex: i, rowIndex: 0));
|
||||
cell.value = headers[i];
|
||||
cell.cellStyle = CellStyle(
|
||||
bold: true,
|
||||
backgroundColorHex: '#E8F4FD',
|
||||
);
|
||||
}
|
||||
|
||||
final examples = [
|
||||
['Croissant', '1.50', 'Sucré', 'Délicieux croissant beurré', '20'],
|
||||
['Sandwich jambon', '4.00', 'Salé', 'Sandwich fait maison', '15'],
|
||||
['Jus d\'orange', '2.50', 'Jus', 'Jus d\'orange frais', '30'],
|
||||
[
|
||||
'Gâteau chocolat',
|
||||
'18.00',
|
||||
'Gateaux',
|
||||
'Gâteau au chocolat portion 8 personnes',
|
||||
'5'
|
||||
],
|
||||
];
|
||||
|
||||
for (int row = 0; row < examples.length; row++) {
|
||||
for (int col = 0; col < examples[row].length; col++) {
|
||||
final cell = sheet.cell(
|
||||
CellIndex.indexByColumnRow(columnIndex: col, rowIndex: row + 1));
|
||||
cell.value = examples[row][col];
|
||||
}
|
||||
}
|
||||
|
||||
sheet.setColWidth(0, 20);
|
||||
sheet.setColWidth(1, 10);
|
||||
sheet.setColWidth(2, 15);
|
||||
sheet.setColWidth(3, 30);
|
||||
sheet.setColWidth(4, 10);
|
||||
|
||||
final bytes = excel.save();
|
||||
|
||||
if (bytes == null) {
|
||||
Get.snackbar('Erreur', 'Impossible de créer le fichier modèle');
|
||||
return;
|
||||
}
|
||||
|
||||
final String? outputFile = await FilePicker.platform.saveFile(
|
||||
fileName: 'modele_import_produits.xlsx',
|
||||
allowedExtensions: ['xlsx'],
|
||||
type: FileType.custom,
|
||||
);
|
||||
|
||||
if (outputFile != null) {
|
||||
try {
|
||||
await File(outputFile).writeAsBytes(bytes);
|
||||
Get.snackbar(
|
||||
'Succès',
|
||||
'Modèle téléchargé avec succès\n$outputFile',
|
||||
duration: const Duration(seconds: 4),
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
} catch (e) {
|
||||
Get.snackbar('Erreur', 'Impossible d\'écrire le fichier: $e');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
Get.snackbar('Erreur', 'Erreur lors de la création du modèle: $e');
|
||||
debugPrint('Erreur création modèle Excel: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _importFromExcel() async {
|
||||
try {
|
||||
final result = await FilePicker.platform.pickFiles(
|
||||
@ -372,13 +241,11 @@ Future<void> _importFromExcel() async {
|
||||
_resetImportState();
|
||||
debugPrint('Erreur décodage Excel: $e');
|
||||
|
||||
if (e.toString().contains('styles') ||
|
||||
e.toString().contains('Damaged')) {
|
||||
if (e.toString().contains('styles') || e.toString().contains('Damaged')) {
|
||||
_showExcelCompatibilityError();
|
||||
return;
|
||||
} else {
|
||||
Get.snackbar('Erreur',
|
||||
'Impossible de lire le fichier Excel. Format non supporté.');
|
||||
Get.snackbar('Erreur', 'Impossible de lire le fichier Excel. Format non supporté.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -483,12 +350,10 @@ Future<void> _importFromExcel() async {
|
||||
}
|
||||
|
||||
String reference = _generateUniqueReference();
|
||||
var existingProduct =
|
||||
await _productDatabase.getProductByReference(reference);
|
||||
var existingProduct = await _productDatabase.getProductByReference(reference);
|
||||
while (existingProduct != null) {
|
||||
reference = _generateUniqueReference();
|
||||
existingProduct =
|
||||
await _productDatabase.getProductByReference(reference);
|
||||
existingProduct = await _productDatabase.getProductByReference(reference);
|
||||
}
|
||||
|
||||
final product = Product(
|
||||
@ -511,6 +376,7 @@ Future<void> _importFromExcel() async {
|
||||
|
||||
await _productDatabase.createProduct(product);
|
||||
successCount++;
|
||||
|
||||
} catch (e) {
|
||||
errorCount++;
|
||||
errorMessages.add('Ligne ${i + 1}: Erreur de traitement - $e');
|
||||
@ -546,6 +412,7 @@ Future<void> _importFromExcel() async {
|
||||
|
||||
// Recharger la liste des produits après importation
|
||||
_loadProducts();
|
||||
|
||||
} catch (e) {
|
||||
_resetImportState();
|
||||
Get.snackbar('Erreur', 'Erreur lors de l\'importation Excel: $e');
|
||||
@ -602,7 +469,6 @@ Future<void> _importFromExcel() async {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
//=============================================================================================================================
|
||||
Future<void> _loadProducts() async {
|
||||
setState(() => _isLoading = true);
|
||||
@ -674,8 +540,7 @@ Future<void> _importFromExcel() async {
|
||||
final path = '${directory.path}/$reference.png';
|
||||
|
||||
try {
|
||||
final picData =
|
||||
await painter.toImageData(2048, format: ImageByteFormat.png);
|
||||
final picData = await painter.toImageData(2048, format: ImageByteFormat.png);
|
||||
if (picData != null) {
|
||||
await File(path).writeAsBytes(picData.buffer.asUint8List());
|
||||
} else {
|
||||
@ -695,8 +560,7 @@ Future<void> _importFromExcel() async {
|
||||
final descriptionController = TextEditingController();
|
||||
final imageController = TextEditingController();
|
||||
|
||||
String selectedCategory =
|
||||
_predefinedCategories.last; // 'Non catégorisé' par défaut
|
||||
String selectedCategory = _predefinedCategories.last; // 'Non catégorisé' par défaut
|
||||
File? pickedImage;
|
||||
String? qrPreviewData;
|
||||
String? currentReference;
|
||||
@ -724,8 +588,7 @@ Future<void> _importFromExcel() async {
|
||||
color: Colors.green.shade100,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child:
|
||||
Icon(Icons.add_shopping_cart, color: Colors.green.shade700),
|
||||
child: Icon(Icons.add_shopping_cart, color: Colors.green.shade700),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const Text('Ajouter un produit'),
|
||||
@ -751,13 +614,11 @@ Future<void> _importFromExcel() async {
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.info,
|
||||
color: Colors.red.shade600, size: 16),
|
||||
Icon(Icons.info, color: Colors.red.shade600, size: 16),
|
||||
const SizedBox(width: 8),
|
||||
const Text(
|
||||
'Les champs marqués d\'un * sont obligatoires',
|
||||
style: TextStyle(
|
||||
fontSize: 12, fontWeight: FontWeight.w500),
|
||||
style: TextStyle(fontSize: 12, fontWeight: FontWeight.w500),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -788,10 +649,9 @@ Future<void> _importFromExcel() async {
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: priceController,
|
||||
keyboardType: const TextInputType.numberWithOptions(
|
||||
decimal: true),
|
||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Prix (MGA) *',
|
||||
labelText: 'Prix (FCFA) *',
|
||||
border: const OutlineInputBorder(),
|
||||
prefixIcon: const Icon(Icons.attach_money),
|
||||
filled: true,
|
||||
@ -820,10 +680,8 @@ Future<void> _importFromExcel() async {
|
||||
// Catégorie
|
||||
DropdownButtonFormField<String>(
|
||||
value: selectedCategory,
|
||||
items: _predefinedCategories
|
||||
.map((category) => DropdownMenuItem(
|
||||
value: category, child: Text(category)))
|
||||
.toList(),
|
||||
items: _predefinedCategories.map((category) =>
|
||||
DropdownMenuItem(value: category, child: Text(category))).toList(),
|
||||
onChanged: (value) {
|
||||
setDialogState(() => selectedCategory = value!);
|
||||
},
|
||||
@ -892,13 +750,10 @@ Future<void> _importFromExcel() async {
|
||||
const SizedBox(width: 8),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () async {
|
||||
final result = await FilePicker.platform
|
||||
.pickFiles(type: FileType.image);
|
||||
if (result != null &&
|
||||
result.files.single.path != null) {
|
||||
final result = await FilePicker.platform.pickFiles(type: FileType.image);
|
||||
if (result != null && result.files.single.path != null) {
|
||||
setDialogState(() {
|
||||
pickedImage =
|
||||
File(result.files.single.path!);
|
||||
pickedImage = File(result.files.single.path!);
|
||||
imageController.text = pickedImage!.path;
|
||||
});
|
||||
}
|
||||
@ -921,13 +776,11 @@ Future<void> _importFromExcel() async {
|
||||
width: 100,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border:
|
||||
Border.all(color: Colors.grey.shade300),
|
||||
border: Border.all(color: Colors.grey.shade300),
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Image.file(pickedImage!,
|
||||
fit: BoxFit.cover),
|
||||
child: Image.file(pickedImage!, fit: BoxFit.cover),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -949,8 +802,7 @@ Future<void> _importFromExcel() async {
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.qr_code_2,
|
||||
color: Colors.green.shade700),
|
||||
Icon(Icons.qr_code_2, color: Colors.green.shade700),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'Aperçu du QR Code',
|
||||
@ -980,8 +832,7 @@ Future<void> _importFromExcel() async {
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Réf: $currentReference',
|
||||
style: const TextStyle(
|
||||
fontSize: 10, color: Colors.grey),
|
||||
style: const TextStyle(fontSize: 10, color: Colors.grey),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -1010,15 +861,12 @@ Future<void> _importFromExcel() async {
|
||||
|
||||
try {
|
||||
// Générer une référence unique et vérifier son unicité
|
||||
String finalReference =
|
||||
currentReference ?? _generateUniqueReference();
|
||||
var existingProduct = await _productDatabase
|
||||
.getProductByReference(finalReference);
|
||||
String finalReference = currentReference ?? _generateUniqueReference();
|
||||
var existingProduct = await _productDatabase.getProductByReference(finalReference);
|
||||
|
||||
while (existingProduct != null) {
|
||||
finalReference = _generateUniqueReference();
|
||||
existingProduct = await _productDatabase
|
||||
.getProductByReference(finalReference);
|
||||
existingProduct = await _productDatabase.getProductByReference(finalReference);
|
||||
}
|
||||
|
||||
// Générer le QR code
|
||||
@ -1149,12 +997,9 @@ Future<void> _importFromExcel() async {
|
||||
|
||||
void _editProduct(Product product) {
|
||||
final nameController = TextEditingController(text: product.name);
|
||||
final priceController =
|
||||
TextEditingController(text: product.price.toString());
|
||||
final stockController =
|
||||
TextEditingController(text: product.stock.toString());
|
||||
final descriptionController =
|
||||
TextEditingController(text: product.description ?? '');
|
||||
final priceController = TextEditingController(text: product.price.toString());
|
||||
final stockController = TextEditingController(text: product.stock.toString());
|
||||
final descriptionController = TextEditingController(text: product.description ?? '');
|
||||
final imageController = TextEditingController(text: product.image);
|
||||
|
||||
String selectedCategory = product.category;
|
||||
@ -1177,16 +1022,17 @@ Future<void> _importFromExcel() async {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
TextField(
|
||||
controller: priceController,
|
||||
keyboardType:
|
||||
const TextInputType.numberWithOptions(decimal: true),
|
||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Prix*',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
TextField(
|
||||
controller: stockController,
|
||||
keyboardType: TextInputType.number,
|
||||
@ -1196,6 +1042,7 @@ Future<void> _importFromExcel() async {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
StatefulBuilder(
|
||||
builder: (context, setDialogState) {
|
||||
return Column(
|
||||
@ -1215,14 +1062,11 @@ Future<void> _importFromExcel() async {
|
||||
const SizedBox(width: 8),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
final result = await FilePicker.platform
|
||||
.pickFiles(type: FileType.image);
|
||||
if (result != null &&
|
||||
result.files.single.path != null) {
|
||||
final result = await FilePicker.platform.pickFiles(type: FileType.image);
|
||||
if (result != null && result.files.single.path != null) {
|
||||
if (context.mounted) {
|
||||
setDialogState(() {
|
||||
pickedImage =
|
||||
File(result.files.single.path!);
|
||||
pickedImage = File(result.files.single.path!);
|
||||
imageController.text = pickedImage!.path;
|
||||
});
|
||||
}
|
||||
@ -1233,6 +1077,7 @@ Future<void> _importFromExcel() async {
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
if (pickedImage != null || product.image!.isNotEmpty)
|
||||
Container(
|
||||
height: 100,
|
||||
@ -1246,19 +1091,16 @@ Future<void> _importFromExcel() async {
|
||||
child: pickedImage != null
|
||||
? Image.file(pickedImage!, fit: BoxFit.cover)
|
||||
: (product.image!.isNotEmpty
|
||||
? Image.file(File(product.image!),
|
||||
fit: BoxFit.cover)
|
||||
? Image.file(File(product.image!), fit: BoxFit.cover)
|
||||
: const Icon(Icons.image, size: 50)),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
DropdownButtonFormField<String>(
|
||||
value: selectedCategory,
|
||||
items: _categories
|
||||
.skip(1)
|
||||
.map((category) => DropdownMenuItem(
|
||||
value: category, child: Text(category)))
|
||||
.toList(),
|
||||
items: _categories.skip(1).map((category) =>
|
||||
DropdownMenuItem(value: category, child: Text(category))).toList(),
|
||||
onChanged: (value) {
|
||||
if (context.mounted) {
|
||||
setDialogState(() => selectedCategory = value!);
|
||||
@ -1270,6 +1112,7 @@ Future<void> _importFromExcel() async {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
TextField(
|
||||
controller: descriptionController,
|
||||
maxLines: 3,
|
||||
@ -1363,8 +1206,7 @@ Future<void> _importFromExcel() async {
|
||||
Get.snackbar('Erreur', 'Suppression échouée: $e');
|
||||
}
|
||||
},
|
||||
child:
|
||||
const Text('Supprimer', style: TextStyle(color: Colors.white)),
|
||||
child: const Text('Supprimer', style: TextStyle(color: Colors.white)),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -1426,8 +1268,7 @@ Future<void> _importFromExcel() async {
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8, vertical: 2),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue.shade100,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
@ -1576,10 +1417,8 @@ Future<void> _importFromExcel() async {
|
||||
),
|
||||
child: DropdownButton<String>(
|
||||
value: _selectedCategory,
|
||||
items: _categories
|
||||
.map((category) => DropdownMenuItem(
|
||||
value: category, child: Text(category)))
|
||||
.toList(),
|
||||
items: _categories.map((category) =>
|
||||
DropdownMenuItem(value: category, child: Text(category))).toList(),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedCategory = value!;
|
||||
@ -1609,8 +1448,7 @@ Future<void> _importFromExcel() async {
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
if (_searchController.text.isNotEmpty ||
|
||||
_selectedCategory != 'Tous')
|
||||
if (_searchController.text.isNotEmpty || _selectedCategory != 'Tous')
|
||||
TextButton.icon(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
|
||||
@ -38,7 +38,7 @@ class _BilanMoisState extends State<BilanMois> {
|
||||
children: [
|
||||
_buildInfoCard(
|
||||
title: 'Chiffre réalisé',
|
||||
value: '${controller.totalSum.value.toStringAsFixed(2)} MGA',
|
||||
value: '${controller.totalSum.value.toStringAsFixed(2)} fcfa',
|
||||
color: Colors.green,
|
||||
icon: Icons.monetization_on,
|
||||
),
|
||||
|
||||
@ -112,7 +112,7 @@ class HistoryDetailPage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Total Somme: $totalSum MGA',
|
||||
'Total Somme: $totalSum fcfa',
|
||||
style: const TextStyle(
|
||||
fontSize: 18.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
@ -197,7 +197,7 @@ class HistoryDetailPage extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Text('Total: ${order.totalPrice} MGA'),
|
||||
subtitle: Text('Total: ${order.totalPrice} fcfa'),
|
||||
trailing: Text('Date: ${order.dateTime}'),
|
||||
leading: Text('vendeur: ${order.user}'),
|
||||
onTap: () {
|
||||
|
||||
@ -86,11 +86,11 @@ class TicketPage extends StatelessWidget {
|
||||
width: 1,
|
||||
),
|
||||
PosColumn(
|
||||
text: '${product.price.toStringAsFixed(2)} MGA',
|
||||
text: '${product.price.toStringAsFixed(2)} fcfa',
|
||||
width: 1,
|
||||
),
|
||||
PosColumn(
|
||||
text: '${productTotal.toStringAsFixed(2)} MGA',
|
||||
text: '${productTotal.toStringAsFixed(2)} fcfa',
|
||||
width: 1,
|
||||
),
|
||||
]);
|
||||
@ -104,7 +104,7 @@ class TicketPage extends StatelessWidget {
|
||||
styles: const PosStyles(align: PosAlign.left, bold: true),
|
||||
),
|
||||
PosColumn(
|
||||
text: '${totalCartPrice.toStringAsFixed(2)} MGA',
|
||||
text: '${totalCartPrice.toStringAsFixed(2)} fcfa',
|
||||
width: 1,
|
||||
styles: const PosStyles(align: PosAlign.left, bold: true),
|
||||
),
|
||||
@ -116,7 +116,7 @@ class TicketPage extends StatelessWidget {
|
||||
styles: const PosStyles(align: PosAlign.left),
|
||||
),
|
||||
PosColumn(
|
||||
text: '${amountPaid.toStringAsFixed(2)} MGA',
|
||||
text: '${amountPaid.toStringAsFixed(2)} fcfa',
|
||||
width: 1,
|
||||
styles: const PosStyles(align: PosAlign.left),
|
||||
),
|
||||
@ -128,7 +128,7 @@ class TicketPage extends StatelessWidget {
|
||||
styles: const PosStyles(align: PosAlign.left),
|
||||
),
|
||||
PosColumn(
|
||||
text: '${(amountPaid - totalCartPrice).toStringAsFixed(2)} MGA',
|
||||
text: '${(amountPaid - totalCartPrice).toStringAsFixed(2)} fcfa',
|
||||
width: 1,
|
||||
styles: const PosStyles(align: PosAlign.left),
|
||||
),
|
||||
@ -179,8 +179,8 @@ class TicketPage extends StatelessWidget {
|
||||
return [
|
||||
product.name,
|
||||
quantity.toString(),
|
||||
'${product.price.toStringAsFixed(2)} MGA',
|
||||
'${productTotal.toStringAsFixed(2)} MGA',
|
||||
'${product.price.toStringAsFixed(2)} fcfa',
|
||||
'${productTotal.toStringAsFixed(2)} fcfa',
|
||||
];
|
||||
}).toList(),
|
||||
],
|
||||
@ -194,7 +194,7 @@ class TicketPage extends StatelessWidget {
|
||||
pw.Text('Total :',
|
||||
style: pw.TextStyle(
|
||||
fontSize: 18, fontWeight: pw.FontWeight.bold)),
|
||||
pw.Text('${totalCartPrice.toStringAsFixed(2)} MGA',
|
||||
pw.Text('${totalCartPrice.toStringAsFixed(2)} fcfa',
|
||||
style: pw.TextStyle(
|
||||
fontSize: 18, fontWeight: pw.FontWeight.bold)),
|
||||
],
|
||||
@ -207,7 +207,7 @@ class TicketPage extends StatelessWidget {
|
||||
children: [
|
||||
pw.Text('Somme remise :',
|
||||
style: const pw.TextStyle(fontSize: 16)),
|
||||
pw.Text('${amountPaid.toStringAsFixed(2)} MGA',
|
||||
pw.Text('${amountPaid.toStringAsFixed(2)} fcfa',
|
||||
style: const pw.TextStyle(fontSize: 16)),
|
||||
],
|
||||
),
|
||||
@ -218,7 +218,7 @@ class TicketPage extends StatelessWidget {
|
||||
pw.Text('Somme rendue :',
|
||||
style: const pw.TextStyle(fontSize: 16)),
|
||||
pw.Text(
|
||||
'${(amountPaid - totalCartPrice).toStringAsFixed(2)} MGA',
|
||||
'${(amountPaid - totalCartPrice).toStringAsFixed(2)} fcfa',
|
||||
style: const pw.TextStyle(fontSize: 16)),
|
||||
],
|
||||
),
|
||||
@ -387,14 +387,14 @@ class TicketPage extends StatelessWidget {
|
||||
TableCell(
|
||||
child: Center(
|
||||
child: Text(
|
||||
'${product.price.toStringAsFixed(2)} MGA',
|
||||
'${product.price.toStringAsFixed(2)} fcfa',
|
||||
),
|
||||
),
|
||||
),
|
||||
TableCell(
|
||||
child: Center(
|
||||
child: Text(
|
||||
'${productTotal.toStringAsFixed(2)} MGA',
|
||||
'${productTotal.toStringAsFixed(2)} fcfa',
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -421,7 +421,7 @@ class TicketPage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${totalOrderAmount.toStringAsFixed(2)} MGA',
|
||||
'${totalOrderAmount.toStringAsFixed(2)} fcfa',
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
@ -446,7 +446,7 @@ class TicketPage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${amountPaid.toStringAsFixed(2)} MGA',
|
||||
'${amountPaid.toStringAsFixed(2)} fcfa',
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
@ -464,7 +464,7 @@ class TicketPage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${change.toStringAsFixed(2)} MGA',
|
||||
'${change.toStringAsFixed(2)} fcfa',
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
|
||||
@ -177,7 +177,8 @@ class _AccueilPageState extends State<AccueilPage> {
|
||||
void showTicketPage() {
|
||||
Get.offAll(TicketPage(
|
||||
businessName: 'Youmaz',
|
||||
businessAddress: 'quartier escale, Diourbel, Sénégal, en face de Sonatel',
|
||||
businessAddress:
|
||||
'quartier escale, Diourbel, Sénégal, en face de Sonatel',
|
||||
businessPhoneNumber: '77 446 92 68',
|
||||
cartItems: selectedProducts,
|
||||
totalCartPrice: calculateTotalPrice(),
|
||||
@ -209,8 +210,7 @@ class _AccueilPageState extends State<AccueilPage> {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
Color.fromARGB(255, 4, 54, 95),
|
||||
),
|
||||
Color.fromARGB(255, 4, 54, 95),),
|
||||
));
|
||||
} else if (snapshot.hasError) {
|
||||
return const Center(
|
||||
@ -235,10 +235,12 @@ class _AccueilPageState extends State<AccueilPage> {
|
||||
child: Container(
|
||||
padding:const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
|
||||
color: Colors.white.withOpacity(0.9),
|
||||
borderRadius: const BorderRadius.only(
|
||||
topRight: Radius.circular(20),
|
||||
),
|
||||
|
||||
),
|
||||
child: ListView.builder(
|
||||
itemCount: categories.length,
|
||||
@ -250,8 +252,7 @@ class _AccueilPageState extends State<AccueilPage> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
margin:
|
||||
const EdgeInsets.symmetric(vertical: 8),
|
||||
margin: const EdgeInsets.symmetric(vertical: 8),
|
||||
padding:const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Color.fromARGB(255, 4, 54, 95),
|
||||
@ -260,8 +261,7 @@ class _AccueilPageState extends State<AccueilPage> {
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
blurRadius: 4,
|
||||
offset: Offset(0, 2),
|
||||
)
|
||||
offset: Offset(0, 2),)
|
||||
],
|
||||
),
|
||||
child: Center(
|
||||
@ -303,9 +303,10 @@ class _AccueilPageState extends State<AccueilPage> {
|
||||
),
|
||||
|
||||
// Section panier
|
||||
|
||||
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
Expanded(flex: 1,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[200],
|
||||
@ -347,8 +348,7 @@ class _AccueilPageState extends State<AccueilPage> {
|
||||
child: selectedProducts.isEmpty
|
||||
? const Center(
|
||||
child: Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.shopping_cart,
|
||||
size: 48, color: Colors.grey),
|
||||
@ -365,35 +365,26 @@ class _AccueilPageState extends State<AccueilPage> {
|
||||
: ListView.builder(
|
||||
itemCount: selectedProducts.length,
|
||||
itemBuilder: (context, index) {
|
||||
final cartItem =
|
||||
selectedProducts[index];
|
||||
final cartItem = selectedProducts[index];
|
||||
return Card(
|
||||
margin:
|
||||
EdgeInsets.symmetric(vertical: 4),
|
||||
margin: EdgeInsets.symmetric(vertical: 4),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(10),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
elevation: 2,
|
||||
child: ListTile(
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 4),
|
||||
leading: Icon(
|
||||
Icons.shopping_basket,
|
||||
color: Color.fromARGB(
|
||||
255, 4, 54, 95),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 12, vertical: 4),
|
||||
leading: Icon(Icons.shopping_basket,
|
||||
color: Color.fromARGB(255, 4, 54, 95),),
|
||||
title: Text(
|
||||
cartItem.product.name,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
subtitle: Text(
|
||||
'${NumberFormat('#,##0').format(cartItem.product.price)} MGA x ${cartItem.quantity}',
|
||||
style:
|
||||
const TextStyle(fontSize: 14),
|
||||
'${NumberFormat('#,##0').format(cartItem.product.price)} FCFA x ${cartItem.quantity}',
|
||||
style:const TextStyle(fontSize: 14),
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@ -401,8 +392,7 @@ class _AccueilPageState extends State<AccueilPage> {
|
||||
Text(
|
||||
'${NumberFormat('#,##0').format(cartItem.product.price * cartItem.quantity)}',
|
||||
style:const TextStyle(
|
||||
fontWeight:
|
||||
FontWeight.bold),
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
IconButton(
|
||||
@ -440,8 +430,7 @@ class _AccueilPageState extends State<AccueilPage> {
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text('Total:',
|
||||
style: TextStyle(
|
||||
@ -452,8 +441,7 @@ class _AccueilPageState extends State<AccueilPage> {
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color.fromARGB(255, 4, 54, 95),
|
||||
),
|
||||
color: Color.fromARGB(255, 4, 54, 95),),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -472,8 +460,7 @@ class _AccueilPageState extends State<AccueilPage> {
|
||||
),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
amountPaid =
|
||||
double.tryParse(value) ?? 0;
|
||||
amountPaid = double.tryParse(value) ?? 0;
|
||||
});
|
||||
},
|
||||
),
|
||||
@ -481,8 +468,7 @@ class _AccueilPageState extends State<AccueilPage> {
|
||||
ElevatedButton.icon(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.green,
|
||||
padding:
|
||||
EdgeInsets.symmetric(vertical: 16),
|
||||
padding: EdgeInsets.symmetric(vertical: 16),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
@ -502,8 +488,7 @@ class _AccueilPageState extends State<AccueilPage> {
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
),)
|
||||
],
|
||||
);
|
||||
} else {
|
||||
|
||||
@ -19,8 +19,7 @@ void main() async {
|
||||
|
||||
// Afficher les informations de la base (pour debug)
|
||||
await AppDatabase.instance.printDatabaseInfo();
|
||||
Get.put(
|
||||
UserController()); // Ajoute ce code AVANT tout accès au UserController
|
||||
Get.put(UserController()); // Ajoute ce code AVANT tout accès au UserController
|
||||
setupLogger();
|
||||
|
||||
runApp(const GetMaterialApp(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user