push 03082025
This commit is contained in:
parent
c2cd893835
commit
c9f4a0ce45
@ -16,6 +16,7 @@ class CommandeDetail {
|
|||||||
final DateTime? dateService;
|
final DateTime? dateService;
|
||||||
final DateTime createdAt;
|
final DateTime createdAt;
|
||||||
final DateTime updatedAt;
|
final DateTime updatedAt;
|
||||||
|
final String tablename;
|
||||||
final List<CommandeItem> items;
|
final List<CommandeItem> items;
|
||||||
|
|
||||||
CommandeDetail({
|
CommandeDetail({
|
||||||
@ -36,6 +37,7 @@ class CommandeDetail {
|
|||||||
required this.createdAt,
|
required this.createdAt,
|
||||||
required this.updatedAt,
|
required this.updatedAt,
|
||||||
required this.items,
|
required this.items,
|
||||||
|
required this.tablename,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory CommandeDetail.fromJson(Map<String, dynamic> json) {
|
factory CommandeDetail.fromJson(Map<String, dynamic> json) {
|
||||||
@ -54,6 +56,7 @@ class CommandeDetail {
|
|||||||
totalTtc: double.tryParse(data['total_ttc']?.toString() ?? '0') ?? 0.0,
|
totalTtc: double.tryParse(data['total_ttc']?.toString() ?? '0') ?? 0.0,
|
||||||
modePaiement: data['mode_paiement'],
|
modePaiement: data['mode_paiement'],
|
||||||
commentaires: data['commentaires'],
|
commentaires: data['commentaires'],
|
||||||
|
tablename: json['tablename'] ?? 'Inconnue',
|
||||||
serveur: data['serveur'] ?? 'Serveur par défaut',
|
serveur: data['serveur'] ?? 'Serveur par défaut',
|
||||||
dateCommande:
|
dateCommande:
|
||||||
data['date_commande'] != null
|
data['date_commande'] != null
|
||||||
|
|||||||
@ -10,7 +10,8 @@ class TableOrder {
|
|||||||
final double? total; // Optionnel pour les commandes en cours
|
final double? total; // Optionnel pour les commandes en cours
|
||||||
final bool isEncashed;
|
final bool isEncashed;
|
||||||
final String? time; // Heure de la commande si applicable
|
final String? time; // Heure de la commande si applicable
|
||||||
final String? date; // Date de la commande si applicable
|
final DateTime? date; // Date de la commande si applicable
|
||||||
|
final String? tablename; // Date de la commande si applicable
|
||||||
// final int? persons; // Nombre de personnes si applicable
|
// final int? persons; // Nombre de personnes si applicable
|
||||||
|
|
||||||
TableOrder({
|
TableOrder({
|
||||||
@ -25,31 +26,34 @@ class TableOrder {
|
|||||||
this.isEncashed = false,
|
this.isEncashed = false,
|
||||||
this.time,
|
this.time,
|
||||||
this.date,
|
this.date,
|
||||||
|
this.tablename,
|
||||||
// this.persons,
|
// this.persons,
|
||||||
});
|
});
|
||||||
|
factory TableOrder.fromJson(Map<String, dynamic> json) {
|
||||||
|
return TableOrder(
|
||||||
|
id: json['id'] ?? 0,
|
||||||
|
nom: json['nom'] ?? '',
|
||||||
|
capacity: json['capacity'] ?? 1,
|
||||||
|
status: json['statut'] ?? 'available',
|
||||||
|
location: json['location'] ?? '',
|
||||||
|
tablename: json['tablename'] ?? '',
|
||||||
|
createdAt: json['created_at'] != null
|
||||||
|
? DateTime.parse(json['created_at'])
|
||||||
|
: DateTime.now(),
|
||||||
|
updatedAt: json['updated_at'] != null
|
||||||
|
? DateTime.parse(json['updated_at'])
|
||||||
|
: DateTime.now(),
|
||||||
|
total: json['total_ht'] != null
|
||||||
|
? double.tryParse(json['total_ht'].toString())
|
||||||
|
: null,
|
||||||
|
isEncashed: json['is_encashed'] ?? false,
|
||||||
|
time: json['time'],
|
||||||
|
date: json['date_commande'] != null
|
||||||
|
? DateTime.parse(json['date_commande']) // tu avais mis updated_at ici par erreur
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
factory TableOrder.fromJson(Map<String, dynamic> json) {
|
|
||||||
return TableOrder(
|
|
||||||
id: json['id'] ?? 0,
|
|
||||||
nom: json['nom'] ?? '',
|
|
||||||
capacity: json['capacity'] ?? 1,
|
|
||||||
status: json['status'] ?? 'available',
|
|
||||||
location: json['location'] ?? '',
|
|
||||||
createdAt:
|
|
||||||
json['created_at'] != null
|
|
||||||
? DateTime.parse(json['created_at'])
|
|
||||||
: DateTime.now(),
|
|
||||||
updatedAt:
|
|
||||||
json['updated_at'] != null
|
|
||||||
? DateTime.parse(json['updated_at'])
|
|
||||||
: DateTime.now(),
|
|
||||||
total: json['total'] != null ? (json['total'] as num).toDouble() : null,
|
|
||||||
isEncashed: json['is_encashed'] ?? false,
|
|
||||||
time: json['time'],
|
|
||||||
date: json['date'],
|
|
||||||
// persons: json['persons'],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return {
|
return {
|
||||||
@ -58,6 +62,7 @@ class TableOrder {
|
|||||||
'capacity': capacity,
|
'capacity': capacity,
|
||||||
'status': status,
|
'status': status,
|
||||||
'location': location,
|
'location': location,
|
||||||
|
'tablename': tablename,
|
||||||
'created_at': createdAt.toIso8601String(),
|
'created_at': createdAt.toIso8601String(),
|
||||||
'updated_at': updatedAt.toIso8601String(),
|
'updated_at': updatedAt.toIso8601String(),
|
||||||
if (total != null) 'total': total,
|
if (total != null) 'total': total,
|
||||||
@ -122,7 +127,7 @@ class TableOrder {
|
|||||||
double? total,
|
double? total,
|
||||||
bool? isEncashed,
|
bool? isEncashed,
|
||||||
String? time,
|
String? time,
|
||||||
String? date,
|
DateTime? date,
|
||||||
int? persons,
|
int? persons,
|
||||||
}) {
|
}) {
|
||||||
return TableOrder(
|
return TableOrder(
|
||||||
@ -153,6 +158,10 @@ class TableOrder {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => id.hashCode;
|
int get hashCode => id.hashCode;
|
||||||
|
|
||||||
|
get items => null;
|
||||||
|
|
||||||
|
where(bool Function(dynamic commande) param0) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Énumération pour les statuts (optionnel, pour plus de type safety)
|
// Énumération pour les statuts (optionnel, pour plus de type safety)
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import './tables.dart';
|
import './tables.dart';
|
||||||
import '../layouts/main_layout.dart';
|
import '../layouts/main_layout.dart';
|
||||||
|
|
||||||
@ -54,6 +55,15 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Méthode pour gérer la touche Entrée
|
||||||
|
void _handleKeyPress(KeyEvent event) {
|
||||||
|
if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.enter) {
|
||||||
|
if (!_isLoading) {
|
||||||
|
_login();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _login() async {
|
void _login() async {
|
||||||
if (!_formKey.currentState!.validate()) return;
|
if (!_formKey.currentState!.validate()) return;
|
||||||
|
|
||||||
@ -99,279 +109,295 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return KeyboardListener(
|
||||||
backgroundColor: const Color(0xFFF5F5F5), // Light gray background
|
focusNode: FocusNode(),
|
||||||
body: Center(
|
onKeyEvent: _handleKeyPress,
|
||||||
child: SingleChildScrollView(
|
child: Scaffold(
|
||||||
padding: const EdgeInsets.all(24.0),
|
backgroundColor: const Color(0xFFF5F5F5), // Light gray background
|
||||||
child: Card(
|
body: Center(
|
||||||
elevation: 2,
|
child: SingleChildScrollView(
|
||||||
shape: RoundedRectangleBorder(
|
padding: const EdgeInsets.all(24.0),
|
||||||
borderRadius: BorderRadius.circular(8),
|
child: Card(
|
||||||
),
|
elevation: 2,
|
||||||
child: Container(
|
shape: RoundedRectangleBorder(
|
||||||
width: 400,
|
borderRadius: BorderRadius.circular(8),
|
||||||
padding: const EdgeInsets.all(40.0),
|
),
|
||||||
child: Form(
|
child: Container(
|
||||||
key: _formKey,
|
width: 400,
|
||||||
child: Column(
|
padding: const EdgeInsets.all(40.0),
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Form(
|
||||||
children: [
|
key: _formKey,
|
||||||
// Logo personnalisé
|
child: Column(
|
||||||
Container(
|
mainAxisSize: MainAxisSize.min,
|
||||||
width: 80,
|
children: [
|
||||||
height: 80,
|
// Logo personnalisé
|
||||||
child: Image.asset(
|
|
||||||
'assets/logo_transparent.png',
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
errorBuilder: (context, error, stackTrace) {
|
|
||||||
// Fallback en cas d'erreur de chargement
|
|
||||||
return Container(
|
|
||||||
width: 64,
|
|
||||||
height: 64,
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: Colors.green,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
Icons.restaurant_menu,
|
|
||||||
color: Colors.white,
|
|
||||||
size: 32,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
|
|
||||||
// Title
|
|
||||||
const Text(
|
|
||||||
'Restaurant App',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
|
|
||||||
// Subtitle
|
|
||||||
const Text(
|
|
||||||
'Connectez-vous pour accéder au système de commandes',
|
|
||||||
style: TextStyle(color: Colors.grey, fontSize: 14),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
|
|
||||||
// Error message
|
|
||||||
if (_errorMessage != null)
|
|
||||||
Container(
|
Container(
|
||||||
width: double.infinity,
|
width: 80,
|
||||||
padding: const EdgeInsets.all(12),
|
height: 80,
|
||||||
margin: const EdgeInsets.only(bottom: 16),
|
child: Image.asset(
|
||||||
decoration: BoxDecoration(
|
'assets/logo_transparent.png',
|
||||||
color: Colors.red.shade50,
|
fit: BoxFit.contain,
|
||||||
border: Border.all(color: Colors.red.shade200),
|
errorBuilder: (context, error, stackTrace) {
|
||||||
borderRadius: BorderRadius.circular(4),
|
// Fallback en cas d'erreur de chargement
|
||||||
),
|
return Container(
|
||||||
child: Text(
|
width: 64,
|
||||||
_errorMessage!,
|
height: 64,
|
||||||
style: TextStyle(
|
decoration: const BoxDecoration(
|
||||||
color: Colors.red.shade600,
|
color: Colors.green,
|
||||||
fontSize: 14,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.center,
|
child: const Icon(
|
||||||
|
Icons.restaurant_menu,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 32,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
// Email label
|
// Title
|
||||||
const Align(
|
const Text(
|
||||||
alignment: Alignment.centerLeft,
|
'Restaurant App',
|
||||||
child: Text(
|
|
||||||
'Email',
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
color: Colors.black87,
|
color: Colors.black87,
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 8),
|
||||||
const SizedBox(height: 8),
|
|
||||||
|
|
||||||
// Email field
|
// Subtitle
|
||||||
TextFormField(
|
const Text(
|
||||||
controller: emailController,
|
'Connectez-vous pour accéder au système de commandes',
|
||||||
decoration: InputDecoration(
|
style: TextStyle(color: Colors.grey, fontSize: 14),
|
||||||
prefixIcon: const Icon(
|
textAlign: TextAlign.center,
|
||||||
Icons.email_outlined,
|
|
||||||
color: Colors.grey,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
hintText: 'serveur@restaurant.com',
|
|
||||||
hintStyle: const TextStyle(color: Colors.grey),
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
borderSide: BorderSide(color: Colors.grey.shade300),
|
|
||||||
),
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
borderSide: BorderSide(color: Colors.grey.shade300),
|
|
||||||
),
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
borderSide: const BorderSide(color: Colors.green),
|
|
||||||
),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 12,
|
|
||||||
vertical: 16,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
keyboardType: TextInputType.emailAddress,
|
const SizedBox(height: 32),
|
||||||
validator: (value) {
|
|
||||||
if (value == null || value.isEmpty) {
|
|
||||||
return 'Veuillez entrer votre email';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
|
|
||||||
// Password label
|
// Error message
|
||||||
const Align(
|
if (_errorMessage != null)
|
||||||
alignment: Alignment.centerLeft,
|
Container(
|
||||||
child: Text(
|
width: double.infinity,
|
||||||
'Mot de passe',
|
padding: const EdgeInsets.all(12),
|
||||||
style: TextStyle(
|
margin: const EdgeInsets.only(bottom: 16),
|
||||||
fontSize: 14,
|
decoration: BoxDecoration(
|
||||||
color: Colors.black87,
|
color: Colors.red.shade50,
|
||||||
fontWeight: FontWeight.w500,
|
border: Border.all(color: Colors.red.shade200),
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
|
|
||||||
// Password field
|
|
||||||
TextFormField(
|
|
||||||
controller: passwordController,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
prefixIcon: const Icon(
|
|
||||||
Icons.lock_outline,
|
|
||||||
color: Colors.grey,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
hintText: '••••••••',
|
|
||||||
hintStyle: const TextStyle(color: Colors.grey),
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
borderSide: BorderSide(color: Colors.grey.shade300),
|
|
||||||
),
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
borderSide: BorderSide(color: Colors.grey.shade300),
|
|
||||||
),
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
borderSide: const BorderSide(color: Colors.green),
|
|
||||||
),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 12,
|
|
||||||
vertical: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
obscureText: true,
|
|
||||||
validator: (value) {
|
|
||||||
if (value == null || value.isEmpty) {
|
|
||||||
return 'Veuillez entrer votre mot de passe';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
|
|
||||||
// Login button
|
|
||||||
SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
height: 48,
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: _isLoading ? null : _login,
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: Colors.green,
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
elevation: 0,
|
child: Text(
|
||||||
),
|
_errorMessage!,
|
||||||
child:
|
|
||||||
_isLoading
|
|
||||||
? const SizedBox(
|
|
||||||
height: 20,
|
|
||||||
width: 20,
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
color: Colors.white,
|
|
||||||
strokeWidth: 2,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: const Text(
|
|
||||||
'Se connecter',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
|
|
||||||
// Demo accounts section
|
|
||||||
Container(
|
|
||||||
width: double.infinity,
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.grey.shade100,
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const Text(
|
|
||||||
'Comptes de démonstration :',
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
color: Colors.red.shade600,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
),
|
||||||
RichText(
|
|
||||||
text: const TextSpan(
|
// Email label
|
||||||
style: TextStyle(
|
const Align(
|
||||||
fontSize: 13,
|
alignment: Alignment.centerLeft,
|
||||||
color: Colors.grey,
|
child: Text(
|
||||||
height: 1.4,
|
'Email',
|
||||||
),
|
style: TextStyle(
|
||||||
children: [
|
fontSize: 14,
|
||||||
TextSpan(
|
color: Colors.black87,
|
||||||
text: 'Serveur : ',
|
fontWeight: FontWeight.w500,
|
||||||
style: TextStyle(fontWeight: FontWeight.w500),
|
|
||||||
),
|
|
||||||
TextSpan(
|
|
||||||
text: 'serveur@restaurant.com / serveur123\n',
|
|
||||||
),
|
|
||||||
TextSpan(
|
|
||||||
text: 'Admin : ',
|
|
||||||
style: TextStyle(fontWeight: FontWeight.w500),
|
|
||||||
),
|
|
||||||
TextSpan(
|
|
||||||
text: 'admin@restaurant.com / admin123',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 8),
|
||||||
],
|
|
||||||
|
// Email field
|
||||||
|
TextFormField(
|
||||||
|
controller: emailController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
prefixIcon: const Icon(
|
||||||
|
Icons.email_outlined,
|
||||||
|
color: Colors.grey,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
hintText: 'serveur@restaurant.com',
|
||||||
|
hintStyle: const TextStyle(color: Colors.grey),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
borderSide: BorderSide(color: Colors.grey.shade300),
|
||||||
|
),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
borderSide: BorderSide(color: Colors.grey.shade300),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
borderSide: const BorderSide(color: Colors.green),
|
||||||
|
),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 12,
|
||||||
|
vertical: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
textInputAction: TextInputAction.next,
|
||||||
|
validator: (value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return 'Veuillez entrer votre email';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
|
// Password label
|
||||||
|
const Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Text(
|
||||||
|
'Mot de passe',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.black87,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
|
||||||
|
// Password field
|
||||||
|
TextFormField(
|
||||||
|
controller: passwordController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
prefixIcon: const Icon(
|
||||||
|
Icons.lock_outline,
|
||||||
|
color: Colors.grey,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
hintText: '••••••••',
|
||||||
|
hintStyle: const TextStyle(color: Colors.grey),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
borderSide: BorderSide(color: Colors.grey.shade300),
|
||||||
|
),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
borderSide: BorderSide(color: Colors.grey.shade300),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
borderSide: const BorderSide(color: Colors.green),
|
||||||
|
),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 12,
|
||||||
|
vertical: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
obscureText: true,
|
||||||
|
textInputAction: TextInputAction.done,
|
||||||
|
onFieldSubmitted: (_) => _login(), // Validation quand on appuie sur Entrée
|
||||||
|
validator: (value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return 'Veuillez entrer votre mot de passe';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
// Login button
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 48,
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: _isLoading ? null : _login,
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
),
|
||||||
|
elevation: 0,
|
||||||
|
),
|
||||||
|
child:
|
||||||
|
_isLoading
|
||||||
|
? const SizedBox(
|
||||||
|
height: 20,
|
||||||
|
width: 20,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: Colors.white,
|
||||||
|
strokeWidth: 2,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const Text(
|
||||||
|
'Se connecter',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
// Demo accounts section
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey.shade100,
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Comptes de démonstration :',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
RichText(
|
||||||
|
text: const TextSpan(
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
color: Colors.grey,
|
||||||
|
height: 1.4,
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: 'Serveur : ',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.w500),
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: 'serveur@restaurant.com / serveur123\n',
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: 'Admin : ',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.w500),
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: 'admin@restaurant.com / admin123',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Text(
|
||||||
|
'Astuce : Appuyez sur Entrée pour vous connecter',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.grey,
|
||||||
|
fontStyle: FontStyle.italic,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -21,7 +21,7 @@ class RestaurantApiService {
|
|||||||
static Future<List<TableOrder>> getCommandes() async {
|
static Future<List<TableOrder>> getCommandes() async {
|
||||||
try {
|
try {
|
||||||
final response = await http
|
final response = await http
|
||||||
.get(Uri.parse('$baseUrl/api/commandes'), headers: _headers)
|
.get(Uri.parse('$baseUrl/api/commandes?statut=servie'), headers: _headers)
|
||||||
.timeout(
|
.timeout(
|
||||||
const Duration(seconds: 30),
|
const Duration(seconds: 30),
|
||||||
onTimeout: () => throw TimeoutException('Délai d\'attente dépassé'),
|
onTimeout: () => throw TimeoutException('Délai d\'attente dépassé'),
|
||||||
@ -171,6 +171,7 @@ class RestaurantApiService {
|
|||||||
totalTtc: 14.00,
|
totalTtc: 14.00,
|
||||||
modePaiement: null,
|
modePaiement: null,
|
||||||
commentaires: null,
|
commentaires: null,
|
||||||
|
tablename: 'a',
|
||||||
serveur: "Serveur par défaut",
|
serveur: "Serveur par défaut",
|
||||||
dateCommande: DateTime.parse("2025-08-02T15:03:44.000Z"),
|
dateCommande: DateTime.parse("2025-08-02T15:03:44.000Z"),
|
||||||
dateService: null,
|
dateService: null,
|
||||||
@ -234,7 +235,6 @@ class RestaurantApiService {
|
|||||||
updatedAt: DateTime.now(),
|
updatedAt: DateTime.now(),
|
||||||
total: 27.00,
|
total: 27.00,
|
||||||
time: '00:02',
|
time: '00:02',
|
||||||
date: '02/08/2025',
|
|
||||||
),
|
),
|
||||||
// Ajoutez d'autres tables de test...
|
// Ajoutez d'autres tables de test...
|
||||||
];
|
];
|
||||||
|
|||||||
@ -12,8 +12,13 @@ class CommandeCard extends StatelessWidget {
|
|||||||
required this.onAllerCaisse,
|
required this.onAllerCaisse,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
String _formatTime(DateTime dateTime) {
|
||||||
|
return '${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')} ${dateTime.day.toString().padLeft(2, '0')}/${dateTime.month.toString().padLeft(2, '0')}/${dateTime.year}';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
margin: EdgeInsets.only(bottom: 16),
|
margin: EdgeInsets.only(bottom: 16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@ -38,7 +43,7 @@ class CommandeCard extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Table ${commande.tableNumber}',
|
' ${commande.tablename}',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@ -57,7 +62,7 @@ class CommandeCard extends StatelessWidget {
|
|||||||
Icon(Icons.check_circle, color: Colors.white, size: 16),
|
Icon(Icons.check_circle, color: Colors.white, size: 16),
|
||||||
SizedBox(width: 4),
|
SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
'À encaisser',
|
'Près à encaisser',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
@ -78,7 +83,8 @@ class CommandeCard extends StatelessWidget {
|
|||||||
Icon(Icons.access_time, size: 16, color: Colors.grey[600]),
|
Icon(Icons.access_time, size: 16, color: Colors.grey[600]),
|
||||||
SizedBox(width: 6),
|
SizedBox(width: 6),
|
||||||
Text(
|
Text(
|
||||||
'${commande.time} • ${commande.date} ',
|
// Fixed: Pass DateTime directly, not as string
|
||||||
|
commande.date != null ? _formatTime(commande.date!) : 'Date non disponible',
|
||||||
style: TextStyle(color: Colors.grey[600], fontSize: 14),
|
style: TextStyle(color: Colors.grey[600], fontSize: 14),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -99,7 +105,7 @@ class CommandeCard extends StatelessWidget {
|
|||||||
style: TextStyle(color: Colors.grey[600], fontSize: 14),
|
style: TextStyle(color: Colors.grey[600], fontSize: 14),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'${commande.total?.toStringAsFixed(2)} MGA',
|
'${commande.total?.toStringAsFixed(2) ?? '0.00'} MGA',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user