style: redesign equipment list cards to improve spacing and center-align the availability badge

This commit is contained in:
ElPoyo
2026-05-26 14:54:38 +02:00
parent f8f6cfb102
commit 854b0a9bb0
+100 -41
View File
@@ -528,52 +528,85 @@ class _EquipmentManagementPageState extends State<EquipmentManagementPage>
key: ValueKey(equipment.id), key: ValueKey(equipment.id),
child: Card( child: Card(
margin: const EdgeInsets.only(bottom: 12), margin: const EdgeInsets.only(bottom: 12),
elevation: 1,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
side: BorderSide(
color: isSelectionMode && isSelected color: isSelectionMode && isSelected
? AppColors.rouge.withValues(alpha: 0.1) ? AppColors.rouge
: null, : Colors.grey.shade200,
child: ListTile( width: isSelectionMode && isSelected ? 2 : 1,
leading: isSelectionMode ),
? Checkbox( ),
color: isSelectionMode && isSelected
? AppColors.rouge.withValues(alpha: 0.05)
: Colors.white,
child: InkWell(
borderRadius: BorderRadius.circular(12),
onTap: isSelectionMode
? () => toggleItemSelection(equipment.id)
: () => _viewEquipmentDetails(equipment),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// 1. leading selection or icon
if (isSelectionMode)
Padding(
padding: const EdgeInsets.only(right: 12),
child: Checkbox(
value: isSelected, value: isSelected,
onChanged: (value) => toggleItemSelection(equipment.id), onChanged: (value) => toggleItemSelection(equipment.id),
activeColor: AppColors.rouge, activeColor: AppColors.rouge,
),
) )
: CircleAvatar( else
backgroundColor: Padding(
equipment.category.color.withValues(alpha: 0.2), padding: const EdgeInsets.only(right: 16),
child: Container(
width: 44,
height: 44,
decoration: BoxDecoration(
color: equipment.category.color.withValues(alpha: 0.15),
borderRadius: BorderRadius.circular(10),
),
child: Center(
child: equipment.category.getIcon( child: equipment.category.getIcon(
size: 20, size: 22,
color: equipment.category.color, color: equipment.category.color,
), ),
), ),
title: Row( ),
children: [ ),
// 2. Info details (ID, Brand/Model, Subcategory/Quantity)
Expanded( Expanded(
child: Text( child: Column(
equipment.id,
style: const TextStyle(fontWeight: FontWeight.bold),
),
),
// Afficher le badge de statut calculé dynamiquement
if (equipment.category != EquipmentCategory.consumable &&
equipment.category != EquipmentCategory.cable)
EquipmentStatusBadge(equipment: equipment),
],
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text(
equipment.id,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
color: AppColors.noir,
),
),
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
'${equipment.brand ?? ''} ${equipment.model ?? ''}' '${equipment.brand ?? ''} ${equipment.model ?? ''}'
.trim() .trim()
.isNotEmpty .isNotEmpty
? '${equipment.brand ?? ''} ${equipment.model ?? ''}' ? '${equipment.brand ?? ''} ${equipment.model ?? ''}'.trim()
.trim()
: 'Marque/Modèle non défini', : 'Marque/Modèle non défini',
style: TextStyle(color: Colors.grey[600], fontSize: 14), style: TextStyle(
color: Colors.grey[700],
fontSize: 13,
), ),
// Afficher la sous-catégorie si elle existe ),
// Sous-catégorie
if (equipment.subCategory != null && if (equipment.subCategory != null &&
equipment.subCategory!.isNotEmpty) ...[ equipment.subCategory!.isNotEmpty) ...[
const SizedBox(height: 2), const SizedBox(height: 2),
@@ -581,22 +614,34 @@ class _EquipmentManagementPageState extends State<EquipmentManagementPage>
'📁 ${equipment.subCategory}', '📁 ${equipment.subCategory}',
style: TextStyle( style: TextStyle(
color: Colors.grey[500], color: Colors.grey[500],
fontSize: 12, fontSize: 11,
fontStyle: FontStyle.italic, fontStyle: FontStyle.italic,
), ),
), ),
], ],
// Afficher la quantité disponible pour les consommables/câbles // Quantité pour consommables
if (equipment.category == EquipmentCategory.consumable || if (equipment.category == EquipmentCategory.consumable ||
equipment.category == EquipmentCategory.cable) ...[ equipment.category == EquipmentCategory.cable) ...[
const SizedBox(height: 4), const SizedBox(height: 6),
_buildQuantityDisplay(equipment), _buildQuantityDisplay(equipment),
], ],
], ],
), ),
trailing: isSelectionMode ),
? null
: Row( const SizedBox(width: 16),
// 3. Status Badge (centered vertically in the row!)
if (equipment.category != EquipmentCategory.consumable &&
equipment.category != EquipmentCategory.cable)
Padding(
padding: const EdgeInsets.only(right: 16),
child: EquipmentStatusBadge(equipment: equipment),
),
// 4. Trailing Action Buttons
if (!isSelectionMode)
Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
// Bouton Restock (uniquement pour consommables/câbles avec permission) // Bouton Restock (uniquement pour consommables/câbles avec permission)
@@ -606,46 +651,60 @@ class _EquipmentManagementPageState extends State<EquipmentManagementPage>
requiredPermissions: const ['manage_equipment'], requiredPermissions: const ['manage_equipment'],
child: IconButton( child: IconButton(
icon: const Icon(Icons.add_shopping_cart, icon: const Icon(Icons.add_shopping_cart,
color: AppColors.rouge), color: AppColors.rouge, size: 20),
tooltip: 'Restock', tooltip: 'Restock',
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
onPressed: () => _showRestockDialog(equipment), onPressed: () => _showRestockDialog(equipment),
), ),
), ),
if (equipment.category == EquipmentCategory.consumable ||
equipment.category == EquipmentCategory.cable)
const SizedBox(width: 8),
// Bouton QR Code // Bouton QR Code
IconButton( IconButton(
icon: const Icon(Icons.qr_code, color: AppColors.rouge), icon: const Icon(Icons.qr_code, color: AppColors.rouge, size: 20),
tooltip: 'QR Code', tooltip: 'QR Code',
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
onPressed: () => showDialog( onPressed: () => showDialog(
context: context, context: context,
builder: (context) => builder: (context) =>
QRCodeDialog.forEquipment(equipment), QRCodeDialog.forEquipment(equipment),
), ),
), ),
const SizedBox(width: 8),
// Bouton Modifier (permission required) // Bouton Modifier (permission required)
PermissionGate( PermissionGate(
requiredPermissions: const ['manage_equipment'], requiredPermissions: const ['manage_equipment'],
child: IconButton( child: IconButton(
icon: const Icon(Icons.edit, color: AppColors.rouge), icon: const Icon(Icons.edit, color: AppColors.rouge, size: 20),
tooltip: 'Modifier', tooltip: 'Modifier',
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
onPressed: () => _editEquipment(equipment), onPressed: () => _editEquipment(equipment),
), ),
), ),
const SizedBox(width: 8),
// Bouton Supprimer (permission required) // Bouton Supprimer (permission required)
PermissionGate( PermissionGate(
requiredPermissions: const ['manage_equipment'], requiredPermissions: const ['manage_equipment'],
child: IconButton( child: IconButton(
icon: const Icon(Icons.delete, color: Colors.red), icon: const Icon(Icons.delete, color: Colors.red, size: 20),
tooltip: 'Supprimer', tooltip: 'Supprimer',
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
onPressed: () => _deleteEquipment(equipment), onPressed: () => _deleteEquipment(equipment),
), ),
), ),
], ],
), ),
onTap: isSelectionMode ],
? () => toggleItemSelection(equipment.id)
: () => _viewEquipmentDetails(equipment),
), ),
)); ),
),
),
);
} }
Widget _buildQuantityDisplay(EquipmentModel equipment) { Widget _buildQuantityDisplay(EquipmentModel equipment) {