Files
EM2_ERP/em2rp/lib/views/widgets/event/missing_equipment_dialog.dart
ElPoyo 08f046c89c feat: Refactor event equipment management with advanced selection and conflict detection
This commit introduces a complete overhaul of how equipment is assigned to events, focusing on an enhanced user experience, advanced selection capabilities, and robust conflict detection.

**Key Features & Enhancements:**

-   **Advanced Equipment Selection UI (`EquipmentSelectionDialog`):**
    -   New full-screen dialog to select equipment and containers ("boîtes") for an event.
    -   Hierarchical view showing containers and a flat list of all individual equipment.
    -   Real-time search and filtering by equipment category.
    -   Side panel summarizing the current selection and providing recommendations for containers based on selected equipment.
    -   Supports quantity selection for consumables and cables.

-   **Conflict Detection & Management (`EventAvailabilityService`):**
    -   A new service (`EventAvailabilityService`) checks for equipment availability against other events based on the selected date range.
    -   The selection dialog visually highlights equipment and containers with scheduling conflicts (e.g., already used, partially unavailable).
    -   A dedicated conflict resolution dialog (`EquipmentConflictDialog`) appears if conflicting items are selected, allowing the user to either remove them or force the assignment.

-   **Integrated Event Form (`EventAssignedEquipmentSection`):**
    -   The event creation/editing form now includes a new section for managing assigned equipment.
    -   It clearly displays assigned containers and standalone equipment, showing the composition of each container.
    -   Integrates the new selection dialog, ensuring all assignments are checked for conflicts before being saved.

-   **Event Preparation & Return Workflow (`EventPreparationPage`):**
    -   New page (`EventPreparationPage`) for managing the check-out (preparation) and check-in (return) of equipment for an event.
    -   Provides a checklist of all assigned equipment.
    -   Users can validate each item, with options to "validate all" or finalize with missing items.
    -   Includes a dialog (`MissingEquipmentDialog`) to handle discrepancies.
    -   Supports tracking returned quantities for consumables.

**Data Model and Other Changes:**

-   The `EventModel` now includes `assignedContainers` to explicitly link containers to an event.
-   `EquipmentAssociatedEventsSection` on the equipment detail page is now functional, displaying current, upcoming, and past events for that item.
-   Added deployment and versioning scripts (`scripts/deploy.js`, `scripts/increment_version.js`, `scripts/toggle_env.js`) to automate the release process.
-   Introduced an application version display in the main drawer (`AppVersion`).
2025-11-30 20:33:03 +01:00

186 lines
6.5 KiB
Dart

import 'package:flutter/material.dart';
import 'package:em2rp/models/equipment_model.dart';
/// Dialog affichant la liste des équipements non validés
class MissingEquipmentDialog extends StatelessWidget {
final List<EquipmentModel> missingEquipments;
final String eventId;
final bool isReturnMode;
const MissingEquipmentDialog({
super.key,
required this.missingEquipments,
required this.eventId,
this.isReturnMode = false,
});
@override
Widget build(BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Container(
constraints: const BoxConstraints(maxWidth: 500, maxHeight: 600),
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Icône d'avertissement
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.orange.shade100,
shape: BoxShape.circle,
),
child: Icon(
Icons.warning_amber_rounded,
size: 48,
color: Colors.orange.shade700,
),
),
const SizedBox(height: 16),
// Titre
Text(
isReturnMode
? 'Équipements non retournés'
: 'Équipements non préparés',
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
// Description
Text(
isReturnMode
? 'Les équipements suivants n\'ont pas été marqués comme retournés :'
: 'Les équipements suivants n\'ont pas été marqués comme préparés :',
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade700,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
// Liste des équipements manquants
Flexible(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.shade300),
borderRadius: BorderRadius.circular(8),
),
child: ListView.separated(
shrinkWrap: true,
itemCount: missingEquipments.length,
separatorBuilder: (context, index) => const Divider(height: 1),
itemBuilder: (context, index) {
final equipment = missingEquipments[index];
return ListTile(
dense: true,
leading: equipment.category.getIcon(
size: 20,
color: equipment.category.color,
),
title: Text(
equipment.id,
style: const TextStyle(fontWeight: FontWeight.bold),
),
subtitle: equipment.brand != null || equipment.model != null
? Text('${equipment.brand ?? ''} ${equipment.model ?? ''}'.trim())
: null,
);
},
),
),
),
const SizedBox(height: 24),
// Boutons d'action
Column(
children: [
// Bouton 1 : Confirmer malgré les manquants (primaire - rouge)
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: () => Navigator.of(context).pop('confirm_with_missing'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red.shade600,
padding: const EdgeInsets.symmetric(vertical: 14),
),
icon: const Icon(Icons.check_circle_outline, color: Colors.white),
label: Text(
isReturnMode
? 'Confirmer malgré les manquants'
: 'Valider malgré les manquants',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
const SizedBox(height: 12),
// Bouton 2 : Marquer comme validés (secondaire - vert)
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: () => Navigator.of(context).pop('validate_missing'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
padding: const EdgeInsets.symmetric(vertical: 14),
),
icon: const Icon(Icons.done_all, color: Colors.white),
label: Text(
isReturnMode
? 'Marquer tout comme retourné'
: 'Marquer tout comme préparé',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
const SizedBox(height: 12),
// Bouton 3 : Retourner à la liste (tertiaire - outline)
SizedBox(
width: double.infinity,
child: OutlinedButton.icon(
onPressed: () => Navigator.of(context).pop(),
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 14),
side: BorderSide(color: Colors.grey.shade400),
),
icon: Icon(Icons.arrow_back, color: Colors.grey.shade700),
label: Text(
'Retourner à la liste',
style: TextStyle(
color: Colors.grey.shade700,
fontWeight: FontWeight.w500,
),
),
),
),
],
),
],
),
),
);
}
}