You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
527 lines
22 KiB
527 lines
22 KiB
import 'package:intl/intl.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:youmazgestion/Services/stock_managementDatabase.dart';
|
|
import 'package:youmazgestion/Views/particles.dart' show ParticleBackground;
|
|
import 'package:youmazgestion/Views/produitsCard.dart';
|
|
import 'Components/appDrawer.dart';
|
|
import 'Components/app_bar.dart';
|
|
import 'Components/cartItem.dart';
|
|
import 'Models/produit.dart';
|
|
import 'Services/OrderDatabase.dart';
|
|
//import 'Services/productDatabase.dart';
|
|
import 'Views/ticketPage.dart';
|
|
import 'controller/userController.dart';
|
|
import 'my_app.dart';
|
|
import 'Services/workDatabase.dart';
|
|
|
|
class AccueilPage extends StatefulWidget {
|
|
const AccueilPage({super.key});
|
|
|
|
@override
|
|
_AccueilPageState createState() => _AccueilPageState();
|
|
}
|
|
|
|
class _AccueilPageState extends State<AccueilPage> {
|
|
final UserController userController = Get.put(UserController());
|
|
final AppDatabase productDatabase = AppDatabase.instance;
|
|
late Future<Map<String, List<Product>>> productsFuture;
|
|
final OrderDatabase orderDatabase = OrderDatabase.instance;
|
|
final WorkDatabase workDatabase = WorkDatabase.instance;
|
|
String? username;
|
|
String? role;
|
|
DateTime? startDate;
|
|
|
|
int orderId = 0;
|
|
List<CartItem> selectedProducts = [];
|
|
int selectedQuantity = 1;
|
|
double totalCartPrice = 0;
|
|
double amountPaid = 0;
|
|
final TextEditingController _amountController = TextEditingController();
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_initializeDatabases();
|
|
loadUserData();
|
|
productsFuture = _initDatabaseAndFetchProducts();
|
|
}
|
|
|
|
Future<void> _initializeDatabases() async {
|
|
await orderDatabase.initDatabase();
|
|
await workDatabase.initDatabase();
|
|
await _initializeRegister();
|
|
}
|
|
|
|
Future<void> _initializeRegister() async {
|
|
if (!MyApp.isRegisterOpen) {
|
|
setState(() {
|
|
MyApp.isRegisterOpen = true;
|
|
String formattedDate = DateFormat('yyyy-MM-dd').format(DateTime.now());
|
|
startDate = DateFormat('yyyy-MM-dd').parse(formattedDate);
|
|
MyApp.startDate = startDate;
|
|
workDatabase.insertDate(formattedDate);
|
|
});
|
|
}
|
|
}
|
|
|
|
Future<void> loadUserData() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
setState(() {
|
|
username = prefs.getString('username') ?? 'Nom inconnu';
|
|
role = prefs.getString('role') ?? 'Rôle inconnu';
|
|
});
|
|
}
|
|
|
|
Future<void> saveOrderToDatabase() async {
|
|
final totalPrice = calculateTotalPrice();
|
|
final dateTime = DateTime.now().toString();
|
|
String user = userController.username;
|
|
|
|
if (selectedProducts.isEmpty) {
|
|
Get.snackbar(
|
|
'Panier vide',
|
|
'Ajoutez des produits avant de passer commande.',
|
|
snackPosition: SnackPosition.BOTTOM,
|
|
duration: const Duration(seconds: 3),
|
|
backgroundColor: Colors.red,
|
|
colorText: Colors.white,
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (amountPaid < totalPrice) {
|
|
Get.snackbar(
|
|
'Paiement incomplet',
|
|
'Le montant payé est insuffisant.',
|
|
snackPosition: SnackPosition.BOTTOM,
|
|
duration: const Duration(seconds: 3),
|
|
backgroundColor: Colors.red,
|
|
colorText: Colors.white,
|
|
);
|
|
return;
|
|
}
|
|
|
|
orderId = await orderDatabase.insertOrder(
|
|
totalPrice, dateTime, MyApp.startDate!, user);
|
|
|
|
for (final cartItem in selectedProducts) {
|
|
final product = cartItem.product;
|
|
final quantity = cartItem.quantity;
|
|
final price = product.price * quantity;
|
|
|
|
await orderDatabase.insertOrderItem(
|
|
orderId, product.name, quantity, price);
|
|
|
|
final updatedStock = product.stock - quantity;
|
|
await productDatabase.updateStock(product.id!, updatedStock);
|
|
}
|
|
|
|
// Afficher le ticket et réinitialiser le panier
|
|
showTicketPage();
|
|
setState(() {
|
|
selectedProducts.clear();
|
|
_amountController.clear();
|
|
amountPaid = 0;
|
|
});
|
|
}
|
|
|
|
Future<Map<String, List<Product>>> _initDatabaseAndFetchProducts() async {
|
|
await productDatabase.initDatabase();
|
|
final categories = await productDatabase.getCategories();
|
|
final productsByCategory = <String, List<Product>>{};
|
|
|
|
categories.sort();
|
|
|
|
for (final categoryName in categories) {
|
|
final products =
|
|
await productDatabase.getProductsByCategory(categoryName);
|
|
productsByCategory[categoryName] = products;
|
|
}
|
|
|
|
return productsByCategory;
|
|
}
|
|
|
|
double calculateTotalPrice() {
|
|
double totalPrice = 0;
|
|
for (final cartItem in selectedProducts) {
|
|
totalPrice += cartItem.product.price * cartItem.quantity;
|
|
}
|
|
return totalPrice;
|
|
}
|
|
|
|
void addToCartWithDetails(Product product, int quantity) {
|
|
setState(() {
|
|
final existingCartItem = selectedProducts.firstWhere(
|
|
(cartItem) => cartItem.product.id == product.id,
|
|
orElse: () => CartItem(product, 0),
|
|
);
|
|
if (existingCartItem.quantity == 0) {
|
|
selectedProducts.add(CartItem(product, quantity));
|
|
} else {
|
|
existingCartItem.quantity += quantity;
|
|
}
|
|
});
|
|
|
|
Get.snackbar(
|
|
'Produit ajouté',
|
|
'${product.name} (x$quantity) ajouté au panier',
|
|
snackPosition: SnackPosition.TOP,
|
|
duration: const Duration(seconds: 1),
|
|
backgroundColor: Colors.green,
|
|
colorText: Colors.white,
|
|
);
|
|
}
|
|
|
|
void showTicketPage() {
|
|
Get.offAll(TicketPage(
|
|
businessName: 'Youmaz',
|
|
businessAddress: 'quartier escale, Diourbel, Sénégal, en face de Sonatel',
|
|
businessPhoneNumber: '77 446 92 68',
|
|
cartItems: selectedProducts,
|
|
totalCartPrice: calculateTotalPrice(),
|
|
amountPaid: amountPaid,
|
|
));
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: CustomAppBar(
|
|
title: "Accueil",
|
|
subtitle: Text('Bienvenue $username ! (Rôle: $role)',
|
|
style: const TextStyle(color: Colors.white70, fontSize: 14)),
|
|
),
|
|
drawer: CustomDrawer(),
|
|
body: ParticleBackground(
|
|
child: Container(
|
|
decoration: const BoxDecoration(
|
|
gradient: LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [Colors.white, Color.fromARGB(255, 4, 54, 95)]),
|
|
),
|
|
child: FutureBuilder<Map<String, List<Product>>>(
|
|
future: productsFuture,
|
|
builder: (context, snapshot) {
|
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
|
return const Center(
|
|
child: CircularProgressIndicator(
|
|
valueColor: AlwaysStoppedAnimation<Color>(
|
|
Color.fromARGB(255, 4, 54, 95),
|
|
),
|
|
));
|
|
} else if (snapshot.hasError) {
|
|
return const Center(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Icon(Icons.error, color: Colors.red, size: 48),
|
|
SizedBox(height: 16),
|
|
Text("Erreur de chargement des produits",
|
|
style: TextStyle(fontSize: 16, color: Colors.white)),
|
|
],
|
|
));
|
|
} else if (snapshot.hasData) {
|
|
final productsByCategory = snapshot.data!;
|
|
final categories = productsByCategory.keys.toList();
|
|
|
|
return Row(
|
|
children: [
|
|
// Section produits
|
|
Expanded(
|
|
flex: 3,
|
|
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,
|
|
itemBuilder: (context, index) {
|
|
final category = categories[index];
|
|
final products = productsByCategory[category]!;
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Container(
|
|
margin:
|
|
const EdgeInsets.symmetric(vertical: 8),
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: Color.fromARGB(255, 4, 54, 95),
|
|
borderRadius: BorderRadius.circular(12),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.1),
|
|
blurRadius: 4,
|
|
offset: Offset(0, 2),
|
|
)
|
|
],
|
|
),
|
|
child: Center(
|
|
child: Text(
|
|
category,
|
|
style: const TextStyle(
|
|
fontSize: 22,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
GridView.builder(
|
|
gridDelegate:
|
|
const SliverGridDelegateWithFixedCrossAxisCount(
|
|
crossAxisCount: 4,
|
|
childAspectRatio: 0.9,
|
|
crossAxisSpacing: 8,
|
|
mainAxisSpacing: 8,
|
|
),
|
|
shrinkWrap: true,
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
itemCount: products.length,
|
|
itemBuilder: (context, index) {
|
|
final product = products[index];
|
|
return ProductCard(
|
|
product: product,
|
|
onAddToCart: (product, quantity) {
|
|
addToCartWithDetails(product, quantity);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
),
|
|
|
|
// Section panier
|
|
),
|
|
Expanded(
|
|
flex: 1,
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
color: Colors.grey[200],
|
|
borderRadius: const BorderRadius.only(
|
|
topLeft: Radius.circular(20),
|
|
),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.2),
|
|
blurRadius: 10,
|
|
spreadRadius: 2,
|
|
),
|
|
],
|
|
),
|
|
padding: EdgeInsets.all(16),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
Container(
|
|
padding: EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: Color.fromARGB(255, 4, 54, 95),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: const Text(
|
|
'Panier',
|
|
style: TextStyle(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.white,
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
),
|
|
SizedBox(height: 16),
|
|
|
|
// Liste des produits dans le panier
|
|
Expanded(
|
|
child: selectedProducts.isEmpty
|
|
? const Center(
|
|
child: Column(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.center,
|
|
children: [
|
|
Icon(Icons.shopping_cart,
|
|
size: 48, color: Colors.grey),
|
|
SizedBox(height: 16),
|
|
Text(
|
|
"Votre panier est vide",
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
color: Colors.grey),
|
|
),
|
|
],
|
|
),
|
|
)
|
|
: ListView.builder(
|
|
itemCount: selectedProducts.length,
|
|
itemBuilder: (context, index) {
|
|
final cartItem =
|
|
selectedProducts[index];
|
|
return Card(
|
|
margin:
|
|
EdgeInsets.symmetric(vertical: 4),
|
|
shape: RoundedRectangleBorder(
|
|
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),
|
|
),
|
|
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),
|
|
),
|
|
trailing: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(
|
|
'${NumberFormat('#,##0').format(cartItem.product.price * cartItem.quantity)}',
|
|
style: const TextStyle(
|
|
fontWeight:
|
|
FontWeight.bold),
|
|
),
|
|
const SizedBox(width: 8),
|
|
IconButton(
|
|
icon: const Icon(Icons.delete,
|
|
color: Colors.red),
|
|
onPressed: () {
|
|
setState(() {
|
|
selectedProducts
|
|
.removeAt(index);
|
|
});
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
|
|
// Total et paiement
|
|
Container(
|
|
padding: EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(12),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.1),
|
|
blurRadius: 4,
|
|
offset: Offset(0, 2),
|
|
)
|
|
],
|
|
),
|
|
child: Column(
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
const Text('Total:',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold)),
|
|
Text(
|
|
'${NumberFormat('#,##0.00').format(calculateTotalPrice())} MGA',
|
|
style: TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
color: Color.fromARGB(255, 4, 54, 95),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 12),
|
|
TextField(
|
|
controller: _amountController,
|
|
keyboardType: TextInputType.number,
|
|
decoration: InputDecoration(
|
|
labelText: 'Montant payé',
|
|
prefixIcon: Icon(Icons.attach_money),
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
filled: true,
|
|
fillColor: Colors.grey[100],
|
|
),
|
|
onChanged: (value) {
|
|
setState(() {
|
|
amountPaid =
|
|
double.tryParse(value) ?? 0;
|
|
});
|
|
},
|
|
),
|
|
SizedBox(height: 16),
|
|
ElevatedButton.icon(
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Colors.green,
|
|
padding:
|
|
EdgeInsets.symmetric(vertical: 16),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
),
|
|
onPressed: saveOrderToDatabase,
|
|
icon: const Icon(Icons.check_circle),
|
|
label: const Text(
|
|
'Valider la commande',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.white),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
)
|
|
],
|
|
);
|
|
} else {
|
|
return const Center(
|
|
child: Text("Aucun produit disponible",
|
|
style: TextStyle(color: Colors.white)),
|
|
);
|
|
}
|
|
},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_amountController.dispose();
|
|
super.dispose();
|
|
}
|
|
}
|
|
|