|
|
@ -61,7 +61,7 @@ class _TablesScreenState extends State<TablesScreen> { |
|
|
case 'available': |
|
|
case 'available': |
|
|
return Colors.green; |
|
|
return Colors.green; |
|
|
case 'occupied': |
|
|
case 'occupied': |
|
|
return Colors.red; |
|
|
return Colors.orange; |
|
|
case 'reserved': |
|
|
case 'reserved': |
|
|
return Colors.orange; |
|
|
return Colors.orange; |
|
|
default: |
|
|
default: |
|
|
@ -82,6 +82,10 @@ class _TablesScreenState extends State<TablesScreen> { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool isTableSelectable(String status) { |
|
|
|
|
|
return status == 'available'; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
@override |
|
|
@override |
|
|
Widget build(BuildContext context) { |
|
|
Widget build(BuildContext context) { |
|
|
final screenWidth = MediaQuery.of(context).size.width; |
|
|
final screenWidth = MediaQuery.of(context).size.width; |
|
|
@ -94,96 +98,170 @@ class _TablesScreenState extends State<TablesScreen> { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return Scaffold( |
|
|
return Scaffold( |
|
|
appBar: AppBar(title: const Text('Sélectionner une table')), |
|
|
backgroundColor: Colors.grey.shade50, |
|
|
body: isLoading |
|
|
body: Column( |
|
|
|
|
|
children: [ |
|
|
|
|
|
// Header |
|
|
|
|
|
Container( |
|
|
|
|
|
width: double.infinity, |
|
|
|
|
|
color: Colors.white, |
|
|
|
|
|
padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 20), |
|
|
|
|
|
child: Column( |
|
|
|
|
|
children: [ |
|
|
|
|
|
const Text( |
|
|
|
|
|
'Sélectionner une table', |
|
|
|
|
|
style: TextStyle( |
|
|
|
|
|
fontSize: 24, |
|
|
|
|
|
fontWeight: FontWeight.bold, |
|
|
|
|
|
color: Colors.black87, |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
const SizedBox(height: 8), |
|
|
|
|
|
Text( |
|
|
|
|
|
'Choisissez une table pour commencer une nouvelle commande', |
|
|
|
|
|
style: TextStyle(fontSize: 14, color: Colors.grey.shade600), |
|
|
|
|
|
), |
|
|
|
|
|
], |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
|
|
|
|
|
|
// Tables Grid |
|
|
|
|
|
Expanded( |
|
|
|
|
|
child: |
|
|
|
|
|
isLoading |
|
|
? const Center(child: CircularProgressIndicator()) |
|
|
? const Center(child: CircularProgressIndicator()) |
|
|
: Padding( |
|
|
: Padding( |
|
|
padding: const EdgeInsets.all(12.0), |
|
|
padding: const EdgeInsets.all(20.0), |
|
|
|
|
|
child: Column( |
|
|
|
|
|
children: [ |
|
|
|
|
|
Expanded( |
|
|
child: GridView.builder( |
|
|
child: GridView.builder( |
|
|
itemCount: tables.length, |
|
|
itemCount: tables.length, |
|
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( |
|
|
gridDelegate: |
|
|
|
|
|
SliverGridDelegateWithFixedCrossAxisCount( |
|
|
crossAxisCount: crossAxisCount, |
|
|
crossAxisCount: crossAxisCount, |
|
|
crossAxisSpacing: 12, |
|
|
crossAxisSpacing: 16, |
|
|
mainAxisSpacing: 12, |
|
|
mainAxisSpacing: 16, |
|
|
childAspectRatio: 2.3, // ⬅️ plus plat ici |
|
|
childAspectRatio: 1.3, |
|
|
), |
|
|
), |
|
|
itemBuilder: (context, index) { |
|
|
itemBuilder: (context, index) { |
|
|
final table = tables[index]; |
|
|
final table = tables[index]; |
|
|
final isAvailable = table.status == 'available'; |
|
|
final isSelectable = isTableSelectable( |
|
|
|
|
|
table.status, |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
return Card( |
|
|
return Container( |
|
|
elevation: 1.5, |
|
|
decoration: BoxDecoration( |
|
|
shape: RoundedRectangleBorder( |
|
|
color: Colors.white, |
|
|
borderRadius: BorderRadius.circular(14), |
|
|
borderRadius: BorderRadius.circular(12), |
|
|
|
|
|
border: Border.all( |
|
|
|
|
|
color: Colors.grey.shade200, |
|
|
|
|
|
), |
|
|
|
|
|
boxShadow: [ |
|
|
|
|
|
BoxShadow( |
|
|
|
|
|
color: Colors.grey.shade100, |
|
|
|
|
|
blurRadius: 4, |
|
|
|
|
|
offset: const Offset(0, 2), |
|
|
|
|
|
), |
|
|
|
|
|
], |
|
|
), |
|
|
), |
|
|
child: Padding( |
|
|
child: Padding( |
|
|
padding: const EdgeInsets.all(10), |
|
|
padding: const EdgeInsets.all(16), |
|
|
child: Column( |
|
|
child: Column( |
|
|
crossAxisAlignment: CrossAxisAlignment.start, |
|
|
crossAxisAlignment: |
|
|
|
|
|
CrossAxisAlignment.start, |
|
|
children: [ |
|
|
children: [ |
|
|
// Titre + badge |
|
|
// Table name and status badge |
|
|
Row( |
|
|
Row( |
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
|
mainAxisAlignment: |
|
|
|
|
|
MainAxisAlignment.spaceBetween, |
|
|
children: [ |
|
|
children: [ |
|
|
Text( |
|
|
Text( |
|
|
table.nom, |
|
|
table.nom, |
|
|
style: const TextStyle( |
|
|
style: const TextStyle( |
|
|
fontWeight: FontWeight.bold, |
|
|
fontWeight: FontWeight.bold, |
|
|
fontSize: 13, |
|
|
fontSize: 16, |
|
|
|
|
|
color: Colors.black87, |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
Container( |
|
|
Container( |
|
|
padding: const EdgeInsets.symmetric( |
|
|
padding: |
|
|
horizontal: 8, |
|
|
const EdgeInsets.symmetric( |
|
|
vertical: 2, |
|
|
horizontal: 12, |
|
|
|
|
|
vertical: 4, |
|
|
), |
|
|
), |
|
|
decoration: BoxDecoration( |
|
|
decoration: BoxDecoration( |
|
|
color: getStatusColor(table.status), |
|
|
color: getStatusColor( |
|
|
borderRadius: BorderRadius.circular(50), |
|
|
table.status, |
|
|
|
|
|
), |
|
|
|
|
|
borderRadius: |
|
|
|
|
|
BorderRadius.circular(20), |
|
|
), |
|
|
), |
|
|
child: Text( |
|
|
child: Text( |
|
|
getStatusLabel(table.status), |
|
|
getStatusLabel(table.status), |
|
|
style: const TextStyle( |
|
|
style: const TextStyle( |
|
|
color: Colors.white, |
|
|
color: Colors.white, |
|
|
fontSize: 10, |
|
|
fontSize: 12, |
|
|
|
|
|
fontWeight: FontWeight.w500, |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
], |
|
|
], |
|
|
), |
|
|
), |
|
|
const Spacer(), |
|
|
|
|
|
|
|
|
const SizedBox(height: 12), |
|
|
|
|
|
|
|
|
|
|
|
// Capacity |
|
|
Row( |
|
|
Row( |
|
|
children: [ |
|
|
children: [ |
|
|
const Icon(Icons.people_outline, |
|
|
Icon( |
|
|
size: 14, color: Colors.grey), |
|
|
Icons.people_outline, |
|
|
const SizedBox(width: 4), |
|
|
size: 16, |
|
|
|
|
|
color: Colors.grey.shade600, |
|
|
|
|
|
), |
|
|
|
|
|
const SizedBox(width: 6), |
|
|
Text( |
|
|
Text( |
|
|
'${table.capacity} personnes', |
|
|
'Capacité: ${table.capacity} personnes', |
|
|
style: const TextStyle( |
|
|
style: TextStyle( |
|
|
fontSize: 11.5, |
|
|
fontSize: 13, |
|
|
color: Colors.grey, |
|
|
color: Colors.grey.shade600, |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
], |
|
|
], |
|
|
), |
|
|
), |
|
|
const SizedBox(height: 8), |
|
|
|
|
|
|
|
|
const Spacer(), |
|
|
|
|
|
|
|
|
|
|
|
// Button |
|
|
SizedBox( |
|
|
SizedBox( |
|
|
width: double.infinity, |
|
|
width: double.infinity, |
|
|
height: 30, |
|
|
height: 36, |
|
|
child: ElevatedButton( |
|
|
child: ElevatedButton( |
|
|
onPressed: isAvailable ? () {} : null, |
|
|
onPressed: |
|
|
|
|
|
isSelectable ? () {} : null, |
|
|
style: ElevatedButton.styleFrom( |
|
|
style: ElevatedButton.styleFrom( |
|
|
backgroundColor: Colors.deepOrange, |
|
|
backgroundColor: |
|
|
padding: EdgeInsets.zero, |
|
|
isSelectable |
|
|
|
|
|
? Colors.green.shade700 |
|
|
|
|
|
: Colors.grey.shade300, |
|
|
|
|
|
foregroundColor: Colors.white, |
|
|
|
|
|
elevation: 0, |
|
|
shape: RoundedRectangleBorder( |
|
|
shape: RoundedRectangleBorder( |
|
|
borderRadius: BorderRadius.circular(6), |
|
|
borderRadius: |
|
|
|
|
|
BorderRadius.circular(8), |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
child: const Text( |
|
|
child: Text( |
|
|
"Réserver", |
|
|
isSelectable |
|
|
|
|
|
? "Sélectionner" |
|
|
|
|
|
: "Indisponible", |
|
|
style: TextStyle( |
|
|
style: TextStyle( |
|
|
color: Colors.white, |
|
|
fontSize: 13, |
|
|
fontSize: 12, |
|
|
fontWeight: FontWeight.w500, |
|
|
|
|
|
color: |
|
|
|
|
|
isSelectable |
|
|
|
|
|
? Colors.white |
|
|
|
|
|
: Colors.grey.shade600, |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
@ -195,6 +273,120 @@ class _TablesScreenState extends State<TablesScreen> { |
|
|
}, |
|
|
}, |
|
|
), |
|
|
), |
|
|
), |
|
|
), |
|
|
|
|
|
|
|
|
|
|
|
// Legend |
|
|
|
|
|
const SizedBox(height: 20), |
|
|
|
|
|
Row( |
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.center, |
|
|
|
|
|
children: [ |
|
|
|
|
|
_buildLegendItem('Disponible', Colors.green), |
|
|
|
|
|
const SizedBox(width: 24), |
|
|
|
|
|
_buildLegendItem( |
|
|
|
|
|
'Occupée', |
|
|
|
|
|
Colors.green.shade800, |
|
|
|
|
|
), |
|
|
|
|
|
const SizedBox(width: 24), |
|
|
|
|
|
_buildLegendItem('Réservée', Colors.orange), |
|
|
|
|
|
], |
|
|
|
|
|
), |
|
|
|
|
|
], |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
|
|
|
|
|
|
// Bottom Navigation |
|
|
|
|
|
Container( |
|
|
|
|
|
color: Colors.white, |
|
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), |
|
|
|
|
|
child: Row( |
|
|
|
|
|
children: [ |
|
|
|
|
|
Container( |
|
|
|
|
|
padding: const EdgeInsets.symmetric( |
|
|
|
|
|
horizontal: 16, |
|
|
|
|
|
vertical: 8, |
|
|
|
|
|
), |
|
|
|
|
|
decoration: BoxDecoration( |
|
|
|
|
|
color: Colors.green.shade700, |
|
|
|
|
|
borderRadius: BorderRadius.circular(20), |
|
|
|
|
|
), |
|
|
|
|
|
child: const Row( |
|
|
|
|
|
mainAxisSize: MainAxisSize.min, |
|
|
|
|
|
children: [ |
|
|
|
|
|
Icon( |
|
|
|
|
|
Icons.table_restaurant, |
|
|
|
|
|
color: Colors.white, |
|
|
|
|
|
size: 16, |
|
|
|
|
|
), |
|
|
|
|
|
SizedBox(width: 6), |
|
|
|
|
|
Text( |
|
|
|
|
|
'Tables', |
|
|
|
|
|
style: TextStyle( |
|
|
|
|
|
color: Colors.white, |
|
|
|
|
|
fontWeight: FontWeight.w500, |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
], |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
const SizedBox(width: 20), |
|
|
|
|
|
Row( |
|
|
|
|
|
children: [ |
|
|
|
|
|
Icon( |
|
|
|
|
|
Icons.receipt_long_outlined, |
|
|
|
|
|
color: Colors.grey.shade600, |
|
|
|
|
|
size: 16, |
|
|
|
|
|
), |
|
|
|
|
|
const SizedBox(width: 6), |
|
|
|
|
|
Text( |
|
|
|
|
|
'Commandes', |
|
|
|
|
|
style: TextStyle(color: Colors.grey.shade600), |
|
|
|
|
|
), |
|
|
|
|
|
], |
|
|
|
|
|
), |
|
|
|
|
|
const Spacer(), |
|
|
|
|
|
Row( |
|
|
|
|
|
children: [ |
|
|
|
|
|
Icon( |
|
|
|
|
|
Icons.person_outline, |
|
|
|
|
|
color: Colors.grey.shade600, |
|
|
|
|
|
size: 16, |
|
|
|
|
|
), |
|
|
|
|
|
const SizedBox(width: 6), |
|
|
|
|
|
Text( |
|
|
|
|
|
'Chef Pierre', |
|
|
|
|
|
style: TextStyle(color: Colors.grey.shade600), |
|
|
|
|
|
), |
|
|
|
|
|
const SizedBox(width: 8), |
|
|
|
|
|
Icon( |
|
|
|
|
|
Icons.expand_more, |
|
|
|
|
|
color: Colors.grey.shade600, |
|
|
|
|
|
size: 16, |
|
|
|
|
|
), |
|
|
|
|
|
], |
|
|
|
|
|
), |
|
|
|
|
|
], |
|
|
|
|
|
), |
|
|
|
|
|
), |
|
|
|
|
|
], |
|
|
|
|
|
), |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Widget _buildLegendItem(String label, Color color) { |
|
|
|
|
|
return Row( |
|
|
|
|
|
mainAxisSize: MainAxisSize.min, |
|
|
|
|
|
children: [ |
|
|
|
|
|
Container( |
|
|
|
|
|
width: 12, |
|
|
|
|
|
height: 12, |
|
|
|
|
|
decoration: BoxDecoration(color: color, shape: BoxShape.circle), |
|
|
|
|
|
), |
|
|
|
|
|
const SizedBox(width: 6), |
|
|
|
|
|
Text( |
|
|
|
|
|
label, |
|
|
|
|
|
style: TextStyle(fontSize: 13, color: Colors.grey.shade700), |
|
|
|
|
|
), |
|
|
|
|
|
], |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|