filtrage
This commit is contained in:
parent
c9a0d72a1e
commit
57efa196b9
@ -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(),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -352,90 +352,112 @@ class _CaisseScreenState extends State<CaisseScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildPaymentMethodCard(PaymentMethod method) {
|
Widget _buildPaymentMethodCard(PaymentMethod method) {
|
||||||
final isSelected = selectedPaymentMethod?.id == method.id;
|
final isSelected = selectedPaymentMethod?.id == method.id;
|
||||||
final amount = commande?.totalTtc ?? 0.0;
|
final amount = commande?.totalTtc ?? 0.0;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.only(bottom: 12),
|
margin: const EdgeInsets.only(bottom: 12),
|
||||||
child: Material(
|
child: Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
selectedPaymentMethod = method;
|
selectedPaymentMethod = method;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
child: Container(
|
child: AnimatedContainer(
|
||||||
padding: const EdgeInsets.all(20),
|
duration: const Duration(milliseconds: 250),
|
||||||
decoration: BoxDecoration(
|
curve: Curves.easeInOut,
|
||||||
color: method.color,
|
padding: const EdgeInsets.all(20),
|
||||||
borderRadius: BorderRadius.circular(12),
|
decoration: BoxDecoration(
|
||||||
border:
|
color: isSelected ? Colors.white : method.color,
|
||||||
isSelected ? Border.all(color: Colors.white, width: 3) : null,
|
borderRadius: BorderRadius.circular(12),
|
||||||
boxShadow: [
|
border: Border.all(
|
||||||
|
color: isSelected ? method.color : Colors.transparent,
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
|
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),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white.withOpacity(0.2),
|
color: isSelected
|
||||||
borderRadius: BorderRadius.circular(8),
|
? method.color.withOpacity(0.1)
|
||||||
),
|
: Colors.white.withOpacity(0.2),
|
||||||
child: Icon(method.icon, color: Colors.white, size: 24),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
|
child: Icon(
|
||||||
|
method.icon,
|
||||||
|
color: isSelected ? method.color : Colors.white,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
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,
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
),
|
||||||
Text(
|
const SizedBox(height: 4),
|
||||||
method.description,
|
Text(
|
||||||
style: TextStyle(
|
method.description,
|
||||||
color: Colors.white.withOpacity(0.9),
|
style: TextStyle(
|
||||||
fontSize: 13,
|
color: isSelected
|
||||||
),
|
? Colors.black54
|
||||||
|
: Colors.white.withOpacity(0.9),
|
||||||
|
fontSize: 13,
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
|
|
||||||
Text(
|
Icon(
|
||||||
'${NumberFormat("#,##0.00", "fr_FR").format(amount)} MGA',
|
isSelected ? Icons.check_circle : Icons.radio_button_unchecked,
|
||||||
style: const TextStyle(
|
color: isSelected ? method.color : Colors.white,
|
||||||
color: Colors.white,
|
size: 22,
|
||||||
fontSize: 18,
|
),
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
const SizedBox(width: 8),
|
||||||
|
|
||||||
|
Text(
|
||||||
|
'${NumberFormat("#,##0.00", "fr_FR").format(amount)} MGA',
|
||||||
|
style: TextStyle(
|
||||||
|
color: isSelected ? Colors.black87 : Colors.white,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Widget _buildPaymentButton() {
|
Widget _buildPaymentButton() {
|
||||||
final canPay = selectedPaymentMethod != null && !isProcessingPayment;
|
final canPay = selectedPaymentMethod != null && !isProcessingPayment;
|
||||||
|
|||||||
@ -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,9 +794,11 @@ 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(
|
||||||
onPressed: () => onProcessPayment(order),
|
onPressed: () => onProcessPayment(order),
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.point_of_sale,
|
Icons.point_of_sale,
|
||||||
@ -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(
|
||||||
|
|||||||
@ -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,144 +39,146 @@ class _OrderHistoryPageState extends State<OrderHistoryPage>
|
|||||||
|
|
||||||
_loadCommandes();
|
_loadCommandes();
|
||||||
}
|
}
|
||||||
|
Future<void> _loadCommandes({int page = 1}) async {
|
||||||
|
try {
|
||||||
|
setState(() {
|
||||||
|
isLoading = true;
|
||||||
|
error = null;
|
||||||
|
});
|
||||||
|
|
||||||
Future<void> _loadCommandes({int page = 1}) async {
|
// Construction des paramètres de requête
|
||||||
try {
|
Map<String, String> queryParams = {
|
||||||
setState(() {
|
'statut': 'payee',
|
||||||
isLoading = true;
|
'page': page.toString(),
|
||||||
error = null;
|
'limit': itemsPerPage.toString(),
|
||||||
});
|
};
|
||||||
|
|
||||||
// Ajouter les paramètres de pagination à l'URL
|
// ✅ CORRECTION : Envoyer la date en format ISO avec fuseau horaire UTC
|
||||||
final uri = Uri.parse('$baseUrl/api/commandes').replace(queryParameters: {
|
if (_startDate != null) {
|
||||||
'statut': 'payee',
|
// Début de journée en UTC (00:00:00)
|
||||||
'page': page.toString(),
|
final startUtc = DateTime.utc(
|
||||||
'limit': itemsPerPage.toString(),
|
_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()}');
|
||||||
|
}
|
||||||
|
|
||||||
final response = await http.get(uri, headers: _headers);
|
// Debug: Afficher l'URL complète
|
||||||
|
final uri = Uri.parse('$baseUrl/api/commandes').replace(queryParameters: queryParams);
|
||||||
if (response.statusCode == 200) {
|
print('🌐 URL appelée: $uri');
|
||||||
final dynamic responseBody = json.decode(response.body);
|
|
||||||
|
final response = await http.get(uri, headers: _headers);
|
||||||
List<dynamic> data = [];
|
|
||||||
|
print('📡 Status code: ${response.statusCode}');
|
||||||
// Gestion améliorée de la réponse
|
|
||||||
if (responseBody is Map<String, dynamic>) {
|
if (response.statusCode == 200) {
|
||||||
|
final dynamic responseBody = json.decode(response.body);
|
||||||
// Structure: {"success": true, "data": {"commandes": [...], "pagination": {...}}}
|
List<dynamic> data = [];
|
||||||
if (responseBody.containsKey('data') && responseBody['data'] is Map<String, dynamic>) {
|
|
||||||
final dataMap = responseBody['data'] as Map<String, dynamic>;
|
// Gestion selon le format de réponse
|
||||||
if (dataMap.containsKey('commandes')) {
|
if (responseBody is Map<String, dynamic>) {
|
||||||
final commandesValue = dataMap['commandes'];
|
final dataMap = responseBody['data'] as Map<String, dynamic>?;
|
||||||
|
|
||||||
if (commandesValue is List<dynamic>) {
|
if (dataMap != null) {
|
||||||
data = commandesValue;
|
final commandesValue = dataMap['commandes'];
|
||||||
} else if (commandesValue != null) {
|
if (commandesValue is List) {
|
||||||
data = [commandesValue];
|
data = commandesValue;
|
||||||
}
|
} else if (commandesValue != null) {
|
||||||
|
data = [commandesValue];
|
||||||
// Pagination
|
|
||||||
if (dataMap.containsKey('pagination')) {
|
|
||||||
final pagination = dataMap['pagination'] as Map<String, dynamic>?;
|
|
||||||
if (pagination != null) {
|
|
||||||
currentPage = pagination['currentPage'] ?? page;
|
|
||||||
totalPages = pagination['totalPages'] ?? 1;
|
|
||||||
totalItems = pagination['totalItems'] ?? data.length;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 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;
|
|
||||||
totalItems = data.length;
|
|
||||||
currentPage = page;
|
|
||||||
totalPages = (totalItems / itemsPerPage).ceil();
|
|
||||||
} else {
|
|
||||||
throw Exception('Format de réponse inattendu: ${responseBody.runtimeType}');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Conversion sécurisée avec prints détaillés
|
|
||||||
List<CommandeData> parsedCommandes = [];
|
|
||||||
for (int i = 0; i < data.length; i++) {
|
|
||||||
try {
|
|
||||||
final item = data[i];
|
|
||||||
|
|
||||||
if (item is Map<String, dynamic>) {
|
|
||||||
item.forEach((key, value) {
|
|
||||||
});
|
|
||||||
|
|
||||||
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);
|
|
||||||
} else {
|
|
||||||
print('ERROR: Item $i n\'est pas un Map: ${item.runtimeType}');
|
|
||||||
}
|
|
||||||
} catch (e, stackTrace) {
|
|
||||||
print('ERROR: Erreur lors du parsing de l\'item $i: $e');
|
|
||||||
print('Stack trace: $stackTrace');
|
|
||||||
// Continue avec les autres items
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
commandes = parsedCommandes;
|
|
||||||
isLoading = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initialiser les animations après avoir mis à jour l'état
|
// Pagination
|
||||||
_initializeAnimations();
|
final pagination = dataMap['pagination'] as Map<String, dynamic>?;
|
||||||
_startAnimations();
|
currentPage = pagination?['currentPage'] ?? page;
|
||||||
|
totalPages = pagination?['totalPages'] ?? (data.length / itemsPerPage).ceil();
|
||||||
|
totalItems = pagination?['totalItems'] ?? data.length;
|
||||||
|
}
|
||||||
|
} else if (responseBody is List) {
|
||||||
|
data = responseBody;
|
||||||
|
totalItems = data.length;
|
||||||
|
currentPage = page;
|
||||||
|
totalPages = (totalItems / itemsPerPage).ceil();
|
||||||
} else {
|
} else {
|
||||||
print('ERROR: HTTP ${response.statusCode}: ${response.reasonPhrase}');
|
throw Exception('Format de réponse inattendu: ${responseBody.runtimeType}');
|
||||||
setState(() {
|
|
||||||
error = 'Erreur HTTP ${response.statusCode}: ${response.reasonPhrase}';
|
|
||||||
isLoading = false;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} catch (e, stackTrace) {
|
|
||||||
print('=== ERREUR GÉNÉRALE ===');
|
// Parsing sécurisé des commandes
|
||||||
print('Erreur: $e');
|
List<CommandeData> parsedCommandes = [];
|
||||||
print('Stack trace: $stackTrace');
|
for (int i = 0; i < data.length; i++) {
|
||||||
|
try {
|
||||||
|
final item = data[i];
|
||||||
|
if (item is Map<String, dynamic>) {
|
||||||
|
final commandeData = CommandeData.fromJson(item);
|
||||||
|
parsedCommandes.add(commandeData);
|
||||||
|
} else {
|
||||||
|
print('Item $i invalide: ${item.runtimeType}');
|
||||||
|
}
|
||||||
|
} catch (e, stackTrace) {
|
||||||
|
print('Erreur parsing item $i: $e');
|
||||||
|
print(stackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print('✅ ${parsedCommandes.length} commandes chargées');
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
error = 'Erreur de connexion: $e';
|
commandes = parsedCommandes;
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_initializeAnimations();
|
||||||
|
_startAnimations();
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
error = 'Erreur HTTP ${response.statusCode}: ${response.reasonPhrase}';
|
||||||
|
isLoading = false;
|
||||||
|
});
|
||||||
|
print('Erreur HTTP ${response.statusCode}: ${response.reasonPhrase}');
|
||||||
}
|
}
|
||||||
|
} catch (e, stackTrace) {
|
||||||
|
print('=== ERREUR GÉNÉRALE ===');
|
||||||
|
print('Erreur: $e');
|
||||||
|
print('Stack trace: $stackTrace');
|
||||||
|
setState(() {
|
||||||
|
error = 'Erreur de connexion: $e';
|
||||||
|
isLoading = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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() {
|
||||||
@ -258,73 +262,151 @@ class _OrderHistoryPageState extends State<OrderHistoryPage>
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Widget _buildHeader() {
|
||||||
Widget _buildHeader() {
|
return AnimatedBuilder(
|
||||||
return AnimatedBuilder(
|
animation: _animationController,
|
||||||
animation: _animationController,
|
builder: (context, child) {
|
||||||
builder: (context, child) {
|
return Transform.translate(
|
||||||
return Transform.translate(
|
offset: Offset(0, -50 * (1 - _animationController.value)),
|
||||||
offset: Offset(0, -50 * (1 - _animationController.value)),
|
child: Opacity(
|
||||||
child: Opacity(
|
opacity: _animationController.value,
|
||||||
opacity: _animationController.value,
|
child: Container(
|
||||||
child: Container(
|
width: double.infinity,
|
||||||
width: double.infinity,
|
margin: EdgeInsets.symmetric(horizontal: 20, vertical: 5),
|
||||||
margin: EdgeInsets.symmetric(horizontal: 20, vertical: 5),
|
padding: EdgeInsets.all(10),
|
||||||
padding: EdgeInsets.all(10),
|
decoration: BoxDecoration(
|
||||||
decoration: BoxDecoration(
|
color: Colors.white,
|
||||||
color: Colors.white,
|
borderRadius: BorderRadius.circular(12),
|
||||||
borderRadius: BorderRadius.circular(12),
|
boxShadow: [
|
||||||
boxShadow: [
|
BoxShadow(
|
||||||
BoxShadow(
|
color: Colors.grey.withOpacity(0.08),
|
||||||
color: Colors.grey.withOpacity(0.08),
|
blurRadius: 6,
|
||||||
blurRadius: 6,
|
offset: Offset(0, 2),
|
||||||
offset: Offset(0, 2),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Historique des commandes payées',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Color(0xFF2c3e50),
|
||||||
),
|
),
|
||||||
],
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
child: Column(
|
SizedBox(height: 2),
|
||||||
children: [
|
Text(
|
||||||
Text(
|
'Consultez toutes les commandes qui ont été payées',
|
||||||
'Historique des commandes payées',
|
style: TextStyle(
|
||||||
style: TextStyle(
|
fontSize: 12,
|
||||||
fontSize: 18,
|
color: Colors.grey.shade600,
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color(0xFF2c3e50),
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
),
|
||||||
SizedBox(height: 2),
|
textAlign: TextAlign.center,
|
||||||
Text(
|
),
|
||||||
'Consultez toutes les commandes qui ont été payées',
|
SizedBox(height: 6),
|
||||||
style: TextStyle(
|
// Barre des filtres
|
||||||
fontSize: 12,
|
Row(
|
||||||
color: Colors.grey.shade600,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
),
|
children: [
|
||||||
textAlign: TextAlign.center,
|
ElevatedButton(
|
||||||
),
|
onPressed: () => _selectDate(context, true),
|
||||||
if (totalItems > 0)
|
child: Text(_startDate == null
|
||||||
Padding(
|
? 'Date début'
|
||||||
padding: EdgeInsets.only(top: 4),
|
: 'Début: ${_startDate!.day}/${_startDate!.month}/${_startDate!.year}'),
|
||||||
child: Text(
|
style: ElevatedButton.styleFrom(
|
||||||
totalPages > 1
|
backgroundColor: Color(0xFF4CAF50),
|
||||||
? '$totalItems commande${totalItems > 1 ? 's' : ''} • Page $currentPage/$totalPages'
|
foregroundColor: Colors.white,
|
||||||
: '$totalItems commande${totalItems > 1 ? 's' : ''} trouvée${totalItems > 1 ? 's' : ''}',
|
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
style: TextStyle(
|
textStyle: TextStyle(fontSize: 10),
|
||||||
fontSize: 10,
|
|
||||||
color: Colors.grey.shade500,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
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)
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 4),
|
||||||
|
child: Text(
|
||||||
|
totalPages > 1
|
||||||
|
? '$totalItems commande${totalItems > 1 ? 's' : ''} • Page $currentPage/$totalPages'
|
||||||
|
: '$totalItems commande${totalItems > 1 ? 's' : ''} trouvée${totalItems > 1 ? 's' : ''}',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 10,
|
||||||
|
color: Colors.grey.shade500,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
);
|
||||||
);
|
},
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildPagination() {
|
Widget _buildPagination() {
|
||||||
return Container(
|
return Container(
|
||||||
@ -462,92 +544,107 @@ class _OrderHistoryPageState extends State<OrderHistoryPage>
|
|||||||
return pages;
|
return pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
|
||||||
return Center(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Icon(Icons.error_outline, size: 64, color: Colors.grey),
|
|
||||||
SizedBox(height: 16),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 20),
|
|
||||||
child: Text(
|
|
||||||
error!,
|
|
||||||
style: TextStyle(color: Colors.grey),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 16),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () => _loadCommandes(page: currentPage),
|
|
||||||
child: Text('Réessayer'),
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: Color(0xFF4CAF50),
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (commandes.isEmpty) {
|
|
||||||
return Center(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Icon(Icons.restaurant_menu, size: 64, color: Colors.grey),
|
|
||||||
SizedBox(height: 16),
|
|
||||||
Text(
|
|
||||||
currentPage > 1
|
|
||||||
? 'Aucune commande sur cette page'
|
|
||||||
: 'Aucune commande payée',
|
|
||||||
style: TextStyle(color: Colors.grey, fontSize: 16),
|
|
||||||
),
|
|
||||||
if (currentPage > 1) ...[
|
|
||||||
SizedBox(height: 16),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () => _goToPage(1),
|
|
||||||
child: Text('Retour à la première page'),
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: Color(0xFF4CAF50),
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ListView.builder(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 20),
|
|
||||||
itemCount: commandes.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
return _buildOrderCard(commandes[index], index);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (error != null) {
|
||||||
|
return Center(
|
||||||
|
child: Text(error!, style: TextStyle(color: Colors.red)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commandes.isEmpty) {
|
||||||
|
return const Center(
|
||||||
|
child: Text("Aucune commande trouvée"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 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(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTableView() {
|
||||||
|
return DataTable(
|
||||||
|
columnSpacing: 20,
|
||||||
|
headingRowColor: MaterialStateProperty.all(const Color(0xFF4CAF50)),
|
||||||
|
headingTextStyle: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
|
||||||
|
dataRowHeight: 48,
|
||||||
|
columns: const [
|
||||||
|
DataColumn(label: Text("N°")),
|
||||||
|
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(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exemple de fonction pour afficher les détails dans un dialog
|
||||||
|
void _showCommandeDetails(CommandeData commande) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text('Détails commande ${commande.numeroCommande ?? ""}'),
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Table: ${commande.tablename ?? "-"}'),
|
||||||
|
Text(
|
||||||
|
'Date de paiement: ${commande.datePaiement != null ? _formatDateTime(commande.datePaiement!) : "-"}',
|
||||||
|
),
|
||||||
|
Text('Total TTC: ${_formatPrice(commande.totalTtc ?? 0)}'),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
const Text('Articles:', style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
...?commande.items?.map((item) => Text(
|
||||||
|
'${item.quantite} × ${item.menuNom} - ${_formatPrice(item.totalItem)}')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: const Text('Fermer'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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();
|
||||||
@ -1052,20 +1149,19 @@ class CommandeData {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static DateTime? _parseDateTime(dynamic value) {
|
static DateTime? _parseDateTime(dynamic value) {
|
||||||
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) {
|
||||||
|
|
||||||
|
|||||||
@ -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,49 +69,67 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _login() async {
|
void _login() async {
|
||||||
if (!_formKey.currentState!.validate()) return;
|
if (!_formKey.currentState!.validate()) return;
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
_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;
|
|
||||||
|
|
||||||
if ((email == serveurEmail && password == serveurPassword) ||
|
// Vérification des credentials
|
||||||
(email == adminEmail && password == adminPassword)) {
|
if ((email == serveurEmail && password == serveurPassword) ||
|
||||||
// Login successful - navigate to home/dashboard
|
(email == cuisinierEmail && password == cuisinierPassword) ||
|
||||||
if (mounted) {
|
(email == caissierEmail && password == caissierPassword) ||
|
||||||
String userType = email == adminEmail ? 'Admin' : 'Serveur';
|
(email == adminEmail && password == adminPassword)) {
|
||||||
Navigator.pushReplacement(
|
|
||||||
context,
|
// Détermination du rôle
|
||||||
MaterialPageRoute(
|
String userType;
|
||||||
builder:
|
if (email == adminEmail) {
|
||||||
(context) => const MainLayout(
|
userType = 'Admin';
|
||||||
currentRoute: '/tables',
|
} else if (email == serveurEmail) {
|
||||||
child: TablesScreen(),
|
userType = 'Serveur';
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Login failed
|
|
||||||
setState(() {
|
|
||||||
_errorMessage = 'Email ou mot de passe incorrect';
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
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(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => const MainLayout(
|
||||||
|
currentRoute: '/tables',
|
||||||
|
child: TablesScreen(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// ❌ Erreur de connexion
|
||||||
|
setState(() {
|
||||||
|
_errorMessage = 'Email ou mot de passe incorrect';
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|||||||
18
lib/providers/auth_provider.dart
Normal file
18
lib/providers/auth_provider.dart
Normal file
@ -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
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:
|
||||||
|
|||||||
@ -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…
Reference in New Issue
Block a user