Browse Source

filtrage

master
andrymodeste 2 months ago
parent
commit
57efa196b9
  1. 45
      lib/main.dart
  2. 50
      lib/pages/caisse_screen.dart
  3. 19
      lib/pages/commandes_screen.dart
  4. 354
      lib/pages/historique_commande.dart
  5. 44
      lib/pages/login_screen.dart
  6. 18
      lib/providers/auth_provider.dart
  7. 16
      pubspec.lock
  8. 1
      pubspec.yaml

45
lib/main.dart

@ -1,17 +1,26 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'providers/auth_provider.dart';
import 'layouts/main_layout.dart'; import 'layouts/main_layout.dart';
import 'pages/login_screen.dart';
import 'pages/tables.dart'; import 'pages/tables.dart';
import 'pages/categorie.dart'; import 'pages/categorie.dart';
import 'pages/commandes_screen.dart'; import 'pages/commandes_screen.dart';
import 'pages/login_screen.dart';
import 'pages/menus_screen.dart'; import 'pages/menus_screen.dart';
import 'pages/historique_commande.dart'; import 'pages/historique_commande.dart';
import 'pages/information.dart'; import 'pages/information.dart';
import 'pages/printer_page.dart'; import 'pages/printer_page.dart';
import 'pages/encaissement_screen.dart'; // NOUVEAU import 'pages/encaissement_screen.dart';
void main() { void main() {
runApp(const MyApp()); runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => AuthProvider()),
],
child: const MyApp(),
),
);
} }
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
@ -22,51 +31,39 @@ class MyApp extends StatelessWidget {
return MaterialApp( return MaterialApp(
title: 'Restaurant App', title: 'Restaurant App',
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
theme: ThemeData( theme: ThemeData(primarySwatch: Colors.green),
primarySwatch: Colors.green,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
initialRoute: '/login', initialRoute: '/login',
routes: { routes: {
'/login': (context) => const LoginScreen(), '/login': (context) => const LoginScreen(),
'/tables': '/tables': (context) => const MainLayout(
(context) => const MainLayout(
currentRoute: '/tables', currentRoute: '/tables',
child: TablesScreen(), child: TablesScreen(),
), ),
'/categories': '/categories': (context) => const MainLayout(
(context) => const MainLayout(
currentRoute: '/categories', currentRoute: '/categories',
child: CategoriesPage(), child: CategoriesPage(),
), ),
'/commandes': '/commandes': (context) => const MainLayout(
(context) => const MainLayout(
currentRoute: '/commandes', currentRoute: '/commandes',
child: OrdersManagementScreen(), child: OrdersManagementScreen(),
), ),
'/plats': '/plats': (context) => const MainLayout(
(context) => const MainLayout(
currentRoute: '/plats', currentRoute: '/plats',
child: PlatsManagementScreen(), child: PlatsManagementScreen(),
), ),
// NOUVELLE ROUTE pour l'encaissement '/encaissement': (context) => const MainLayout(
'/encaissement':
(context) => const MainLayout(
currentRoute: '/encaissement', currentRoute: '/encaissement',
child: EncaissementScreen(), child: EncaissementScreen(),
), ),
'/historique': '/historique': (context) => MainLayout(
(context) => MainLayout(
currentRoute: '/historique', currentRoute: '/historique',
child: OrderHistoryPage(), child: OrderHistoryPage(),
), ),
'/information': '/information': (context) => MainLayout(
(context) => MainLayout(
currentRoute: '/information', currentRoute: '/information',
child: PrintTemplateManagementScreen(), child: PrintTemplateManagementScreen(),
), ),
'/Setting': '/Setting': (context) => MainLayout(
(context) => MainLayout(
currentRoute: '/Setting', currentRoute: '/Setting',
child: PrinterPage(), child: PrinterPage(),
), ),

50
lib/pages/caisse_screen.dart

@ -367,18 +367,23 @@ class _CaisseScreenState extends State<CaisseScreen> {
}); });
}, },
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
child: Container( child: AnimatedContainer(
duration: const Duration(milliseconds: 250),
curve: Curves.easeInOut,
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
decoration: BoxDecoration( decoration: BoxDecoration(
color: method.color, color: isSelected ? Colors.white : method.color,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
border: border: Border.all(
isSelected ? Border.all(color: Colors.white, width: 3) : null, color: isSelected ? method.color : Colors.transparent,
width: 2,
),
boxShadow: [ boxShadow: [
if (isSelected)
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.1), color: method.color.withOpacity(0.4),
blurRadius: 8, blurRadius: 8,
offset: const Offset(0, 2), offset: const Offset(0, 3),
), ),
], ],
), ),
@ -387,10 +392,16 @@ class _CaisseScreenState extends State<CaisseScreen> {
Container( Container(
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2), color: isSelected
? method.color.withOpacity(0.1)
: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: Icon(method.icon, color: Colors.white, size: 24), child: Icon(
method.icon,
color: isSelected ? method.color : Colors.white,
size: 24,
),
), ),
const SizedBox(width: 16), const SizedBox(width: 16),
@ -401,8 +412,8 @@ class _CaisseScreenState extends State<CaisseScreen> {
children: [ children: [
Text( Text(
method.name, method.name,
style: const TextStyle( style: TextStyle(
color: Colors.white, color: isSelected ? Colors.black87 : Colors.white,
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@ -411,7 +422,9 @@ class _CaisseScreenState extends State<CaisseScreen> {
Text( Text(
method.description, method.description,
style: TextStyle( style: TextStyle(
color: Colors.white.withOpacity(0.9), color: isSelected
? Colors.black54
: Colors.white.withOpacity(0.9),
fontSize: 13, fontSize: 13,
), ),
), ),
@ -421,11 +434,19 @@ class _CaisseScreenState extends State<CaisseScreen> {
const SizedBox(width: 16), const SizedBox(width: 16),
Icon(
isSelected ? Icons.check_circle : Icons.radio_button_unchecked,
color: isSelected ? method.color : Colors.white,
size: 22,
),
const SizedBox(width: 8),
Text( Text(
'${NumberFormat("#,##0.00", "fr_FR").format(amount)} MGA', '${NumberFormat("#,##0.00", "fr_FR").format(amount)} MGA',
style: const TextStyle( style: TextStyle(
color: Colors.white, color: isSelected ? Colors.black87 : Colors.white,
fontSize: 18, fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
@ -437,6 +458,7 @@ class _CaisseScreenState extends State<CaisseScreen> {
); );
} }
Widget _buildPaymentButton() { Widget _buildPaymentButton() {
final canPay = selectedPaymentMethod != null && !isProcessingPayment; final canPay = selectedPaymentMethod != null && !isProcessingPayment;

19
lib/pages/commandes_screen.dart

@ -4,9 +4,16 @@ import 'package:flutter/material.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'dart:convert'; import 'dart:convert';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:itrimobe/providers/auth_provider.dart';
import 'package:provider/provider.dart' show Provider;
import 'commande_item_screen.dart'; import 'commande_item_screen.dart';
import '../services/pdf_impression_commande.dart'; import '../services/pdf_impression_commande.dart';
bool isMobile(BuildContext context) {
final size = MediaQuery.of(context).size.width;
return size < 600;
}
class OrdersManagementScreen extends StatefulWidget { class OrdersManagementScreen extends StatefulWidget {
const OrdersManagementScreen({super.key}); const OrdersManagementScreen({super.key});
@ -523,6 +530,7 @@ Future<void> deleteOrder(Order order) async {
} }
} }
class OrderCard extends StatelessWidget { class OrderCard extends StatelessWidget {
final Order order; final Order order;
final Function(Order, String, {String? modePaiement}) onStatusUpdate; final Function(Order, String, {String? modePaiement}) onStatusUpdate;
@ -530,6 +538,8 @@ class OrderCard extends StatelessWidget {
final Function(Order) onDelete; final Function(Order) onDelete;
final VoidCallback onViewDetails; final VoidCallback onViewDetails;
const OrderCard({ const OrderCard({
Key? key, Key? key,
required this.order, required this.order,
@ -579,6 +589,8 @@ class OrderCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final auth = Provider.of<AuthProvider>(context);
final userType = auth.userType;
return Container( return Container(
margin: const EdgeInsets.only(bottom: 16), margin: const EdgeInsets.only(bottom: 16),
decoration: BoxDecoration( decoration: BoxDecoration(
@ -782,6 +794,8 @@ class OrderCard extends StatelessWidget {
), ),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
if (userType != 'Serveur' || !isMobile(context))
Expanded( Expanded(
flex: 3, flex: 3,
child:ElevatedButton.icon( child:ElevatedButton.icon(
@ -809,7 +823,9 @@ class OrderCard extends StatelessWidget {
), ),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
if (order.statut == 'en_attente') if (order.statut == 'en_attente')
if (userType != 'Serveur' || !isMobile(context))
Expanded( Expanded(
child: ElevatedButton( child: ElevatedButton(
onPressed: onPressed:
@ -827,8 +843,10 @@ class OrderCard extends StatelessWidget {
), ),
), ),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
if (order.statut == 'en_preparation') if (order.statut == 'en_preparation')
if (userType != 'Serveur' || !isMobile(context))
Expanded( Expanded(
child: ElevatedButton( child: ElevatedButton(
onPressed: onPressed:
@ -846,6 +864,7 @@ class OrderCard extends StatelessWidget {
), ),
), ),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(

354
lib/pages/historique_commande.dart

@ -14,6 +14,8 @@ class _OrderHistoryPageState extends State<OrderHistoryPage>
List<CommandeData> commandes = []; List<CommandeData> commandes = [];
bool isLoading = true; bool isLoading = true;
String? error; String? error;
DateTime? _startDate;
DateTime? _endDate;
// Informations d'affichage et pagination // Informations d'affichage et pagination
int totalItems = 0; int totalItems = 0;
@ -37,7 +39,6 @@ class _OrderHistoryPageState extends State<OrderHistoryPage>
_loadCommandes(); _loadCommandes();
} }
Future<void> _loadCommandes({int page = 1}) async { Future<void> _loadCommandes({int page = 1}) async {
try { try {
setState(() { setState(() {
@ -45,72 +46,69 @@ class _OrderHistoryPageState extends State<OrderHistoryPage>
error = null; error = null;
}); });
// Ajouter les paramètres de pagination à l'URL // Construction des paramètres de requête
final uri = Uri.parse('$baseUrl/api/commandes').replace(queryParameters: { Map<String, String> queryParams = {
'statut': 'payee', 'statut': 'payee',
'page': page.toString(), 'page': page.toString(),
'limit': itemsPerPage.toString(), 'limit': itemsPerPage.toString(),
}); };
// CORRECTION : Envoyer la date en format ISO avec fuseau horaire UTC
if (_startDate != null) {
// Début de journée en UTC (00:00:00)
final startUtc = DateTime.utc(
_startDate!.year,
_startDate!.month,
_startDate!.day,
0, 0, 0,
);
queryParams['date_start'] = startUtc.toIso8601String();
print('📅 Date début (UTC): ${startUtc.toIso8601String()}');
}
if (_endDate != null) {
// Fin de journée en UTC (23:59:59)
final endUtc = DateTime.utc(
_endDate!.year,
_endDate!.month,
_endDate!.day,
23, 59, 59, 999,
);
queryParams['date_end'] = endUtc.toIso8601String();
print('📅 Date fin (UTC): ${endUtc.toIso8601String()}');
}
// Debug: Afficher l'URL complète
final uri = Uri.parse('$baseUrl/api/commandes').replace(queryParameters: queryParams);
print('🌐 URL appelée: $uri');
final response = await http.get(uri, headers: _headers); final response = await http.get(uri, headers: _headers);
print('📡 Status code: ${response.statusCode}');
if (response.statusCode == 200) { if (response.statusCode == 200) {
final dynamic responseBody = json.decode(response.body); final dynamic responseBody = json.decode(response.body);
List<dynamic> data = []; List<dynamic> data = [];
// Gestion améliorée de la réponse // Gestion selon le format de réponse
if (responseBody is Map<String, dynamic>) { if (responseBody is Map<String, dynamic>) {
final dataMap = responseBody['data'] as Map<String, dynamic>?;
// Structure: {"success": true, "data": {"commandes": [...], "pagination": {...}}} if (dataMap != null) {
if (responseBody.containsKey('data') && responseBody['data'] is Map<String, dynamic>) {
final dataMap = responseBody['data'] as Map<String, dynamic>;
if (dataMap.containsKey('commandes')) {
final commandesValue = dataMap['commandes']; final commandesValue = dataMap['commandes'];
if (commandesValue is List) {
if (commandesValue is List<dynamic>) {
data = commandesValue; data = commandesValue;
} else if (commandesValue != null) { } else if (commandesValue != null) {
data = [commandesValue]; data = [commandesValue];
} }
// Pagination // Pagination
if (dataMap.containsKey('pagination')) {
final pagination = dataMap['pagination'] as Map<String, dynamic>?; final pagination = dataMap['pagination'] as Map<String, dynamic>?;
if (pagination != null) { currentPage = pagination?['currentPage'] ?? page;
currentPage = pagination['currentPage'] ?? page; totalPages = pagination?['totalPages'] ?? (data.length / itemsPerPage).ceil();
totalPages = pagination['totalPages'] ?? 1; totalItems = pagination?['totalItems'] ?? data.length;
totalItems = pagination['totalItems'] ?? data.length;
} }
} else { } else if (responseBody is List) {
// Si pas de pagination dans la réponse, calculer approximativement
totalItems = data.length;
currentPage = page;
totalPages = (totalItems / itemsPerPage).ceil();
}
} else {
totalItems = 0;
currentPage = 1;
totalPages = 1;
}
} else if (responseBody.containsKey('commandes')) {
// Fallback: commandes directement dans responseBody
final commandesValue = responseBody['commandes'];
if (commandesValue is List<dynamic>) {
data = commandesValue;
} else if (commandesValue != null) {
data = [commandesValue];
}
totalItems = data.length;
currentPage = page;
totalPages = (totalItems / itemsPerPage).ceil();
} else {
totalItems = 0;
currentPage = 1;
totalPages = 1;
}
} else if (responseBody is List<dynamic>) {
data = responseBody; data = responseBody;
totalItems = data.length; totalItems = data.length;
currentPage = page; currentPage = page;
@ -119,51 +117,38 @@ class _OrderHistoryPageState extends State<OrderHistoryPage>
throw Exception('Format de réponse inattendu: ${responseBody.runtimeType}'); throw Exception('Format de réponse inattendu: ${responseBody.runtimeType}');
} }
// Parsing sécurisé des commandes
// Conversion sécurisée avec prints détaillés
List<CommandeData> parsedCommandes = []; List<CommandeData> parsedCommandes = [];
for (int i = 0; i < data.length; i++) { for (int i = 0; i < data.length; i++) {
try { try {
final item = data[i]; final item = data[i];
if (item is Map<String, dynamic>) { if (item is Map<String, dynamic>) {
item.forEach((key, value) {
});
final commandeData = CommandeData.fromJson(item); final commandeData = CommandeData.fromJson(item);
if (commandeData.items != null) {
for (int j = 0; j < commandeData.items!.length; j++) {
final commandeItem = commandeData.items![j];
}
}
parsedCommandes.add(commandeData); parsedCommandes.add(commandeData);
} else { } else {
print('ERROR: Item $i n\'est pas un Map: ${item.runtimeType}'); print('Item $i invalide: ${item.runtimeType}');
} }
} catch (e, stackTrace) { } catch (e, stackTrace) {
print('ERROR: Erreur lors du parsing de l\'item $i: $e'); print('Erreur parsing item $i: $e');
print('Stack trace: $stackTrace'); print(stackTrace);
// Continue avec les autres items
} }
} }
print('${parsedCommandes.length} commandes chargées');
setState(() { setState(() {
commandes = parsedCommandes; commandes = parsedCommandes;
isLoading = false; isLoading = false;
}); });
// Initialiser les animations après avoir mis à jour l'état
_initializeAnimations(); _initializeAnimations();
_startAnimations(); _startAnimations();
} else { } else {
print('ERROR: HTTP ${response.statusCode}: ${response.reasonPhrase}');
setState(() { setState(() {
error = 'Erreur HTTP ${response.statusCode}: ${response.reasonPhrase}'; error = 'Erreur HTTP ${response.statusCode}: ${response.reasonPhrase}';
isLoading = false; isLoading = false;
}); });
print('Erreur HTTP ${response.statusCode}: ${response.reasonPhrase}');
} }
} catch (e, stackTrace) { } catch (e, stackTrace) {
print('=== ERREUR GÉNÉRALE ==='); print('=== ERREUR GÉNÉRALE ===');
@ -176,6 +161,25 @@ class _OrderHistoryPageState extends State<OrderHistoryPage>
} }
} }
Future<void> _selectDate(BuildContext context, bool isStart) async {
final DateTime? picked = await showDatePicker(
context: context,
initialDate: isStart ? (_startDate ?? DateTime.now()) : (_endDate ?? DateTime.now()),
firstDate: DateTime(2020),
lastDate: DateTime(2100),
);
if (picked != null) {
setState(() {
if (isStart) {
_startDate = picked;
} else {
_endDate = picked;
}
});
}
}
// Fonction pour aller à la page suivante // Fonction pour aller à la page suivante
void _goToNextPage() { void _goToNextPage() {
if (currentPage < totalPages) { if (currentPage < totalPages) {
@ -258,7 +262,6 @@ class _OrderHistoryPageState extends State<OrderHistoryPage>
), ),
); );
} }
Widget _buildHeader() { Widget _buildHeader() {
return AnimatedBuilder( return AnimatedBuilder(
animation: _animationController, animation: _animationController,
@ -302,6 +305,85 @@ class _OrderHistoryPageState extends State<OrderHistoryPage>
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
SizedBox(height: 6),
// Barre des filtres
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () => _selectDate(context, true),
child: Text(_startDate == null
? 'Date début'
: 'Début: ${_startDate!.day}/${_startDate!.month}/${_startDate!.year}'),
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF4CAF50),
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
textStyle: TextStyle(fontSize: 10),
),
),
SizedBox(width: 5),
ElevatedButton(
onPressed: () => _selectDate(context, false),
child: Text(_endDate == null
? 'Date fin'
: 'Fin: ${_endDate!.day}/${_endDate!.month}/${_endDate!.year}'),
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF4CAF50),
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
textStyle: TextStyle(fontSize: 10),
),
),
SizedBox(width: 5),
ElevatedButton(
onPressed: () => _loadCommandes(page: 1),
child: Text('Filtrer'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.orange,
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
textStyle: TextStyle(fontSize: 10),
),
),
SizedBox(width: 5),
ElevatedButton(
onPressed: () {
setState(() {
_startDate = null;
_endDate = null;
});
_loadCommandes(page: 1);
},
child: Text('Réinitialiser'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey,
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
textStyle: TextStyle(fontSize: 10),
),
),
SizedBox(width: 5),
ElevatedButton(
onPressed: () {
final today = DateTime.now();
setState(() {
_startDate = DateTime(today.year, today.month, today.day);
_endDate = DateTime(today.year, today.month, today.day, 23, 59, 59);
});
_loadCommandes(page: 1);
},
child: Text('Aujourd’hui'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
textStyle: TextStyle(fontSize: 10),
),
),
],
),
SizedBox(height: 4),
if (totalItems > 0) if (totalItems > 0)
Padding( Padding(
padding: EdgeInsets.only(top: 4), padding: EdgeInsets.only(top: 4),
@ -464,90 +546,105 @@ class _OrderHistoryPageState extends State<OrderHistoryPage>
Widget _buildContent() { Widget _buildContent() {
if (isLoading) { if (isLoading) {
return Center( return const Center(
child: Column( child: CircularProgressIndicator(color: Color(0xFF4CAF50)),
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Color(0xFF4CAF50)),
),
SizedBox(height: 16),
Text(
'Chargement des commandes...',
style: TextStyle(color: Colors.grey.shade600),
),
],
),
); );
} }
if (error != null) { if (error != null) {
return Center( return Center(
child: Column( child: Text(error!, style: TextStyle(color: Colors.red)),
mainAxisAlignment: MainAxisAlignment.center, );
children: [ }
Icon(Icons.error_outline, size: 64, color: Colors.grey),
SizedBox(height: 16), if (commandes.isEmpty) {
Padding( return const Center(
padding: EdgeInsets.symmetric(horizontal: 20), child: Text("Aucune commande trouvée"),
child: Text( );
error!, }
style: TextStyle(color: Colors.grey),
textAlign: TextAlign.center, // Afficher les données sous forme de tableau
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: ConstrainedBox(
constraints: BoxConstraints(minWidth: MediaQuery.of(context).size.width * 0.9), // 1.5x la largeur écran
child: SingleChildScrollView(
child: _buildTableView(),
), ),
), ),
SizedBox(height: 16), );
ElevatedButton(
onPressed: () => _loadCommandes(page: currentPage), }
child: Text('Réessayer'),
style: ElevatedButton.styleFrom( Widget _buildTableView() {
backgroundColor: Color(0xFF4CAF50), return DataTable(
foregroundColor: Colors.white, columnSpacing: 20,
headingRowColor: MaterialStateProperty.all(const Color(0xFF4CAF50)),
headingTextStyle: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
dataRowHeight: 48,
columns: const [
DataColumn(label: Text("")),
DataColumn(label: Text("Table")),
DataColumn(label: Text("Total")),
DataColumn(label: Text("Détails")), // Nouvelle colonne pour le bouton
],
rows: commandes.map((commande) {
return DataRow(
cells: [
DataCell(Text(commande.numeroCommande ?? '-')),
DataCell(Text(commande.tablename ?? '-')),
DataCell(Text(_formatPrice(commande.totalTtc ?? 0))),
DataCell(
IconButton(
icon: Icon(Icons.info, color: Color(0xFF4CAF50)),
tooltip: 'Voir les détails',
onPressed: () {
_showCommandeDetails(commande);
},
), ),
), ),
], ],
), );
}).toList(),
); );
} }
if (commandes.isEmpty) { // Exemple de fonction pour afficher les détails dans un dialog
return Center( void _showCommandeDetails(CommandeData commande) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Détails commande ${commande.numeroCommande ?? ""}'),
content: SingleChildScrollView(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Icon(Icons.restaurant_menu, size: 64, color: Colors.grey), Text('Table: ${commande.tablename ?? "-"}'),
SizedBox(height: 16),
Text( Text(
currentPage > 1 'Date de paiement: ${commande.datePaiement != null ? _formatDateTime(commande.datePaiement!) : "-"}',
? 'Aucune commande sur cette page'
: 'Aucune commande payée',
style: TextStyle(color: Colors.grey, fontSize: 16),
), ),
if (currentPage > 1) ...[ Text('Total TTC: ${_formatPrice(commande.totalTtc ?? 0)}'),
SizedBox(height: 16), SizedBox(height: 10),
ElevatedButton( const Text('Articles:', style: TextStyle(fontWeight: FontWeight.bold)),
onPressed: () => _goToPage(1), ...?commande.items?.map((item) => Text(
child: Text('Retour à la première page'), '${item.quantite} × ${item.menuNom} - ${_formatPrice(item.totalItem)}')),
style: ElevatedButton.styleFrom( ],
backgroundColor: Color(0xFF4CAF50),
foregroundColor: Colors.white,
), ),
), ),
], actions: [
], TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Fermer'),
), ),
],
); );
}
return ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 20),
itemCount: commandes.length,
itemBuilder: (context, index) {
return _buildOrderCard(commandes[index], index);
}, },
); );
} }
Widget _buildOrderCard(CommandeData commande, int index) { Widget _buildOrderCard(CommandeData commande, int index) {
if (index >= _cardAnimationControllers.length) { if (index >= _cardAnimationControllers.length) {
return SizedBox.shrink(); return SizedBox.shrink();
@ -1056,17 +1153,16 @@ class CommandeData {
if (value == null) return null; if (value == null) return null;
if (value is String) { if (value is String) {
try { try {
final result = DateTime.parse(value); return DateTime.parse(value).toLocal(); // converti en heure locale
return result;
} catch (e) { } catch (e) {
print('Erreur parsing date: $value - $e'); print('Erreur parsing date: $value - $e');
return null; return null;
} }
} }
print('Impossible de parser en datetime: $value');
return null; return null;
} }
static List<CommandeItem>? _parseItems(dynamic value) { static List<CommandeItem>? _parseItems(dynamic value) {
if (value == null) { if (value == null) {

44
lib/pages/login_screen.dart

@ -1,5 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import '../providers/auth_provider.dart';
import './tables.dart'; import './tables.dart';
import '../layouts/main_layout.dart'; import '../layouts/main_layout.dart';
@ -33,6 +35,10 @@ class _LoginScreenState extends State<LoginScreen> {
static const String serveurPassword = 'serveur123'; static const String serveurPassword = 'serveur123';
static const String adminEmail = 'admin@restaurant.com'; static const String adminEmail = 'admin@restaurant.com';
static const String adminPassword = 'admin123'; static const String adminPassword = 'admin123';
static const String cuisinierEmail = 'cuisinier@restaurant.com';
static const String cuisinierPassword = 'cuisinier123';
static const String caissierEmail = 'caissier@restaurant.com';
static const String caissierPassword = 'caissier123';
final TextEditingController emailController = TextEditingController(); final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController(); final TextEditingController passwordController = TextEditingController();
@ -63,7 +69,6 @@ class _LoginScreenState extends State<LoginScreen> {
} }
} }
} }
void _login() async { void _login() async {
if (!_formKey.currentState!.validate()) return; if (!_formKey.currentState!.validate()) return;
@ -72,41 +77,60 @@ class _LoginScreenState extends State<LoginScreen> {
_errorMessage = null; _errorMessage = null;
}); });
// Simulate network delay // Simule un délai pour l'authentification
await Future.delayed(const Duration(seconds: 1)); await Future.delayed(const Duration(seconds: 1));
setState(() { setState(() {
_isLoading = false; _isLoading = false;
}); });
// Check credentials
String email = emailController.text.trim(); String email = emailController.text.trim();
String password = passwordController.text; String password = passwordController.text;
// Vérification des credentials
if ((email == serveurEmail && password == serveurPassword) || if ((email == serveurEmail && password == serveurPassword) ||
(email == cuisinierEmail && password == cuisinierPassword) ||
(email == caissierEmail && password == caissierPassword) ||
(email == adminEmail && password == adminPassword)) { (email == adminEmail && password == adminPassword)) {
// Login successful - navigate to home/dashboard
if (mounted) { // Détermination du rôle
String userType = email == adminEmail ? 'Admin' : 'Serveur'; String userType;
if (email == adminEmail) {
userType = 'Admin';
} else if (email == serveurEmail) {
userType = 'Serveur';
}
else if (email == caissierEmail) {
userType = 'caissier';
} else {
userType = 'Cuisinier';
}
// On enregistre le rôle dans le provider
final authProvider = Provider.of<AuthProvider>(context, listen: false);
authProvider.loginAs(userType);
// Navigation vers la page principale
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: builder: (context) => const MainLayout(
(context) => const MainLayout(
currentRoute: '/tables', currentRoute: '/tables',
child: TablesScreen(), child: TablesScreen(),
), ),
), ),
); );
}
} else { } else {
// Login failed // Erreur de connexion
setState(() { setState(() {
_errorMessage = 'Email ou mot de passe incorrect'; _errorMessage = 'Email ou mot de passe incorrect';
}); });
} }
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return KeyboardListener( return KeyboardListener(

18
lib/providers/auth_provider.dart

@ -0,0 +1,18 @@
import 'package:flutter/material.dart';
class AuthProvider extends ChangeNotifier {
String? _userType; // "Admin" ou "Serveur"
String? get userType => _userType;
bool get isLoggedIn => _userType != null;
void loginAs(String userType) {
_userType = userType;
notifyListeners();
}
void logout() {
_userType = null;
notifyListeners();
}
}

16
pubspec.lock

@ -296,6 +296,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.6" version: "1.0.6"
nested:
dependency: transitive
description:
name: nested
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
path: path:
dependency: transitive dependency: transitive
description: description:
@ -456,6 +464,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.14.2" version: "5.14.2"
provider:
dependency: "direct main"
description:
name: provider
sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272"
url: "https://pub.dev"
source: hosted
version: "6.1.5+1"
qr: qr:
dependency: transitive dependency: transitive
description: description:

1
pubspec.yaml

@ -29,6 +29,7 @@ dependencies:
intl: ^0.18.1 intl: ^0.18.1
esc_pos_printer: ^4.1.0 esc_pos_printer: ^4.1.0
esc_pos_utils: ^1.1.0 esc_pos_utils: ^1.1.0
provider: ^6.1.1
# Dépendances de développement/test # Dépendances de développement/test
dev_dependencies: dev_dependencies:

Loading…
Cancel
Save