feat: add current events section for equipment with dynamic status calculation
This commit is contained in:
@@ -87,40 +87,45 @@ class _EventPreparationButtonsState extends State<EventPreparationButtons> {
|
||||
children: [
|
||||
// Bouton de l'étape actuelle
|
||||
if (!isCompleted)
|
||||
ElevatedButton.icon(
|
||||
onPressed: () async {
|
||||
final result = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => EventPreparationPage(
|
||||
initialEvent: event,
|
||||
Center(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 400),
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () async {
|
||||
final result = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => EventPreparationPage(
|
||||
initialEvent: event,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Si la validation a réussi, le StreamBuilder se rechargera automatiquement
|
||||
if (result == true && context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Étape validée avec succès'),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
icon: Icon(buttonIcon),
|
||||
label: Text(
|
||||
buttonText,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Si la validation a réussi, le StreamBuilder se rechargera automatiquement
|
||||
if (result == true && context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Étape validée avec succès'),
|
||||
backgroundColor: Colors.green,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.bleuFonce,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 24),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
icon: Icon(buttonIcon),
|
||||
label: Text(
|
||||
buttonText,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.bleuFonce,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -6,9 +6,8 @@ import 'package:em2rp/utils/colors.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
enum EventFilter {
|
||||
current, // Événements en cours (préparés mais pas encore retournés)
|
||||
upcoming, // Événements à venir
|
||||
past, // Événements passés
|
||||
past, // Événements passés
|
||||
}
|
||||
|
||||
/// Widget pour afficher les événements associés à un équipement
|
||||
@@ -27,7 +26,7 @@ class EquipmentAssociatedEventsSection extends StatefulWidget {
|
||||
|
||||
class _EquipmentAssociatedEventsSectionState
|
||||
extends State<EquipmentAssociatedEventsSection> {
|
||||
EventFilter _selectedFilter = EventFilter.current;
|
||||
EventFilter _selectedFilter = EventFilter.upcoming;
|
||||
List<EventModel> _events = [];
|
||||
bool _isLoading = true;
|
||||
|
||||
@@ -41,38 +40,80 @@ class _EquipmentAssociatedEventsSectionState
|
||||
setState(() => _isLoading = true);
|
||||
|
||||
try {
|
||||
// Récupérer TOUS les événements car on ne peut pas faire arrayContains sur un objet
|
||||
final eventsSnapshot = await FirebaseFirestore.instance
|
||||
.collection('events')
|
||||
.where('assignedEquipment',
|
||||
arrayContains: {'equipmentId': widget.equipment.id})
|
||||
.get();
|
||||
|
||||
final events = eventsSnapshot.docs
|
||||
.map((doc) => EventModel.fromMap(doc.data(), doc.id))
|
||||
.toList();
|
||||
final events = <EventModel>[];
|
||||
|
||||
// Récupérer toutes les boîtes pour vérifier leur contenu
|
||||
final containersSnapshot = await FirebaseFirestore.instance
|
||||
.collection('containers')
|
||||
.get();
|
||||
|
||||
final containersWithEquipment = <String>[];
|
||||
for (var containerDoc in containersSnapshot.docs) {
|
||||
try {
|
||||
final data = containerDoc.data();
|
||||
final equipmentIds = List<String>.from(data['equipmentIds'] ?? []);
|
||||
|
||||
if (equipmentIds.contains(widget.equipment.id)) {
|
||||
containersWithEquipment.add(containerDoc.id);
|
||||
}
|
||||
} catch (e) {
|
||||
print('[EquipmentAssociatedEventsSection] Error parsing container ${containerDoc.id}: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Filtrer manuellement les événements qui contiennent cet équipement
|
||||
for (var doc in eventsSnapshot.docs) {
|
||||
try {
|
||||
final event = EventModel.fromMap(doc.data(), doc.id);
|
||||
|
||||
// Vérifier si l'équipement est directement assigné
|
||||
final hasEquipmentDirectly = event.assignedEquipment.any(
|
||||
(eq) => eq.equipmentId == widget.equipment.id,
|
||||
);
|
||||
|
||||
// Vérifier si l'équipement est dans une boîte assignée à l'événement
|
||||
final hasEquipmentInAssignedContainer = event.assignedContainers.any(
|
||||
(containerId) => containersWithEquipment.contains(containerId),
|
||||
);
|
||||
|
||||
if (hasEquipmentDirectly || hasEquipmentInAssignedContainer) {
|
||||
events.add(event);
|
||||
}
|
||||
} catch (e) {
|
||||
print('[EquipmentAssociatedEventsSection] Error parsing event ${doc.id}: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Filtrer selon le statut
|
||||
final now = DateTime.now();
|
||||
final filteredEvents = events.where((event) {
|
||||
switch (_selectedFilter) {
|
||||
case EventFilter.current:
|
||||
// Événement en cours = préparation complétée ET retour pas encore complété
|
||||
return (event.preparationStatus == PreparationStatus.completed ||
|
||||
event.preparationStatus ==
|
||||
PreparationStatus.completedWithMissing) &&
|
||||
(event.returnStatus == null ||
|
||||
event.returnStatus == ReturnStatus.notStarted ||
|
||||
event.returnStatus == ReturnStatus.inProgress);
|
||||
// Un événement est EN COURS dès que la préparation est complétée
|
||||
// et jusqu'à ce que le retour soit complété
|
||||
final isPrepared = event.preparationStatus == PreparationStatus.completed ||
|
||||
event.preparationStatus == PreparationStatus.completedWithMissing;
|
||||
|
||||
final isReturned = event.returnStatus == ReturnStatus.completed ||
|
||||
event.returnStatus == ReturnStatus.completedWithMissing;
|
||||
|
||||
final isInProgress = isPrepared && !isReturned;
|
||||
|
||||
if (isInProgress) {
|
||||
return false; // Les événements en cours sont affichés dans une autre section
|
||||
}
|
||||
|
||||
switch (_selectedFilter) {
|
||||
case EventFilter.upcoming:
|
||||
// Événements à venir = date de début dans le futur OU préparation pas encore faite
|
||||
return event.startDateTime.isAfter(now) ||
|
||||
event.preparationStatus == PreparationStatus.notStarted;
|
||||
// Événements à venir = date de début dans le futur
|
||||
return event.startDateTime.isAfter(now);
|
||||
|
||||
case EventFilter.past:
|
||||
// Événements passés = retour complété
|
||||
return event.returnStatus == ReturnStatus.completed ||
|
||||
event.returnStatus == ReturnStatus.completedWithMissing;
|
||||
// Événements passés = date de fin passée
|
||||
return event.endDateTime.isBefore(now);
|
||||
}
|
||||
}).toList();
|
||||
|
||||
@@ -98,8 +139,6 @@ class _EquipmentAssociatedEventsSectionState
|
||||
|
||||
String _getFilterLabel(EventFilter filter) {
|
||||
switch (filter) {
|
||||
case EventFilter.current:
|
||||
return 'En cours';
|
||||
case EventFilter.upcoming:
|
||||
return 'À venir';
|
||||
case EventFilter.past:
|
||||
@@ -109,8 +148,6 @@ class _EquipmentAssociatedEventsSectionState
|
||||
|
||||
IconData _getFilterIcon(EventFilter filter) {
|
||||
switch (filter) {
|
||||
case EventFilter.current:
|
||||
return Icons.play_circle;
|
||||
case EventFilter.upcoming:
|
||||
return Icons.upcoming;
|
||||
case EventFilter.past:
|
||||
@@ -134,7 +171,7 @@ class _EquipmentAssociatedEventsSectionState
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Événements associés',
|
||||
'Événements passés / à venir',
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
@@ -225,11 +262,28 @@ class _EquipmentAssociatedEventsSectionState
|
||||
|
||||
Widget _buildEventCard(EventModel event) {
|
||||
final dateFormat = DateFormat('dd/MM/yyyy HH:mm');
|
||||
final isInProgress = (event.preparationStatus == PreparationStatus.completed ||
|
||||
event.preparationStatus == PreparationStatus.completedWithMissing) &&
|
||||
(event.returnStatus == null ||
|
||||
event.returnStatus == ReturnStatus.notStarted ||
|
||||
event.returnStatus == ReturnStatus.inProgress);
|
||||
|
||||
// Un événement est en cours dès que la préparation est complétée
|
||||
// et jusqu'à ce que le retour soit complété
|
||||
final isPrepared = event.preparationStatus == PreparationStatus.completed ||
|
||||
event.preparationStatus == PreparationStatus.completedWithMissing;
|
||||
|
||||
final isReturned = event.returnStatus == ReturnStatus.completed ||
|
||||
event.returnStatus == ReturnStatus.completedWithMissing;
|
||||
|
||||
final isInProgress = isPrepared && !isReturned;
|
||||
|
||||
// Trouver la quantité utilisée pour les consommables/câbles
|
||||
int? usedQuantity;
|
||||
if (widget.equipment.hasQuantity) {
|
||||
final assignedEquipment = event.assignedEquipment.firstWhere(
|
||||
(eq) => eq.equipmentId == widget.equipment.id,
|
||||
orElse: () => EventEquipment(equipmentId: ''),
|
||||
);
|
||||
if (assignedEquipment.equipmentId.isNotEmpty) {
|
||||
usedQuantity = assignedEquipment.quantity;
|
||||
}
|
||||
}
|
||||
|
||||
return Card(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
@@ -290,16 +344,46 @@ class _EquipmentAssociatedEventsSectionState
|
||||
children: [
|
||||
const Icon(Icons.calendar_today, size: 14, color: Colors.grey),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'${dateFormat.format(event.startDateTime)} → ${dateFormat.format(event.endDateTime)}',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Colors.grey.shade700,
|
||||
Expanded(
|
||||
child: Text(
|
||||
'${dateFormat.format(event.startDateTime)} → ${dateFormat.format(event.endDateTime)}',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Colors.grey.shade700,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// Quantité utilisée pour consommables/câbles
|
||||
if (usedQuantity != null) ...[
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: Colors.blue),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.inventory, size: 14, color: Colors.blue),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
'Quantité utilisée: $usedQuantity',
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.blue,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// Statuts de préparation et retour
|
||||
@@ -316,31 +400,6 @@ class _EquipmentAssociatedEventsSectionState
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// Boutons d'action
|
||||
if (isInProgress && _selectedFilter == EventFilter.current) ...[
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
'/event_preparation',
|
||||
arguments: event.id,
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.logout, size: 16),
|
||||
label: const Text('Check-out'),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: AppColors.rouge,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -0,0 +1,284 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:em2rp/models/event_model.dart';
|
||||
import 'package:em2rp/models/equipment_model.dart';
|
||||
import 'package:em2rp/utils/colors.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
/// Widget pour afficher les événements EN COURS utilisant cet équipement
|
||||
class EquipmentCurrentEventsSection extends StatefulWidget {
|
||||
final EquipmentModel equipment;
|
||||
|
||||
const EquipmentCurrentEventsSection({
|
||||
super.key,
|
||||
required this.equipment,
|
||||
});
|
||||
|
||||
@override
|
||||
State<EquipmentCurrentEventsSection> createState() =>
|
||||
_EquipmentCurrentEventsSectionState();
|
||||
}
|
||||
|
||||
class _EquipmentCurrentEventsSectionState
|
||||
extends State<EquipmentCurrentEventsSection> {
|
||||
List<EventModel> _events = [];
|
||||
bool _isLoading = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadCurrentEvents();
|
||||
}
|
||||
|
||||
Future<void> _loadCurrentEvents() async {
|
||||
setState(() => _isLoading = true);
|
||||
|
||||
try {
|
||||
// Récupérer TOUS les événements
|
||||
final eventsSnapshot = await FirebaseFirestore.instance
|
||||
.collection('events')
|
||||
.get();
|
||||
|
||||
final events = <EventModel>[];
|
||||
|
||||
// Récupérer toutes les boîtes pour vérifier leur contenu
|
||||
final containersSnapshot = await FirebaseFirestore.instance
|
||||
.collection('containers')
|
||||
.get();
|
||||
|
||||
final containersWithEquipment = <String>[];
|
||||
for (var containerDoc in containersSnapshot.docs) {
|
||||
try {
|
||||
final data = containerDoc.data();
|
||||
final equipmentIds = List<String>.from(data['equipmentIds'] ?? []);
|
||||
|
||||
if (equipmentIds.contains(widget.equipment.id)) {
|
||||
containersWithEquipment.add(containerDoc.id);
|
||||
}
|
||||
} catch (e) {
|
||||
print('[EquipmentCurrentEventsSection] Error parsing container ${containerDoc.id}: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Filtrer les événements en cours
|
||||
for (var doc in eventsSnapshot.docs) {
|
||||
try {
|
||||
final event = EventModel.fromMap(doc.data(), doc.id);
|
||||
|
||||
// Vérifier si l'équipement est directement assigné
|
||||
final hasEquipmentDirectly = event.assignedEquipment.any(
|
||||
(eq) => eq.equipmentId == widget.equipment.id,
|
||||
);
|
||||
|
||||
// Vérifier si l'équipement est dans une boîte assignée à l'événement
|
||||
final hasEquipmentInAssignedContainer = event.assignedContainers.any(
|
||||
(containerId) => containersWithEquipment.contains(containerId),
|
||||
);
|
||||
|
||||
if (hasEquipmentDirectly || hasEquipmentInAssignedContainer) {
|
||||
// Un événement est EN COURS dès que la préparation est complétée
|
||||
// et jusqu'à ce que le retour soit complété
|
||||
final isPrepared = event.preparationStatus == PreparationStatus.completed ||
|
||||
event.preparationStatus == PreparationStatus.completedWithMissing;
|
||||
|
||||
final isReturned = event.returnStatus == ReturnStatus.completed ||
|
||||
event.returnStatus == ReturnStatus.completedWithMissing;
|
||||
|
||||
final isInProgress = isPrepared && !isReturned;
|
||||
|
||||
if (isInProgress) {
|
||||
events.add(event);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print('[EquipmentCurrentEventsSection] Error parsing event ${doc.id}: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Trier par date
|
||||
events.sort((a, b) => a.startDateTime.compareTo(b.startDateTime));
|
||||
|
||||
setState(() {
|
||||
_events = events;
|
||||
_isLoading = false;
|
||||
});
|
||||
} catch (e) {
|
||||
setState(() => _isLoading = false);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Erreur lors du chargement: $e'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Ne rien afficher si pas d'événements en cours
|
||||
if (!_isLoading && _events.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return Card(
|
||||
elevation: 2,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// En-tête
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.play_circle, color: AppColors.rouge),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Événements en cours utilisant ce matériel',
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Divider(height: 24),
|
||||
|
||||
// Liste des événements
|
||||
if (_isLoading)
|
||||
const Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(32.0),
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
)
|
||||
else
|
||||
Column(
|
||||
children: _events.map((event) => _buildEventCard(event)).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEventCard(EventModel event) {
|
||||
final dateFormat = DateFormat('dd/MM/yyyy HH:mm');
|
||||
|
||||
// Trouver la quantité utilisée pour les consommables/câbles
|
||||
int? usedQuantity;
|
||||
if (widget.equipment.hasQuantity) {
|
||||
final assignedEquipment = event.assignedEquipment.firstWhere(
|
||||
(eq) => eq.equipmentId == widget.equipment.id,
|
||||
orElse: () => EventEquipment(equipmentId: ''),
|
||||
);
|
||||
if (assignedEquipment.equipmentId.isNotEmpty) {
|
||||
usedQuantity = assignedEquipment.quantity;
|
||||
}
|
||||
}
|
||||
|
||||
return Card(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
elevation: 1,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: const BorderSide(color: AppColors.rouge, width: 2),
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
// Navigation vers les détails de l'événement si nécessaire
|
||||
},
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Titre de l'événement
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
event.name,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 4,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.rouge,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: const Text(
|
||||
'EN COURS',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// Dates
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.calendar_today, size: 14, color: Colors.grey),
|
||||
const SizedBox(width: 4),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'${dateFormat.format(event.startDateTime)} → ${dateFormat.format(event.endDateTime)}',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Colors.grey.shade700,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// Quantité utilisée pour consommables/câbles
|
||||
if (usedQuantity != null) ...[
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: Colors.blue),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.inventory, size: 14, color: Colors.blue),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
'Quantité utilisée: $usedQuantity',
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.blue,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class EquipmentHeaderSection extends StatelessWidget {
|
||||
radius: 30,
|
||||
child: equipment.category.getIcon(
|
||||
size: 32,
|
||||
color: AppColors.rouge,
|
||||
color: equipment.category.color,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:em2rp/models/equipment_model.dart';
|
||||
import 'package:em2rp/providers/equipment_provider.dart';
|
||||
|
||||
/// Widget qui affiche le badge de statut d'un équipement
|
||||
/// Le statut est calculé de manière asynchrone basé sur les événements en cours
|
||||
class EquipmentStatusBadge extends StatelessWidget {
|
||||
final EquipmentModel equipment;
|
||||
|
||||
const EquipmentStatusBadge({
|
||||
super.key,
|
||||
required this.equipment,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final provider = Provider.of<EquipmentProvider>(context, listen: false);
|
||||
print('[EquipmentStatusBadge] Building badge for ${equipment.id}');
|
||||
|
||||
return FutureBuilder<EquipmentStatus>(
|
||||
// On calcule le statut réel de manière asynchrone
|
||||
future: provider.calculateRealStatus(equipment),
|
||||
// En attendant, on affiche le statut stocké
|
||||
initialData: equipment.status,
|
||||
builder: (context, snapshot) {
|
||||
// Utiliser le statut calculé s'il est disponible, sinon le statut stocké
|
||||
final status = snapshot.data ?? equipment.status;
|
||||
print('[EquipmentStatusBadge] ${equipment.id} - Status: ${status.label} (hasData: ${snapshot.hasData}, connectionState: ${snapshot.connectionState})');
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: status.color.withValues(alpha: 0.2),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: status.color),
|
||||
),
|
||||
child: Text(
|
||||
status.label,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: status.color,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user