feat: Ajout du scan de QR Code pour retrouver équipements et conteneurs

Cette mise à jour introduit une fonctionnalité de scan de QR codes directement depuis l'application, permettant aux utilisateurs de retrouver et d'accéder rapidement à la page de détail d'un équipement ou d'un conteneur.

**Features :**
- **Scan de QR Code :**
    - Un nouveau bouton "Scanner QR Code" est ajouté sur les pages de gestion des équipements et des conteneurs.
    - L'appui sur ce bouton ouvre une nouvelle boîte de dialogue (`QRCodeScannerDialog`) utilisant la caméra de l'appareil pour scanner un QR code.
    - Le scanner affiche un overlay visuel clair avec un cadre de détection et fournit un retour visuel (icône de validation) lorsqu'un code est détecté avec succès.
- **Recherche et Redirection Intelligente :**
    - Une fois un QR code scanné, l'application recherche l'ID correspondant d'abord dans les équipements, puis dans les conteneurs.
    - Si une correspondance est trouvée, l'utilisateur est automatiquement redirigé vers la page de détail de l'élément correspondant (`EquipmentDetailPage` ou `ContainerDetailPage`).
    - Un message informe l'utilisateur si aucun élément ne correspond à l'ID scanné.

**Changements Techniques :**
- **Dépendance :** Ajout de la bibliothèque `mobile_scanner` pour gérer la fonctionnalité de scan.
- **Nouveau Widget :** Création du widget `QRCodeScannerDialog`, un dialogue réutilisable et stylisé pour le scan, incluant un overlay personnalisé (`_ScannerOverlayPainter`).
- **Intégration UI :**
    - Le `ManagementSearchBar` accepte désormais une liste de `additionalActions` pour permettre l'ajout de boutons personnalisés comme celui du scanner.
    - Ajout du bouton de scan sur les écrans `EquipmentManagementPage` et `ContainerManagementPage`, à la fois en version bureau (icône) et mobile (bouton plein).
- **Logique de Recherche :** Implémentation de la fonction `_scanQRCode` dans les deux pages de gestion pour orchestrer l'ouverture du scanner, la recherche dans les `EquipmentProvider` et `ContainerProvider`, et la navigation.
This commit is contained in:
ElPoyo
2026-01-16 00:35:10 +01:00
parent 67b85d323c
commit 06f394b728
5 changed files with 581 additions and 1 deletions

View File

@@ -5,14 +5,19 @@ import 'package:em2rp/utils/permission_gate.dart';
import 'package:em2rp/views/widgets/nav/main_drawer.dart';
import 'package:em2rp/views/widgets/nav/custom_app_bar.dart';
import 'package:em2rp/providers/container_provider.dart';
import 'package:em2rp/providers/equipment_provider.dart';
import 'package:em2rp/providers/local_user_provider.dart';
import 'package:em2rp/models/container_model.dart';
import 'package:em2rp/models/equipment_model.dart';
import 'package:em2rp/views/equipment_detail_page.dart';
import 'package:em2rp/views/widgets/common/qr_code_dialog.dart';
import 'package:em2rp/views/widgets/common/qr_code_scanner_dialog.dart';
import 'package:em2rp/views/widgets/common/qr_code_format_selector_dialog.dart';
import 'package:em2rp/mixins/selection_mode_mixin.dart';
import 'package:em2rp/views/widgets/management/management_search_bar.dart';
import 'package:em2rp/views/widgets/management/management_card.dart';
import 'package:em2rp/views/widgets/management/management_list.dart';
import 'package:em2rp/utils/debug_log.dart';
class ContainerManagementPage extends StatefulWidget {
const ContainerManagementPage({super.key});
@@ -177,6 +182,14 @@ class _ContainerManagementPageState extends State<ContainerManagementPage>
},
onSelectionModeToggle: isSelectionMode ? null : toggleSelectionMode,
showSelectionModeButton: !isSelectionMode,
additionalActions: [
const SizedBox(width: 12),
IconButton(
icon: const Icon(Icons.qr_code_scanner, color: AppColors.rouge),
tooltip: 'Scanner un QR Code',
onPressed: _scanQRCode,
),
],
);
}
@@ -417,7 +430,7 @@ class _ContainerManagementPageState extends State<ContainerManagementPage>
_editContainer(container);
break;
case 'qr':
// Non utilisé - les QR codes multiples sont générés via _generateQRCodesForSelected
_showQRCode(container);
break;
case 'delete':
_deleteContainer(container);
@@ -425,6 +438,14 @@ class _ContainerManagementPageState extends State<ContainerManagementPage>
}
}
/// Afficher le QR code d'un conteneur
void _showQRCode(ContainerModel container) {
showDialog(
context: context,
builder: (context) => QRCodeDialog.forContainer(container),
);
}
void _navigateToForm(BuildContext context) async {
final result = await Navigator.pushNamed(context, '/container_form');
if (result == true) {
@@ -583,5 +604,120 @@ class _ContainerManagementPageState extends State<ContainerManagementPage>
}
}
}
/// Scanner un QR Code et ouvrir la vue de détail correspondante
Future<void> _scanQRCode() async {
try {
// Ouvrir le scanner
final scannedCode = await showDialog<String>(
context: context,
builder: (context) => const QRCodeScannerDialog(),
);
if (scannedCode == null || scannedCode.isEmpty) {
return; // L'utilisateur a annulé
}
if (!mounted) return;
// Afficher un indicateur de chargement
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => const Center(
child: CircularProgressIndicator(color: AppColors.rouge),
),
);
// Rechercher d'abord dans les conteneurs
final containerProvider = context.read<ContainerProvider>();
if (containerProvider.containers.isEmpty) {
await containerProvider.loadContainers();
}
final container = containerProvider.containers.firstWhere(
(c) => c.id == scannedCode,
orElse: () => ContainerModel(
id: '',
name: '',
type: ContainerType.flightCase,
status: EquipmentStatus.available,
equipmentIds: [],
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
),
);
if (mounted) {
Navigator.of(context).pop(); // Fermer l'indicateur
}
if (container.id.isNotEmpty) {
// Conteneur trouvé
if (mounted) {
Navigator.pushNamed(
context,
'/container_detail',
arguments: container,
);
}
return;
}
// Si pas trouvé dans les conteneurs, chercher dans les équipements
final equipmentProvider = context.read<EquipmentProvider>();
await equipmentProvider.ensureLoaded();
final equipment = equipmentProvider.allEquipment.firstWhere(
(eq) => eq.id == scannedCode,
orElse: () => EquipmentModel(
id: '',
name: '',
category: EquipmentCategory.other,
status: EquipmentStatus.available,
parentBoxIds: [],
maintenanceIds: [],
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
),
);
if (equipment.id.isNotEmpty) {
// Équipement trouvé
if (mounted) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EquipmentDetailPage(equipment: equipment),
),
);
}
return;
}
// Rien trouvé
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Aucun conteneur ou équipement trouvé avec l\'ID : $scannedCode'),
backgroundColor: Colors.orange,
),
);
}
} catch (e) {
DebugLog.error('[ContainerManagementPage] Error scanning QR code', e);
if (mounted) {
// Fermer l'indicateur si ouvert
Navigator.of(context).popUntil((route) => route.isFirst);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Erreur lors du scan : ${e.toString()}'),
backgroundColor: Colors.red,
),
);
}
}
}
}