301 lines
27 KiB
Plaintext
301 lines
27 KiB
Plaintext
color: Colors.white,
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildEquipmentCard(EquipmentModel equipment, {Key? key}) {
|
|
final isSelected = _selectedItems.containsKey(equipment.id);
|
|
final isConsumable = equipment.category == EquipmentCategory.consumable ||
|
|
equipment.category == EquipmentCategory.cable;
|
|
final availableQty = _availableQuantities[equipment.id];
|
|
final selectedItem = _selectedItems[equipment.id];
|
|
final hasConflict = _conflictingEquipmentIds.contains(equipment.id); // CORRECTION ICI !
|
|
final conflictDetails = _getConflictDetailsFor(equipment.id);
|
|
|
|
// Bloquer la sélection si en conflit et non forcé
|
|
final canSelect = !hasConflict || isSelected;
|
|
|
|
return RepaintBoundary(
|
|
key: key,
|
|
child: Card(
|
|
margin: const EdgeInsets.only(bottom: 12),
|
|
elevation: isSelected ? 4 : 1,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
side: isSelected
|
|
? const BorderSide(color: AppColors.rouge, width: 2)
|
|
: hasConflict
|
|
? BorderSide(color: Colors.orange.shade300, width: 1)
|
|
: BorderSide.none,
|
|
),
|
|
child: InkWell(
|
|
onTap: canSelect
|
|
? () => _toggleSelection(
|
|
equipment.id,
|
|
equipment.id,
|
|
SelectionType.equipment,
|
|
maxQuantity: availableQty,
|
|
)
|
|
: null,
|
|
borderRadius: BorderRadius.circular(8),
|
|
child: Container(
|
|
decoration: hasConflict && !isSelected
|
|
? BoxDecoration(
|
|
color: Colors.orange.shade50,
|
|
borderRadius: BorderRadius.circular(8),
|
|
)
|
|
: null,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16),
|
|
child: Column(
|
|
children: [
|
|
Row(
|
|
children: [
|
|
// Checkbox
|
|
Checkbox(
|
|
value: isSelected,
|
|
onChanged: canSelect
|
|
? (value) => _toggleSelection(
|
|
equipment.id,
|
|
equipment.id,
|
|
SelectionType.equipment,
|
|
maxQuantity: availableQty,
|
|
)
|
|
: null,
|
|
activeColor: AppColors.rouge,
|
|
),
|
|
|
|
const SizedBox(width: 12),
|
|
|
|
// Icône
|
|
equipment.category.getIcon(size: 32, color: equipment.category.color),
|
|
|
|
const SizedBox(width: 16),
|
|
|
|
// Infos
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
equipment.id,
|
|
style: const TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 16,
|
|
),
|
|
),
|
|
),
|
|
if (hasConflict)
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
color: Colors.orange.shade700,
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
const Icon(Icons.warning, size: 14, color: Colors.white),
|
|
const SizedBox(width: 4),
|
|
Text(
|
|
'Déjà utilisé',
|
|
style: const TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
if (equipment.brand != null || equipment.model != null)
|
|
Text(
|
|
'${equipment.brand ?? ''} ${equipment.model ?? ''}'.trim(),
|
|
style: TextStyle(
|
|
color: Colors.grey.shade700,
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
// Affichage des boîtes parentes
|
|
if (_getParentContainers(equipment.id).isNotEmpty)
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 4),
|
|
child: Wrap(
|
|
spacing: 4,
|
|
runSpacing: 4,
|
|
children: _getParentContainers(equipment.id).map((container) {
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
|
decoration: BoxDecoration(
|
|
color: Colors.blue.shade50,
|
|
border: Border.all(color: Colors.blue.shade300),
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(Icons.inventory, size: 12, color: Colors.blue.shade700),
|
|
const SizedBox(width: 4),
|
|
Text(
|
|
container.name,
|
|
style: TextStyle(
|
|
fontSize: 10,
|
|
color: Colors.blue.shade700,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}).toList(),
|
|
),
|
|
),
|
|
if (isConsumable)
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 4),
|
|
child: _buildQuantityInfo(equipment),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
// Sélecteur de quantité pour consommables (toujours affiché)
|
|
if (isConsumable && availableQty != null)
|
|
_buildQuantitySelector(
|
|
equipment.id,
|
|
selectedItem ?? SelectedItem(
|
|
id: equipment.id,
|
|
name: equipment.id,
|
|
type: SelectionType.equipment,
|
|
quantity: 0, // Quantité 0 si non sélectionné
|
|
),
|
|
availableQty,
|
|
isSelected: isSelected, // Passer l'état de sélection
|
|
),
|
|
],
|
|
),
|
|
|
|
// Affichage des conflits
|
|
if (hasConflict)
|
|
Container(
|
|
margin: const EdgeInsets.only(top: 12),
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: Colors.orange.shade100,
|
|
borderRadius: BorderRadius.circular(8),
|
|
border: Border.all(color: Colors.orange.shade300),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Icon(Icons.info_outline, size: 16, color: Colors.orange.shade900),
|
|
const SizedBox(width: 6),
|
|
Text(
|
|
'Utilisé sur ${conflictDetails.length} événement(s) :',
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.orange.shade900,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 6),
|
|
...conflictDetails.take(2).map((detail) {
|
|
final eventName = detail['eventName'] as String? ?? 'Événement inconnu';
|
|
final viaContainer = detail['viaContainer'] as String?;
|
|
final viaContainerName = detail['viaContainerName'] as String?;
|
|
|
|
return Padding(
|
|
padding: const EdgeInsets.only(left: 22, top: 4),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'• $eventName',
|
|
style: TextStyle(
|
|
fontSize: 11,
|
|
color: Colors.orange.shade800,
|
|
),
|
|
),
|
|
if (viaContainer != null)
|
|
Padding(
|
|
padding: const EdgeInsets.only(left: 8),
|
|
child: Text(
|
|
'via ${viaContainerName ?? viaContainer}',
|
|
style: TextStyle(
|
|
fontSize: 10,
|
|
color: Colors.grey.shade600,
|
|
fontStyle: FontStyle.italic,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}),
|
|
if (conflictDetails.length > 2)
|
|
Padding(
|
|
padding: const EdgeInsets.only(left: 22, top: 4),
|
|
child: Text(
|
|
'... et ${conflictDetails.length - 2} autre(s)',
|
|
style: TextStyle(
|
|
fontSize: 11,
|
|
color: Colors.orange.shade800,
|
|
fontStyle: FontStyle.italic,
|
|
),
|
|
),
|
|
),
|
|
if (!isSelected)
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 8),
|
|
child: TextButton.icon(
|
|
onPressed: () => _toggleSelection(
|
|
equipment.id,
|
|
equipment.id,
|
|
SelectionType.equipment,
|
|
maxQuantity: availableQty,
|
|
force: true,
|
|
),
|
|
icon: const Icon(Icons.warning, size: 16),
|
|
label: const Text('Forcer quand même', style: TextStyle(fontSize: 12)),
|
|
style: TextButton.styleFrom(
|
|
foregroundColor: Colors.orange.shade900,
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Widget pour le sélecteur de quantité
|
|
/// Si isSelected = false, le premier clic sur + sélectionne l'item avec quantité 1
|
|
Widget _buildQuantitySelector(
|
|
String equipmentId,
|
|
SelectedItem selectedItem,
|
|
int maxQuantity, {
|
|
required bool isSelected,
|
|
}) {
|
|
final displayQuantity = isSelected ? selectedItem.quantity : 0;
|