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.
 
 
 
 
 
 

1329 lines
49 KiB

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:path_provider/path_provider.dart';
import 'package:open_file/open_file.dart';
import 'package:youmazgestion/Components/app_bar.dart';
import 'package:youmazgestion/Components/appDrawer.dart';
import 'package:youmazgestion/Models/client.dart';
import 'package:youmazgestion/Services/productDatabase.dart';
class GestionCommandesPage extends StatefulWidget {
const GestionCommandesPage({super.key});
@override
_GestionCommandesPageState createState() => _GestionCommandesPageState();
}
class _GestionCommandesPageState extends State<GestionCommandesPage> {
final ProductDatabase _database = ProductDatabase.instance;
List<Commande> _commandes = [];
List<Commande> _filteredCommandes = [];
StatutCommande? _selectedStatut;
DateTime? _selectedDate;
final TextEditingController _searchController = TextEditingController();
bool _showCancelledOrders = false; // Nouveau: contrôle l'affichage des commandes annulées
@override
void initState() {
super.initState();
_loadCommandes();
_searchController.addListener(_filterCommandes);
}
Future<void> _loadCommandes() async {
final commandes = await _database.getCommandes();
setState(() {
_commandes = commandes;
_filterCommandes();
});
}
Future<Uint8List> loadImage() async {
final data = await rootBundle.load('assets/youmaz2.png');
return data.buffer.asUint8List();
}
void _filterCommandes() {
final query = _searchController.text.toLowerCase();
setState(() {
_filteredCommandes = _commandes.where((commande) {
final matchesSearch = commande.clientNomComplet.toLowerCase().contains(query) ||
commande.id.toString().contains(query);
final matchesStatut = _selectedStatut == null || commande.statut == _selectedStatut;
final matchesDate = _selectedDate == null ||
DateFormat('yyyy-MM-dd').format(commande.dateCommande) ==
DateFormat('yyyy-MM-dd').format(_selectedDate!);
// Nouveau: filtrer les commandes annulées selon le toggle
final shouldShowCancelled = _showCancelledOrders || commande.statut != StatutCommande.annulee;
return matchesSearch && matchesStatut && matchesDate && shouldShowCancelled;
}).toList();
});
}
Future<void> _updateStatut(int commandeId, StatutCommande newStatut) async {
await _database.updateStatutCommande(commandeId, newStatut);
await _loadCommandes();
// Amélioration: message plus spécifique selon le statut
String message = 'Statut de la commande mis à jour';
Color backgroundColor = Colors.green;
switch (newStatut) {
case StatutCommande.annulee:
message = 'Commande annulée avec succès';
backgroundColor = Colors.orange;
break;
case StatutCommande.livree:
message = 'Commande marquée comme livrée';
backgroundColor = Colors.green;
break;
case StatutCommande.confirmee:
message = 'Commande confirmée';
backgroundColor = Colors.blue;
break;
default:
break;
}
Get.snackbar(
'Succès',
message,
snackPosition: SnackPosition.BOTTOM,
backgroundColor: backgroundColor,
colorText: Colors.white,
duration: const Duration(seconds: 2),
);
}
Future<void> _generateInvoice(Commande commande) async {
final details = await _database.getDetailsCommande(commande.id!);
final client = await _database.getClientById(commande.clientId);
final pdf = pw.Document();
final imageBytes = await loadImage(); // Charge les données de l'image
final image = pw.MemoryImage(imageBytes);
// Amélioration: styles plus professionnels
final headerStyle = pw.TextStyle(
fontSize: 18,
fontWeight: pw.FontWeight.bold,
color: PdfColors.blue900,
);
final titleStyle = pw.TextStyle(
fontSize: 14,
fontWeight: pw.FontWeight.bold,
);
final subtitleStyle = pw.TextStyle(
fontSize: 12,
color: PdfColors.grey600,
);
// Contenu du PDF amélioré
pdf.addPage(
pw.Page(
margin: const pw.EdgeInsets.all(20),
build: (pw.Context context) {
return pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
// En-tête avec logo (si disponible)
pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
// Placeholder pour le logo - à remplacer par votre logo
pw.Container(
width: 100,
height: 80,
decoration: pw.BoxDecoration(
border: pw.Border.all(color: PdfColors.blue900, width: 2),
borderRadius: pw.BorderRadius.circular(8),
),
child: pw.Center(
child: pw.Image(image)
),
),
pw.SizedBox(height: 10),
pw.Text('guycom', style: headerStyle),
pw.Text('123 Rue des Entreprises', style: subtitleStyle),
pw.Text('Antananarivo, Madagascar', style: subtitleStyle),
pw.Text('Tél: +213 123 456 789', style: subtitleStyle),
pw.Text('Site: guycom.mg', style: subtitleStyle),
],
),
pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.end,
children: [
pw.Container(
padding: const pw.EdgeInsets.all(12),
decoration: pw.BoxDecoration(
color: PdfColors.blue50,
borderRadius: pw.BorderRadius.circular(8),
),
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Text('FACTURE',
style: pw.TextStyle(
fontSize: 20,
fontWeight: pw.FontWeight.bold,
color: PdfColors.blue900,
),
),
pw.SizedBox(height: 8),
pw.Text('N°: ${commande.id}', style: titleStyle),
pw.Text('Date: ${DateFormat('dd/MM/yyyy').format(commande.dateCommande)}'),
],
),
),
],
),
],
),
pw.SizedBox(height: 30),
// Informations client
pw.Container(
width: double.infinity,
padding: const pw.EdgeInsets.all(12),
decoration: pw.BoxDecoration(
color: PdfColors.grey100,
borderRadius: pw.BorderRadius.circular(8),
),
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Text('FACTURÉ À:', style: titleStyle),
pw.SizedBox(height: 5),
pw.Text(client?.nomComplet ?? 'Client inconnu',
style: pw.TextStyle(fontSize: 12)),
if (client?.telephone != null)
pw.Text('Tél: ${client!.telephone}',
style: pw.TextStyle(fontSize: 10, color: PdfColors.grey600)),
],
),
),
pw.SizedBox(height: 30),
// Tableau des produits
pw.Text('DÉTAILS DE LA COMMANDE', style: titleStyle),
pw.SizedBox(height: 10),
pw.Table(
border: pw.TableBorder.all(color: PdfColors.grey400, width: 0.5),
children: [
pw.TableRow(
decoration: const pw.BoxDecoration(color: PdfColors.blue900),
children: [
_buildTableCell('Produit', titleStyle.copyWith(color: PdfColors.white)),
_buildTableCell('Qté', titleStyle.copyWith(color: PdfColors.white)),
_buildTableCell('Prix unit.', titleStyle.copyWith(color: PdfColors.white)),
_buildTableCell('Total', titleStyle.copyWith(color: PdfColors.white)),
],
),
...details.asMap().entries.map((entry) {
final index = entry.key;
final detail = entry.value;
final isEven = index % 2 == 0;
return pw.TableRow(
decoration: pw.BoxDecoration(
color: isEven ? PdfColors.white : PdfColors.grey50,
),
children: [
_buildTableCell(detail.produitNom ?? 'Produit inconnu'),
_buildTableCell(detail.quantite.toString()),
_buildTableCell('${detail.prixUnitaire.toStringAsFixed(2)} DA'),
_buildTableCell('${detail.sousTotal.toStringAsFixed(2)} DA'),
],
);
}),
],
),
pw.SizedBox(height: 20),
// Total
pw.Container(
alignment: pw.Alignment.centerRight,
child: pw.Container(
padding: const pw.EdgeInsets.all(12),
decoration: pw.BoxDecoration(
color: PdfColors.blue900,
borderRadius: pw.BorderRadius.circular(8),
),
child: pw.Text(
'TOTAL: ${commande.montantTotal.toStringAsFixed(2)} DA',
style: pw.TextStyle(
fontSize: 16,
fontWeight: pw.FontWeight.bold,
color: PdfColors.white,
),
),
),
),
pw.Spacer(),
// Pied de page
pw.Container(
width: double.infinity,
padding: const pw.EdgeInsets.all(12),
decoration: pw.BoxDecoration(
border: pw.Border(
top: pw.BorderSide(color: PdfColors.grey400, width: 1),
),
),
child: pw.Column(
children: [
pw.Text(
'Merci pour votre confiance!',
style: pw.TextStyle(
fontSize: 14,
fontStyle: pw.FontStyle.italic,
color: PdfColors.blue900,
),
),
pw.SizedBox(height: 5),
pw.Text(
'Cette facture est générée automatiquement par le système Youmaz Gestion',
style: pw.TextStyle(fontSize: 8, color: PdfColors.grey600),
),
],
),
),
],
);
},
),
);
// Sauvegarder le PDF
final output = await getTemporaryDirectory();
final file = File('${output.path}/facture_${commande.id}.pdf');
await file.writeAsBytes(await pdf.save());
// Ouvrir le PDF
await OpenFile.open(file.path);
}
pw.Widget _buildTableCell(String text, [pw.TextStyle? style]) {
return pw.Padding(
padding: const pw.EdgeInsets.all(8.0),
child: pw.Text(text, style: style ?? pw.TextStyle(fontSize: 10)),
);
}
Color _getStatutColor(StatutCommande statut) {
switch (statut) {
case StatutCommande.enAttente:
return Colors.orange.shade100;
case StatutCommande.confirmee:
return Colors.blue.shade100;
case StatutCommande.enPreparation:
return Colors.amber.shade100;
case StatutCommande.expediee:
return Colors.purple.shade100;
case StatutCommande.livree:
return Colors.green.shade100;
case StatutCommande.annulee:
return Colors.red.shade100;
}
}
IconData _getStatutIcon(StatutCommande statut) {
switch (statut) {
case StatutCommande.enAttente:
return Icons.schedule;
case StatutCommande.confirmee:
return Icons.check_circle_outline;
case StatutCommande.enPreparation:
return Icons.settings;
case StatutCommande.expediee:
return Icons.local_shipping;
case StatutCommande.livree:
return Icons.check_circle;
case StatutCommande.annulee:
return Icons.cancel;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const CustomAppBar(title: 'Gestion des Commandes'),
drawer: CustomDrawer(),
body: Column(
children: [
// Header avec logo et statistiques
Container(
padding: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue.shade50, Colors.white],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
child: Column(
children: [
// Logo et titre
Row(
children: [
// Logo de l'entreprise
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.asset(
'assets/logo.png', // Remplacez par le chemin de votre logo
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Container(
decoration: BoxDecoration(
color: Colors.blue.shade900,
borderRadius: BorderRadius.circular(8),
),
child: const Icon(
Icons.business,
color: Colors.white,
size: 30,
),
);
},
),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Gestion des Commandes',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
Text(
'${_filteredCommandes.length} commande(s) affichée(s)',
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade600,
),
),
],
),
),
],
),
const SizedBox(height: 16),
// Barre de recherche améliorée
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: TextField(
controller: _searchController,
decoration: InputDecoration(
labelText: 'Rechercher par client ou numéro de commande',
prefixIcon: Icon(Icons.search, color: Colors.blue.shade800),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
filled: true,
fillColor: Colors.white,
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 12,
),
),
),
),
const SizedBox(height: 16),
// Filtres améliorés
Row(
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: DropdownButtonFormField<StatutCommande>(
value: _selectedStatut,
decoration: InputDecoration(
labelText: 'Filtrer par statut',
prefixIcon: Icon(Icons.filter_list, color: Colors.blue.shade600),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
filled: true,
fillColor: Colors.white,
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 12,
),
),
items: [
const DropdownMenuItem<StatutCommande>(
value: null,
child: Text('Tous les statuts'),
),
...StatutCommande.values.map((statut) {
return DropdownMenuItem<StatutCommande>(
value: statut,
child: Row(
children: [
Icon(_getStatutIcon(statut), size: 16),
const SizedBox(width: 8),
Text(statutLibelle(statut)),
],
),
);
}),
],
onChanged: (value) {
setState(() {
_selectedStatut = value;
_filterCommandes();
});
},
),
),
),
const SizedBox(width: 12),
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: TextButton.icon(
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(
vertical: 16,
horizontal: 12,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
onPressed: () async {
final date = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2020),
lastDate: DateTime.now(),
builder: (context, child) {
return Theme(
data: Theme.of(context).copyWith(
colorScheme: ColorScheme.light(
primary: Colors.blue.shade900,
),
),
child: child!,
);
},
);
if (date != null) {
setState(() {
_selectedDate = date;
_filterCommandes();
});
}
},
icon: Icon(Icons.calendar_today, color: Colors.blue.shade600),
label: Text(
_selectedDate == null
? 'Date'
: DateFormat('dd/MM/yyyy').format(_selectedDate!),
style: const TextStyle(color: Colors.black87),
),
),
),
),
const SizedBox(width: 12),
// Bouton reset
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: IconButton(
icon: Icon(Icons.refresh, color: Colors.blue.shade600),
onPressed: () {
setState(() {
_selectedStatut = null;
_selectedDate = null;
_searchController.clear();
_filterCommandes();
});
},
tooltip: 'Réinitialiser les filtres',
),
),
],
),
const SizedBox(height: 12),
// Toggle pour afficher/masquer les commandes annulées
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
children: [
Icon(
Icons.visibility,
size: 20,
color: Colors.grey.shade600,
),
const SizedBox(width: 8),
Text(
'Afficher commandes annulées',
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade700,
),
),
const Spacer(),
Switch(
value: _showCancelledOrders,
onChanged: (value) {
setState(() {
_showCancelledOrders = value;
_filterCommandes();
});
},
activeColor: Colors.blue.shade600,
),
],
),
),
],
),
),
// Liste des commandes
Expanded(
child: _filteredCommandes.isEmpty
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.inbox,
size: 64,
color: Colors.grey.shade400,
),
const SizedBox(height: 16),
Text(
'Aucune commande trouvée',
style: TextStyle(
fontSize: 18,
color: Colors.grey.shade600,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 8),
Text(
'Essayez de modifier vos filtres',
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade500,
),
),
],
),
)
: ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 16),
itemCount: _filteredCommandes.length,
itemBuilder: (context, index) {
final commande = _filteredCommandes[index];
return Container(
margin: const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
color: _getStatutColor(commande.statut),
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: ExpansionTile(
tilePadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
leading: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(25),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 2,
offset: const Offset(0, 1),
),
],
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
_getStatutIcon(commande.statut),
size: 20,
color: commande.statut == StatutCommande.annulee
? Colors.red
: Colors.blue.shade600,
),
Text(
'#${commande.id}',
style: const TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
),
),
],
),
),
title: Text(
commande.clientNomComplet,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 4),
Row(
children: [
Icon(
Icons.calendar_today,
size: 14,
color: Colors.grey.shade600,
),
const SizedBox(width: 4),
Text(
DateFormat('dd/MM/yyyy').format(commande.dateCommande),
style: TextStyle(
fontSize: 12,
color: Colors.grey.shade600,
),
),
const SizedBox(width: 16),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 2,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: Text(
commande.statutLibelle,
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: commande.statut == StatutCommande.annulee
? Colors.red
: Colors.blue.shade700,
),
),
),
],
),
const SizedBox(height: 4),
Row(
children: [
Icon(
Icons.attach_money,
size: 14,
color: Colors.green.shade600,
),
const SizedBox(width: 4),
Text(
'${commande.montantTotal.toStringAsFixed(2)} DA',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.green.shade700,
),
),
],
),
],
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 2,
offset: const Offset(0, 1),
),
],
),
child: IconButton(
icon: Icon(
Icons.receipt_long,
color: Colors.blue.shade600,
),
onPressed: () => _generateInvoice(commande),
tooltip: 'Générer la facture',
),
),
],
),
children: [
Container(
padding: const EdgeInsets.all(16.0),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(12),
bottomRight: Radius.circular(12),
),
),
child: Column(
children: [
_CommandeDetails(commande: commande),
const SizedBox(height: 16),
if (commande.statut != StatutCommande.annulee)
_CommandeActions(
commande: commande,
onStatutChanged: _updateStatut,
),
],
),
),
],
),
);
},
),
),
],
),
);
}
String statutLibelle(StatutCommande statut) {
switch (statut) {
case StatutCommande.enAttente:
return 'En attente';
case StatutCommande.confirmee:
return 'Confirmée';
case StatutCommande.enPreparation:
return 'En préparation';
case StatutCommande.expediee:
return 'Expédiée';
case StatutCommande.livree:
return 'Livrée';
case StatutCommande.annulee:
return 'Annulée';
}
}
@override
void dispose() {
_searchController.dispose();
super.dispose();
}
}
class _CommandeDetails extends StatelessWidget {
final Commande commande;
const _CommandeDetails({required this.commande});
@override
Widget build(BuildContext context) {
return FutureBuilder<List<DetailCommande>>(
future: ProductDatabase.instance.getDetailsCommande(commande.id!),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (!snapshot.hasData || snapshot.data!.isEmpty) {
return const Text('Aucun détail disponible');
}
final details = snapshot.data!;
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(8),
),
child: const Text(
'Détails de la commande',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: Colors.black87,
),
),
),
const SizedBox(height: 12),
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.shade300),
borderRadius: BorderRadius.circular(8),
),
child: Table(
children: [
TableRow(
decoration: BoxDecoration(
color: Colors.grey.shade100,
),
children: [
_buildTableHeader('Produit'),
_buildTableHeader('Qté'),
_buildTableHeader('Prix unit.'),
_buildTableHeader('Total'),
],
),
...details.map((detail) => TableRow(
children: [
_buildTableCell(detail.produitNom ?? 'Produit inconnu'),
_buildTableCell('${detail.quantite}'),
_buildTableCell('${detail.prixUnitaire.toStringAsFixed(2)} DA'),
_buildTableCell('${detail.sousTotal.toStringAsFixed(2)} DA'),
],
)),
],
),
),
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.green.shade50,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.green.shade200),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'Total de la commande:',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
Text(
'${commande.montantTotal.toStringAsFixed(2)} DA',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
color: Colors.green.shade700,
),
),
],
),
),
],
);
},
);
}
Widget _buildTableHeader(String text) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
text,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
),
textAlign: TextAlign.center,
),
);
}
Widget _buildTableCell(String text) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
text,
style: const TextStyle(fontSize: 13),
textAlign: TextAlign.center,
),
);
}
}
class _CommandeActions extends StatelessWidget {
final Commande commande;
final Function(int, StatutCommande) onStatutChanged;
const _CommandeActions({
required this.commande,
required this.onStatutChanged,
});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.grey.shade50,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.grey.shade200),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text(
'Actions sur la commande',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
const SizedBox(height: 12),
Wrap(
spacing: 8,
runSpacing: 8,
children: _buildActionButtons(context),
),
],
),
);
}
List<Widget> _buildActionButtons(BuildContext context) {
List<Widget> buttons = [];
switch (commande.statut) {
case StatutCommande.enAttente:
buttons.addAll([
_buildActionButton(
label: 'Confirmer',
icon: Icons.check_circle,
color: Colors.blue,
onPressed: () => _showConfirmDialog(
context,
'Confirmer la commande',
'Êtes-vous sûr de vouloir confirmer cette commande?',
() => onStatutChanged(commande.id!, StatutCommande.confirmee),
),
),
_buildActionButton(
label: 'Annuler',
icon: Icons.cancel,
color: Colors.red,
onPressed: () => _showConfirmDialog(
context,
'Annuler la commande',
'Êtes-vous sûr de vouloir annuler cette commande?',
() => onStatutChanged(commande.id!, StatutCommande.annulee),
),
),
]);
break;
case StatutCommande.confirmee:
buttons.addAll([
_buildActionButton(
label: 'Préparer',
icon: Icons.settings,
color: Colors.amber,
onPressed: () => _showConfirmDialog(
context,
'Marquer en préparation',
'La commande va être marquée comme étant en cours de préparation.',
() => onStatutChanged(commande.id!, StatutCommande.enPreparation),
),
),
_buildActionButton(
label: 'Annuler',
icon: Icons.cancel,
color: Colors.red,
onPressed: () => _showConfirmDialog(
context,
'Annuler la commande',
'Êtes-vous sûr de vouloir annuler cette commande?',
() => onStatutChanged(commande.id!, StatutCommande.annulee),
),
),
]);
break;
case StatutCommande.enPreparation:
buttons.addAll([
_buildActionButton(
label: 'Expédier',
icon: Icons.local_shipping,
color: Colors.purple,
onPressed: () => _showConfirmDialog(
context,
'Expédier la commande',
'La commande va être marquée comme expédiée.',
() => onStatutChanged(commande.id!, StatutCommande.expediee),
),
),
_buildActionButton(
label: 'Annuler',
icon: Icons.cancel,
color: Colors.red,
onPressed: () => _showConfirmDialog(
context,
'Annuler la commande',
'Êtes-vous sûr de vouloir annuler cette commande?',
() => onStatutChanged(commande.id!, StatutCommande.annulee),
),
),
]);
break;
case StatutCommande.expediee:
buttons.add(
_buildActionButton(
label: 'Marquer livrée',
icon: Icons.check_circle,
color: Colors.green,
onPressed: () => _showConfirmDialog(
context,
'Marquer comme livrée',
'La commande va être marquée comme livrée.',
() => onStatutChanged(commande.id!, StatutCommande.livree),
),
),
);
break;
case StatutCommande.livree:
buttons.add(
Container(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
decoration: BoxDecoration(
color: Colors.green.shade100,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.green.shade300),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.check_circle, color: Colors.green.shade600, size: 16),
const SizedBox(width: 8),
Text(
'Commande livrée',
style: TextStyle(
color: Colors.green.shade700,
fontWeight: FontWeight.w600,
),
),
],
),
),
);
break;
case StatutCommande.annulee:
buttons.add(
Container(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
decoration: BoxDecoration(
color: Colors.red.shade100,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.red.shade300),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.cancel, color: Colors.red.shade600, size: 16),
const SizedBox(width: 8),
Text(
'Commande annulée',
style: TextStyle(
color: Colors.red.shade700,
fontWeight: FontWeight.w600,
),
),
],
),
),
);
break;
}
return buttons;
}
Widget _buildActionButton({
required String label,
required IconData icon,
required Color color,
required VoidCallback onPressed,
}) {
return ElevatedButton.icon(
onPressed: onPressed,
icon: Icon(icon, size: 16),
label: Text(label),
style: ElevatedButton.styleFrom(
backgroundColor: color,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
elevation: 2,
),
);
}
void _showConfirmDialog(
BuildContext context,
String title,
String content,
VoidCallback onConfirm,
) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
title: Row(
children: [
Icon(
Icons.help_outline,
color: Colors.blue.shade600,
),
const SizedBox(width: 8),
Text(
title,
style: const TextStyle(fontSize: 18),
),
],
),
content: Text(content),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(
'Annuler',
style: TextStyle(color: Colors.grey.shade600),
),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
onConfirm();
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue.shade600,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: const Text('Confirmer'),
),
],
);
},
);
}
}