import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:youmazgestion/Components/QrScan.dart'; import 'package:youmazgestion/Components/app_bar.dart'; import 'package:youmazgestion/Components/appDrawer.dart'; import 'package:youmazgestion/Models/client.dart'; import 'package:youmazgestion/Models/users.dart'; import 'package:youmazgestion/Models/produit.dart'; import 'package:youmazgestion/Services/stock_managementDatabase.dart'; import 'package:youmazgestion/Views/historique.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return GetMaterialApp( title: 'Youmaz Gestion', theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: const MainLayout(), debugShowCheckedModeBanner: false, ); } } class MainLayout extends StatefulWidget { const MainLayout({super.key}); @override State createState() => _MainLayoutState(); } class _MainLayoutState extends State { int _currentIndex = 1; // Index par défaut pour la page de commande final List _pages = [ const HistoriquePage(), const NouvelleCommandePage(), // Page 1 - Nouvelle commande const ScanQRPage(), // Page 2 - Scan QR ]; @override Widget build(BuildContext context) { return Scaffold( appBar: _currentIndex == 1 ? CustomAppBar(title: 'Nouvelle Commande') : null, drawer: CustomDrawer(), body: _pages[_currentIndex], bottomNavigationBar: _buildAdaptiveBottomNavBar(), ); } Widget _buildAdaptiveBottomNavBar() { final isDesktop = MediaQuery.of(context).size.width > 600; return Container( decoration: BoxDecoration( border: isDesktop ? const Border(top: BorderSide(color: Colors.grey, width: 0.5)) : null, ), child: BottomNavigationBar( currentIndex: _currentIndex, onTap: (index) { setState(() { _currentIndex = index; }); }, // Style adapté pour desktop type: isDesktop ? BottomNavigationBarType.fixed : BottomNavigationBarType.fixed, selectedFontSize: isDesktop ? 14 : 12, unselectedFontSize: isDesktop ? 14 : 12, iconSize: isDesktop ? 28 : 24, items: const [ BottomNavigationBarItem( icon: Icon(Icons.history), label: 'Historique', ), BottomNavigationBarItem( icon: Icon(Icons.add_shopping_cart), label: 'Commande', ), BottomNavigationBarItem( icon: Icon(Icons.qr_code_scanner), label: 'Scan QR', ), ], ), ); } } // Votre code existant pour NouvelleCommandePage reste inchangé class NouvelleCommandePage extends StatefulWidget { const NouvelleCommandePage({super.key}); @override _NouvelleCommandePageState createState() => _NouvelleCommandePageState(); } class _NouvelleCommandePageState extends State { final AppDatabase _appDatabase = AppDatabase.instance; final _formKey = GlobalKey(); bool _isLoading = false; // Contrôleurs client final TextEditingController _nomController = TextEditingController(); final TextEditingController _prenomController = TextEditingController(); final TextEditingController _emailController = TextEditingController(); final TextEditingController _telephoneController = TextEditingController(); final TextEditingController _adresseController = TextEditingController(); // Panier final List _products = []; final Map _quantites = {}; // Utilisateurs commerciaux List _commercialUsers = []; Users? _selectedCommercialUser; @override void initState() { super.initState(); _loadProducts(); _loadCommercialUsers(); } Future _loadProducts() async { final products = await _appDatabase.getProducts(); setState(() { _products.addAll(products); }); } Future _loadCommercialUsers() async { final commercialUsers = await _appDatabase.getCommercialUsers(); setState(() { _commercialUsers = commercialUsers; if (_commercialUsers.isNotEmpty) { _selectedCommercialUser = _commercialUsers.first; } }); } @override Widget build(BuildContext context) { return Scaffold( floatingActionButton: _buildFloatingCartButton(), drawer: MediaQuery.of(context).size.width > 600 ? null : CustomDrawer(), body: Column( children: [ // Header Container( padding: const EdgeInsets.all(16.0), decoration: BoxDecoration( gradient: LinearGradient( colors: [Colors.blue.shade800, Colors.blue.shade600], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 6, offset: const Offset(0, 2), ), ], ), child: Column( children: [ Row( children: [ Container( width: 50, height: 50, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), ), child: const Icon( Icons.shopping_cart, color: Colors.blue, size: 30, ), ), const SizedBox(width: 12), const Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Nouvelle Commande', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: Colors.white, ), ), Text( 'Créez une nouvelle commande pour un client', style: TextStyle( fontSize: 14, color: Colors.white70, ), ), ], ), ), ], ), ], ), ), // Contenu principal Expanded( child: SingleChildScrollView( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ ElevatedButton( style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), backgroundColor: Colors.blue.shade800, foregroundColor: Colors.white, ), onPressed: _showClientFormDialog, child: const Text('Ajouter les informations client'), ), const SizedBox(height: 20), _buildProductList(), ], ), ), ), ], ), ); } Widget _buildFloatingCartButton() { return FloatingActionButton.extended( onPressed: () { _showCartBottomSheet(); }, icon: const Icon(Icons.shopping_cart), label: Text('Panier (${_quantites.values.where((q) => q > 0).length})'), backgroundColor: Colors.blue.shade800, foregroundColor: Colors.white, ); } void _showClientFormDialog() { Get.dialog( AlertDialog( title: const Text('Informations Client'), content: SingleChildScrollView( child: Form( key: _formKey, child: Column( mainAxisSize: MainAxisSize.min, children: [ _buildTextFormField( controller: _nomController, label: 'Nom', validator: (value) => value?.isEmpty ?? true ? 'Veuillez entrer un nom' : null, ), const SizedBox(height: 12), _buildTextFormField( controller: _prenomController, label: 'Prénom', validator: (value) => value?.isEmpty ?? true ? 'Veuillez entrer un prénom' : null, ), const SizedBox(height: 12), _buildTextFormField( controller: _emailController, label: 'Email', keyboardType: TextInputType.emailAddress, validator: (value) { if (value?.isEmpty ?? true) return 'Veuillez entrer un email'; if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value!)) { return 'Email invalide'; } return null; }, ), const SizedBox(height: 12), _buildTextFormField( controller: _telephoneController, label: 'Téléphone', keyboardType: TextInputType.phone, validator: (value) => value?.isEmpty ?? true ? 'Veuillez entrer un téléphone' : null, ), const SizedBox(height: 12), _buildTextFormField( controller: _adresseController, label: 'Adresse', maxLines: 2, validator: (value) => value?.isEmpty ?? true ? 'Veuillez entrer une adresse' : null, ), const SizedBox(height: 12), _buildCommercialDropdown(), ], ), ), ), actions: [ TextButton( onPressed: () => Get.back(), child: const Text('Annuler'), ), ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.blue.shade800, foregroundColor: Colors.white, ), onPressed: () { if (_formKey.currentState!.validate()) { Get.back(); Get.snackbar( 'Succès', 'Informations client enregistrées', snackPosition: SnackPosition.BOTTOM, backgroundColor: Colors.green, colorText: Colors.white, ); } }, child: const Text('Enregistrer'), ), ], ), ); } Widget _buildTextFormField({ required TextEditingController controller, required String label, TextInputType? keyboardType, String? Function(String?)? validator, int? maxLines, }) { return TextFormField( controller: controller, decoration: InputDecoration( labelText: label, border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: Colors.grey.shade400), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: Colors.grey.shade400), ), filled: true, fillColor: Colors.white, contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), ), keyboardType: keyboardType, validator: validator, maxLines: maxLines, ); } Widget _buildCommercialDropdown() { return DropdownButtonFormField( value: _selectedCommercialUser, decoration: InputDecoration( labelText: 'Commercial', border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), ), filled: true, fillColor: Colors.white, ), items: _commercialUsers.map((Users user) { return DropdownMenuItem( value: user, child: Text('${user.name} ${user.lastName}'), ); }).toList(), onChanged: (Users? newValue) { setState(() { _selectedCommercialUser = newValue; }); }, validator: (value) => value == null ? 'Veuillez sélectionner un commercial' : null, ); } Widget _buildProductList() { return Card( elevation: 4, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Produits Disponibles', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Color.fromARGB(255, 9, 56, 95), ), ), const SizedBox(height: 16), _products.isEmpty ? const Center(child: CircularProgressIndicator()) : ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: _products.length, itemBuilder: (context, index) { final product = _products[index]; final quantity = _quantites[product.id] ?? 0; return _buildProductListItem(product, quantity); }, ), ], ), ), ); } Widget _buildProductListItem(Product product, int quantity) { return Card( margin: const EdgeInsets.symmetric(vertical: 8), elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), child: ListTile( contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 8, ), leading: Container( width: 50, height: 50, decoration: BoxDecoration( color: Colors.blue.shade50, borderRadius: BorderRadius.circular(8), ), child: const Icon(Icons.shopping_bag, color: Colors.blue), ), title: Text( product.name, style: const TextStyle(fontWeight: FontWeight.bold), ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 4), Text( '${product.price.toStringAsFixed(2)} DA', style: TextStyle( color: Colors.green.shade700, fontWeight: FontWeight.w600, ), ), if (product.stock != null) Text( 'Stock: ${product.stock}', style: TextStyle( fontSize: 12, color: Colors.grey.shade600, ), ), ], ), trailing: Container( decoration: BoxDecoration( color: Colors.blue.shade50, borderRadius: BorderRadius.circular(20), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( icon: const Icon(Icons.remove, size: 18), onPressed: () { if (quantity > 0) { setState(() { _quantites[product.id!] = quantity - 1; }); } }, ), Text( quantity.toString(), style: const TextStyle(fontWeight: FontWeight.bold), ), IconButton( icon: const Icon(Icons.add, size: 18), onPressed: () { if (product.stock == null || quantity < product.stock!) { setState(() { _quantites[product.id!] = quantity + 1; }); } else { Get.snackbar( 'Stock insuffisant', 'Quantité demandée non disponible', snackPosition: SnackPosition.BOTTOM, backgroundColor: Colors.red, colorText: Colors.white, ); } }, ), ], ), ), ), ); } void _showCartBottomSheet() { Get.bottomSheet( Container( height: MediaQuery.of(context).size.height * 0.7, padding: const EdgeInsets.all(16), decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Votre Panier', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), IconButton( icon: const Icon(Icons.close), onPressed: () => Get.back(), ), ], ), const Divider(), Expanded(child: _buildCartItemsList()), const Divider(), _buildCartTotalSection(), const SizedBox(height: 16), _buildSubmitButton(), ], ), ), isScrollControlled: true, ); } Widget _buildCartItemsList() { final itemsInCart = _quantites.entries.where((e) => e.value > 0).toList(); if (itemsInCart.isEmpty) { return const Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.shopping_cart_outlined, size: 60, color: Colors.grey), SizedBox(height: 16), Text( 'Votre panier est vide', style: TextStyle(fontSize: 16, color: Colors.grey), ), ], ), ); } return ListView.builder( itemCount: itemsInCart.length, itemBuilder: (context, index) { final entry = itemsInCart[index]; final product = _products.firstWhere((p) => p.id == entry.key); return Dismissible( key: Key(entry.key.toString()), background: Container( color: Colors.red.shade100, alignment: Alignment.centerRight, padding: const EdgeInsets.only(right: 20), child: const Icon(Icons.delete, color: Colors.red), ), direction: DismissDirection.endToStart, onDismissed: (direction) { setState(() { _quantites.remove(entry.key); }); Get.snackbar( 'Produit retiré', '${product.name} a été retiré du panier', snackPosition: SnackPosition.BOTTOM, ); }, child: Card( margin: const EdgeInsets.only(bottom: 8), elevation: 1, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), child: ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), leading: Container( width: 40, height: 40, decoration: BoxDecoration( color: Colors.blue.shade50, borderRadius: BorderRadius.circular(8), ), child: const Icon(Icons.shopping_bag, size: 20), ), title: Text(product.name), subtitle: Text('${entry.value} x ${product.price.toStringAsFixed(2)} DA'), trailing: Text( '${(entry.value * product.price).toStringAsFixed(2)} DA', style: TextStyle( fontWeight: FontWeight.bold, color: Colors.blue.shade800, ), ), ), ), ); }, ); } Widget _buildCartTotalSection() { double total = 0; _quantites.forEach((productId, quantity) { final product = _products.firstWhere((p) => p.id == productId); total += quantity * product.price; }); return Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Total:', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), Text( '${total.toStringAsFixed(2)} DA', style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.green, ), ), ], ), const SizedBox(height: 8), Text( '${_quantites.values.where((q) => q > 0).length} article(s)', style: TextStyle(color: Colors.grey.shade600), ), ], ); } Widget _buildSubmitButton() { return SizedBox( width: double.infinity, child: ElevatedButton( style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), backgroundColor: Colors.blue.shade800, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), elevation: 4, ), onPressed: _submitOrder, child: _isLoading ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white, ), ) : const Text( 'Valider la Commande', style: TextStyle(fontSize: 16), ), ), ); } Future _submitOrder() async { if (_nomController.text.isEmpty || _prenomController.text.isEmpty || _emailController.text.isEmpty || _telephoneController.text.isEmpty || _adresseController.text.isEmpty) { Get.back(); // Ferme le bottom sheet Get.snackbar( 'Informations manquantes', 'Veuillez remplir les informations client', snackPosition: SnackPosition.BOTTOM, backgroundColor: Colors.red, colorText: Colors.white, ); _showClientFormDialog(); return; } final itemsInCart = _quantites.entries.where((e) => e.value > 0).toList(); if (itemsInCart.isEmpty) { Get.snackbar( 'Panier vide', 'Veuillez ajouter des produits à votre commande', snackPosition: SnackPosition.BOTTOM, backgroundColor: Colors.red, colorText: Colors.white, ); return; } setState(() { _isLoading = true; }); // Créer le client final client = Client( nom: _nomController.text, prenom: _prenomController.text, email: _emailController.text, telephone: _telephoneController.text, adresse: _adresseController.text, dateCreation: DateTime.now(), ); // Calculer le total et préparer les détails double total = 0; final details = []; for (final entry in itemsInCart) { final product = _products.firstWhere((p) => p.id == entry.key); total += entry.value * product.price; details.add(DetailCommande( commandeId: 0, produitId: product.id!, quantite: entry.value, prixUnitaire: product.price, sousTotal: entry.value * product.price, )); } // Créer la commande final commande = Commande( clientId: 0, dateCommande: DateTime.now(), statut: StatutCommande.enAttente, montantTotal: total, notes: 'Commande passée via l\'application', commandeurId: _selectedCommercialUser?.id, ); 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.'), actions: [ TextButton( onPressed: () { Navigator.pop(context); // Réinitialiser le formulaire _nomController.clear(); _prenomController.clear(); _emailController.clear(); _telephoneController.clear(); _adresseController.clear(); setState(() { _quantites.clear(); _isLoading = false; }); }, child: const Text('OK'), ), ], ), ); } catch (e) { setState(() { _isLoading = false; }); Get.snackbar( 'Erreur', 'Une erreur est survenue: ${e.toString()}', snackPosition: SnackPosition.BOTTOM, backgroundColor: Colors.red, colorText: Colors.white, ); } } @override void dispose() { _nomController.dispose(); _prenomController.dispose(); _emailController.dispose(); _telephoneController.dispose(); _adresseController.dispose(); super.dispose(); } }