|
|
@ -13,7 +13,6 @@ import '../Components/app_bar.dart'; |
|
|
import '../Models/produit.dart'; |
|
|
import '../Models/produit.dart'; |
|
|
import '../Services/productDatabase.dart'; |
|
|
import '../Services/productDatabase.dart'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ProductManagementPage extends StatefulWidget { |
|
|
class ProductManagementPage extends StatefulWidget { |
|
|
const ProductManagementPage({super.key}); |
|
|
const ProductManagementPage({super.key}); |
|
|
|
|
|
|
|
|
@ -32,7 +31,13 @@ class _ProductManagementPageState extends State<ProductManagementPage> { |
|
|
|
|
|
|
|
|
// Catégories prédéfinies pour l'ajout de produits |
|
|
// Catégories prédéfinies pour l'ajout de produits |
|
|
final List<String> _predefinedCategories = [ |
|
|
final List<String> _predefinedCategories = [ |
|
|
'Sucré', 'Salé', 'Jus', 'Gateaux', 'Snacks', 'Boissons', 'Non catégorisé' |
|
|
'Sucré', |
|
|
|
|
|
'Salé', |
|
|
|
|
|
'Jus', |
|
|
|
|
|
'Gateaux', |
|
|
|
|
|
'Snacks', |
|
|
|
|
|
'Boissons', |
|
|
|
|
|
'Non catégorisé' |
|
|
]; |
|
|
]; |
|
|
|
|
|
|
|
|
@override |
|
|
@override |
|
|
@ -48,12 +53,6 @@ class _ProductManagementPageState extends State<ProductManagementPage> { |
|
|
super.dispose(); |
|
|
super.dispose(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//====================================================================================================== |
|
|
//====================================================================================================== |
|
|
// Ajoutez ces variables à la classe _ProductManagementPageState |
|
|
// Ajoutez ces variables à la classe _ProductManagementPageState |
|
|
bool _isImporting = false; |
|
|
bool _isImporting = false; |
|
|
@ -78,8 +77,7 @@ void _showExcelCompatibilityError() { |
|
|
'Solutions recommandées :\n' |
|
|
'Solutions recommandées :\n' |
|
|
'• Téléchargez notre modèle Excel et copiez-y vos donné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 exportez votre fichier en format simple: Classeur Excel .xlsx depuis Excel\n' |
|
|
'• Ou créez un nouveau fichier Excel simple sans formatage complexe' |
|
|
'• Ou créez un nouveau fichier Excel simple sans formatage complexe'), |
|
|
), |
|
|
|
|
|
actions: [ |
|
|
actions: [ |
|
|
TextButton( |
|
|
TextButton( |
|
|
onPressed: () => Get.back(), |
|
|
onPressed: () => Get.back(), |
|
|
@ -112,7 +110,8 @@ Future<void> _downloadExcelTemplate() async { |
|
|
|
|
|
|
|
|
final headers = ['Nom', 'Prix', 'Catégorie', 'Description', 'Stock']; |
|
|
final headers = ['Nom', 'Prix', 'Catégorie', 'Description', 'Stock']; |
|
|
for (int i = 0; i < headers.length; i++) { |
|
|
for (int i = 0; i < headers.length; i++) { |
|
|
final cell = sheet.cell(CellIndex.indexByColumnRow(columnIndex: i, rowIndex: 0)); |
|
|
final cell = |
|
|
|
|
|
sheet.cell(CellIndex.indexByColumnRow(columnIndex: i, rowIndex: 0)); |
|
|
cell.value = headers[i]; |
|
|
cell.value = headers[i]; |
|
|
cell.cellStyle = CellStyle( |
|
|
cell.cellStyle = CellStyle( |
|
|
bold: true, |
|
|
bold: true, |
|
|
@ -124,12 +123,19 @@ Future<void> _downloadExcelTemplate() async { |
|
|
['Croissant', '1.50', 'Sucré', 'Délicieux croissant beurré', '20'], |
|
|
['Croissant', '1.50', 'Sucré', 'Délicieux croissant beurré', '20'], |
|
|
['Sandwich jambon', '4.00', 'Salé', 'Sandwich fait maison', '15'], |
|
|
['Sandwich jambon', '4.00', 'Salé', 'Sandwich fait maison', '15'], |
|
|
['Jus d\'orange', '2.50', 'Jus', 'Jus d\'orange frais', '30'], |
|
|
['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'], |
|
|
[ |
|
|
|
|
|
'Gâteau chocolat', |
|
|
|
|
|
'18.00', |
|
|
|
|
|
'Gateaux', |
|
|
|
|
|
'Gâteau au chocolat portion 8 personnes', |
|
|
|
|
|
'5' |
|
|
|
|
|
], |
|
|
]; |
|
|
]; |
|
|
|
|
|
|
|
|
for (int row = 0; row < examples.length; row++) { |
|
|
for (int row = 0; row < examples.length; row++) { |
|
|
for (int col = 0; col < examples[row].length; col++) { |
|
|
for (int col = 0; col < examples[row].length; col++) { |
|
|
final cell = sheet.cell(CellIndex.indexByColumnRow(columnIndex: col, rowIndex: row + 1)); |
|
|
final cell = sheet.cell( |
|
|
|
|
|
CellIndex.indexByColumnRow(columnIndex: col, rowIndex: row + 1)); |
|
|
cell.value = examples[row][col]; |
|
|
cell.value = examples[row][col]; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -232,11 +238,13 @@ Future<void> _importFromExcel() async { |
|
|
_resetImportState(); |
|
|
_resetImportState(); |
|
|
debugPrint('Erreur décodage Excel: $e'); |
|
|
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(); |
|
|
_showExcelCompatibilityError(); |
|
|
return; |
|
|
return; |
|
|
} else { |
|
|
} 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; |
|
|
return; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -341,10 +349,12 @@ Future<void> _importFromExcel() async { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
String reference = _generateUniqueReference(); |
|
|
String reference = _generateUniqueReference(); |
|
|
var existingProduct = await _productDatabase.getProductByReference(reference); |
|
|
var existingProduct = |
|
|
|
|
|
await _productDatabase.getProductByReference(reference); |
|
|
while (existingProduct != null) { |
|
|
while (existingProduct != null) { |
|
|
reference = _generateUniqueReference(); |
|
|
reference = _generateUniqueReference(); |
|
|
existingProduct = await _productDatabase.getProductByReference(reference); |
|
|
existingProduct = |
|
|
|
|
|
await _productDatabase.getProductByReference(reference); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
final product = Product( |
|
|
final product = Product( |
|
|
@ -367,7 +377,6 @@ Future<void> _importFromExcel() async { |
|
|
|
|
|
|
|
|
await _productDatabase.createProduct(product); |
|
|
await _productDatabase.createProduct(product); |
|
|
successCount++; |
|
|
successCount++; |
|
|
|
|
|
|
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
errorCount++; |
|
|
errorCount++; |
|
|
errorMessages.add('Ligne ${i + 1}: Erreur de traitement - $e'); |
|
|
errorMessages.add('Ligne ${i + 1}: Erreur de traitement - $e'); |
|
|
@ -403,7 +412,6 @@ Future<void> _importFromExcel() async { |
|
|
|
|
|
|
|
|
// Recharger la liste des produits après importation |
|
|
// Recharger la liste des produits après importation |
|
|
_loadProducts(); |
|
|
_loadProducts(); |
|
|
|
|
|
|
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
_resetImportState(); |
|
|
_resetImportState(); |
|
|
Get.snackbar('Erreur', 'Erreur lors de l\'importation Excel: $e'); |
|
|
Get.snackbar('Erreur', 'Erreur lors de l\'importation Excel: $e'); |
|
|
@ -460,6 +468,7 @@ Widget _buildImportProgressIndicator() { |
|
|
), |
|
|
), |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
//============================================================================================================================= |
|
|
//============================================================================================================================= |
|
|
Future<void> _loadProducts() async { |
|
|
Future<void> _loadProducts() async { |
|
|
setState(() => _isLoading = true); |
|
|
setState(() => _isLoading = true); |
|
|
@ -531,7 +540,8 @@ Widget _buildImportProgressIndicator() { |
|
|
final path = '${directory.path}/$reference.png'; |
|
|
final path = '${directory.path}/$reference.png'; |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
final picData = await painter.toImageData(2048, format: ImageByteFormat.png); |
|
|
final picData = |
|
|
|
|
|
await painter.toImageData(2048, format: ImageByteFormat.png); |
|
|
if (picData != null) { |
|
|
if (picData != null) { |
|
|
await File(path).writeAsBytes(picData.buffer.asUint8List()); |
|
|
await File(path).writeAsBytes(picData.buffer.asUint8List()); |
|
|
} else { |
|
|
} else { |
|
|
@ -551,7 +561,8 @@ Widget _buildImportProgressIndicator() { |
|
|
final descriptionController = TextEditingController(); |
|
|
final descriptionController = TextEditingController(); |
|
|
final imageController = 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; |
|
|
File? pickedImage; |
|
|
String? qrPreviewData; |
|
|
String? qrPreviewData; |
|
|
String? currentReference; |
|
|
String? currentReference; |
|
|
@ -579,7 +590,8 @@ Widget _buildImportProgressIndicator() { |
|
|
color: Colors.green.shade100, |
|
|
color: Colors.green.shade100, |
|
|
borderRadius: BorderRadius.circular(8), |
|
|
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 SizedBox(width: 12), |
|
|
const Text('Ajouter un produit'), |
|
|
const Text('Ajouter un produit'), |
|
|
@ -605,11 +617,13 @@ Widget _buildImportProgressIndicator() { |
|
|
), |
|
|
), |
|
|
child: Row( |
|
|
child: Row( |
|
|
children: [ |
|
|
children: [ |
|
|
Icon(Icons.info, color: Colors.red.shade600, size: 16), |
|
|
Icon(Icons.info, |
|
|
|
|
|
color: Colors.red.shade600, size: 16), |
|
|
const SizedBox(width: 8), |
|
|
const SizedBox(width: 8), |
|
|
const Text( |
|
|
const Text( |
|
|
'Les champs marqués d\'un * sont obligatoires', |
|
|
'Les champs marqués d\'un * sont obligatoires', |
|
|
style: TextStyle(fontSize: 12, fontWeight: FontWeight.w500), |
|
|
style: TextStyle( |
|
|
|
|
|
fontSize: 12, fontWeight: FontWeight.w500), |
|
|
), |
|
|
), |
|
|
], |
|
|
], |
|
|
), |
|
|
), |
|
|
@ -640,9 +654,10 @@ Widget _buildImportProgressIndicator() { |
|
|
Expanded( |
|
|
Expanded( |
|
|
child: TextField( |
|
|
child: TextField( |
|
|
controller: priceController, |
|
|
controller: priceController, |
|
|
keyboardType: const TextInputType.numberWithOptions(decimal: true), |
|
|
keyboardType: const TextInputType.numberWithOptions( |
|
|
|
|
|
decimal: true), |
|
|
decoration: InputDecoration( |
|
|
decoration: InputDecoration( |
|
|
labelText: 'Prix (FCFA) *', |
|
|
labelText: 'Prix (MGA) *', |
|
|
border: const OutlineInputBorder(), |
|
|
border: const OutlineInputBorder(), |
|
|
prefixIcon: const Icon(Icons.attach_money), |
|
|
prefixIcon: const Icon(Icons.attach_money), |
|
|
filled: true, |
|
|
filled: true, |
|
|
@ -671,8 +686,10 @@ Widget _buildImportProgressIndicator() { |
|
|
// Catégorie |
|
|
// Catégorie |
|
|
DropdownButtonFormField<String>( |
|
|
DropdownButtonFormField<String>( |
|
|
value: selectedCategory, |
|
|
value: selectedCategory, |
|
|
items: _predefinedCategories.map((category) => |
|
|
items: _predefinedCategories |
|
|
DropdownMenuItem(value: category, child: Text(category))).toList(), |
|
|
.map((category) => DropdownMenuItem( |
|
|
|
|
|
value: category, child: Text(category))) |
|
|
|
|
|
.toList(), |
|
|
onChanged: (value) { |
|
|
onChanged: (value) { |
|
|
setDialogState(() => selectedCategory = value!); |
|
|
setDialogState(() => selectedCategory = value!); |
|
|
}, |
|
|
}, |
|
|
@ -741,10 +758,13 @@ Widget _buildImportProgressIndicator() { |
|
|
const SizedBox(width: 8), |
|
|
const SizedBox(width: 8), |
|
|
ElevatedButton.icon( |
|
|
ElevatedButton.icon( |
|
|
onPressed: () async { |
|
|
onPressed: () async { |
|
|
final result = await FilePicker.platform.pickFiles(type: FileType.image); |
|
|
final result = await FilePicker.platform |
|
|
if (result != null && result.files.single.path != null) { |
|
|
.pickFiles(type: FileType.image); |
|
|
|
|
|
if (result != null && |
|
|
|
|
|
result.files.single.path != null) { |
|
|
setDialogState(() { |
|
|
setDialogState(() { |
|
|
pickedImage = File(result.files.single.path!); |
|
|
pickedImage = |
|
|
|
|
|
File(result.files.single.path!); |
|
|
imageController.text = pickedImage!.path; |
|
|
imageController.text = pickedImage!.path; |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
@ -767,11 +787,13 @@ Widget _buildImportProgressIndicator() { |
|
|
width: 100, |
|
|
width: 100, |
|
|
decoration: BoxDecoration( |
|
|
decoration: BoxDecoration( |
|
|
borderRadius: BorderRadius.circular(8), |
|
|
borderRadius: BorderRadius.circular(8), |
|
|
border: Border.all(color: Colors.grey.shade300), |
|
|
border: |
|
|
|
|
|
Border.all(color: Colors.grey.shade300), |
|
|
), |
|
|
), |
|
|
child: ClipRRect( |
|
|
child: ClipRRect( |
|
|
borderRadius: BorderRadius.circular(8), |
|
|
borderRadius: BorderRadius.circular(8), |
|
|
child: Image.file(pickedImage!, fit: BoxFit.cover), |
|
|
child: Image.file(pickedImage!, |
|
|
|
|
|
fit: BoxFit.cover), |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
@ -793,7 +815,8 @@ Widget _buildImportProgressIndicator() { |
|
|
children: [ |
|
|
children: [ |
|
|
Row( |
|
|
Row( |
|
|
children: [ |
|
|
children: [ |
|
|
Icon(Icons.qr_code_2, color: Colors.green.shade700), |
|
|
Icon(Icons.qr_code_2, |
|
|
|
|
|
color: Colors.green.shade700), |
|
|
const SizedBox(width: 8), |
|
|
const SizedBox(width: 8), |
|
|
Text( |
|
|
Text( |
|
|
'Aperçu du QR Code', |
|
|
'Aperçu du QR Code', |
|
|
@ -823,7 +846,8 @@ Widget _buildImportProgressIndicator() { |
|
|
const SizedBox(height: 8), |
|
|
const SizedBox(height: 8), |
|
|
Text( |
|
|
Text( |
|
|
'Réf: $currentReference', |
|
|
'Réf: $currentReference', |
|
|
style: const TextStyle(fontSize: 10, color: Colors.grey), |
|
|
style: const TextStyle( |
|
|
|
|
|
fontSize: 10, color: Colors.grey), |
|
|
), |
|
|
), |
|
|
], |
|
|
], |
|
|
), |
|
|
), |
|
|
@ -852,12 +876,15 @@ Widget _buildImportProgressIndicator() { |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
// Générer une référence unique et vérifier son unicité |
|
|
// Générer une référence unique et vérifier son unicité |
|
|
String finalReference = currentReference ?? _generateUniqueReference(); |
|
|
String finalReference = |
|
|
var existingProduct = await _productDatabase.getProductByReference(finalReference); |
|
|
currentReference ?? _generateUniqueReference(); |
|
|
|
|
|
var existingProduct = await _productDatabase |
|
|
|
|
|
.getProductByReference(finalReference); |
|
|
|
|
|
|
|
|
while (existingProduct != null) { |
|
|
while (existingProduct != null) { |
|
|
finalReference = _generateUniqueReference(); |
|
|
finalReference = _generateUniqueReference(); |
|
|
existingProduct = await _productDatabase.getProductByReference(finalReference); |
|
|
existingProduct = await _productDatabase |
|
|
|
|
|
.getProductByReference(finalReference); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Générer le QR code |
|
|
// Générer le QR code |
|
|
@ -988,9 +1015,12 @@ Widget _buildImportProgressIndicator() { |
|
|
|
|
|
|
|
|
void _editProduct(Product product) { |
|
|
void _editProduct(Product product) { |
|
|
final nameController = TextEditingController(text: product.name); |
|
|
final nameController = TextEditingController(text: product.name); |
|
|
final priceController = TextEditingController(text: product.price.toString()); |
|
|
final priceController = |
|
|
final stockController = TextEditingController(text: product.stock.toString()); |
|
|
TextEditingController(text: product.price.toString()); |
|
|
final descriptionController = TextEditingController(text: product.description ?? ''); |
|
|
final stockController = |
|
|
|
|
|
TextEditingController(text: product.stock.toString()); |
|
|
|
|
|
final descriptionController = |
|
|
|
|
|
TextEditingController(text: product.description ?? ''); |
|
|
final imageController = TextEditingController(text: product.image); |
|
|
final imageController = TextEditingController(text: product.image); |
|
|
|
|
|
|
|
|
String selectedCategory = product.category; |
|
|
String selectedCategory = product.category; |
|
|
@ -1013,17 +1043,16 @@ Widget _buildImportProgressIndicator() { |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
const SizedBox(height: 16), |
|
|
const SizedBox(height: 16), |
|
|
|
|
|
|
|
|
TextField( |
|
|
TextField( |
|
|
controller: priceController, |
|
|
controller: priceController, |
|
|
keyboardType: const TextInputType.numberWithOptions(decimal: true), |
|
|
keyboardType: |
|
|
|
|
|
const TextInputType.numberWithOptions(decimal: true), |
|
|
decoration: const InputDecoration( |
|
|
decoration: const InputDecoration( |
|
|
labelText: 'Prix*', |
|
|
labelText: 'Prix*', |
|
|
border: OutlineInputBorder(), |
|
|
border: OutlineInputBorder(), |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
const SizedBox(height: 16), |
|
|
const SizedBox(height: 16), |
|
|
|
|
|
|
|
|
TextField( |
|
|
TextField( |
|
|
controller: stockController, |
|
|
controller: stockController, |
|
|
keyboardType: TextInputType.number, |
|
|
keyboardType: TextInputType.number, |
|
|
@ -1033,7 +1062,6 @@ Widget _buildImportProgressIndicator() { |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
const SizedBox(height: 16), |
|
|
const SizedBox(height: 16), |
|
|
|
|
|
|
|
|
StatefulBuilder( |
|
|
StatefulBuilder( |
|
|
builder: (context, setDialogState) { |
|
|
builder: (context, setDialogState) { |
|
|
return Column( |
|
|
return Column( |
|
|
@ -1053,11 +1081,14 @@ Widget _buildImportProgressIndicator() { |
|
|
const SizedBox(width: 8), |
|
|
const SizedBox(width: 8), |
|
|
ElevatedButton( |
|
|
ElevatedButton( |
|
|
onPressed: () async { |
|
|
onPressed: () async { |
|
|
final result = await FilePicker.platform.pickFiles(type: FileType.image); |
|
|
final result = await FilePicker.platform |
|
|
if (result != null && result.files.single.path != null) { |
|
|
.pickFiles(type: FileType.image); |
|
|
|
|
|
if (result != null && |
|
|
|
|
|
result.files.single.path != null) { |
|
|
if (context.mounted) { |
|
|
if (context.mounted) { |
|
|
setDialogState(() { |
|
|
setDialogState(() { |
|
|
pickedImage = File(result.files.single.path!); |
|
|
pickedImage = |
|
|
|
|
|
File(result.files.single.path!); |
|
|
imageController.text = pickedImage!.path; |
|
|
imageController.text = pickedImage!.path; |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
@ -1068,7 +1099,6 @@ Widget _buildImportProgressIndicator() { |
|
|
], |
|
|
], |
|
|
), |
|
|
), |
|
|
const SizedBox(height: 16), |
|
|
const SizedBox(height: 16), |
|
|
|
|
|
|
|
|
if (pickedImage != null || product.image!.isNotEmpty) |
|
|
if (pickedImage != null || product.image!.isNotEmpty) |
|
|
Container( |
|
|
Container( |
|
|
height: 100, |
|
|
height: 100, |
|
|
@ -1082,16 +1112,19 @@ Widget _buildImportProgressIndicator() { |
|
|
child: pickedImage != null |
|
|
child: pickedImage != null |
|
|
? Image.file(pickedImage!, fit: BoxFit.cover) |
|
|
? Image.file(pickedImage!, fit: BoxFit.cover) |
|
|
: (product.image!.isNotEmpty |
|
|
: (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 Icon(Icons.image, size: 50)), |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
const SizedBox(height: 16), |
|
|
const SizedBox(height: 16), |
|
|
|
|
|
|
|
|
DropdownButtonFormField<String>( |
|
|
DropdownButtonFormField<String>( |
|
|
value: selectedCategory, |
|
|
value: selectedCategory, |
|
|
items: _categories.skip(1).map((category) => |
|
|
items: _categories |
|
|
DropdownMenuItem(value: category, child: Text(category))).toList(), |
|
|
.skip(1) |
|
|
|
|
|
.map((category) => DropdownMenuItem( |
|
|
|
|
|
value: category, child: Text(category))) |
|
|
|
|
|
.toList(), |
|
|
onChanged: (value) { |
|
|
onChanged: (value) { |
|
|
if (context.mounted) { |
|
|
if (context.mounted) { |
|
|
setDialogState(() => selectedCategory = value!); |
|
|
setDialogState(() => selectedCategory = value!); |
|
|
@ -1103,7 +1136,6 @@ Widget _buildImportProgressIndicator() { |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
const SizedBox(height: 16), |
|
|
const SizedBox(height: 16), |
|
|
|
|
|
|
|
|
TextField( |
|
|
TextField( |
|
|
controller: descriptionController, |
|
|
controller: descriptionController, |
|
|
maxLines: 3, |
|
|
maxLines: 3, |
|
|
@ -1197,7 +1229,8 @@ Widget _buildImportProgressIndicator() { |
|
|
Get.snackbar('Erreur', 'Suppression échouée: $e'); |
|
|
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)), |
|
|
), |
|
|
), |
|
|
], |
|
|
], |
|
|
), |
|
|
), |
|
|
@ -1259,7 +1292,8 @@ Widget _buildImportProgressIndicator() { |
|
|
Row( |
|
|
Row( |
|
|
children: [ |
|
|
children: [ |
|
|
Container( |
|
|
Container( |
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), |
|
|
padding: const EdgeInsets.symmetric( |
|
|
|
|
|
horizontal: 8, vertical: 2), |
|
|
decoration: BoxDecoration( |
|
|
decoration: BoxDecoration( |
|
|
color: Colors.blue.shade100, |
|
|
color: Colors.blue.shade100, |
|
|
borderRadius: BorderRadius.circular(12), |
|
|
borderRadius: BorderRadius.circular(12), |
|
|
@ -1408,8 +1442,10 @@ Widget _buildImportProgressIndicator() { |
|
|
), |
|
|
), |
|
|
child: DropdownButton<String>( |
|
|
child: DropdownButton<String>( |
|
|
value: _selectedCategory, |
|
|
value: _selectedCategory, |
|
|
items: _categories.map((category) => |
|
|
items: _categories |
|
|
DropdownMenuItem(value: category, child: Text(category))).toList(), |
|
|
.map((category) => DropdownMenuItem( |
|
|
|
|
|
value: category, child: Text(category))) |
|
|
|
|
|
.toList(), |
|
|
onChanged: (value) { |
|
|
onChanged: (value) { |
|
|
setState(() { |
|
|
setState(() { |
|
|
_selectedCategory = value!; |
|
|
_selectedCategory = value!; |
|
|
@ -1439,7 +1475,8 @@ Widget _buildImportProgressIndicator() { |
|
|
color: Colors.grey, |
|
|
color: Colors.grey, |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
if (_searchController.text.isNotEmpty || _selectedCategory != 'Tous') |
|
|
if (_searchController.text.isNotEmpty || |
|
|
|
|
|
_selectedCategory != 'Tous') |
|
|
TextButton.icon( |
|
|
TextButton.icon( |
|
|
onPressed: () { |
|
|
onPressed: () { |
|
|
setState(() { |
|
|
setState(() { |
|
|
|