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:
@@ -5,10 +5,14 @@ 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/equipment_provider.dart';
|
||||
import 'package:em2rp/providers/container_provider.dart';
|
||||
import 'package:em2rp/models/equipment_model.dart';
|
||||
import 'package:em2rp/models/container_model.dart';
|
||||
import 'package:em2rp/views/equipment_form_page.dart';
|
||||
import 'package:em2rp/views/equipment_detail_page.dart';
|
||||
import 'package:em2rp/views/container_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/views/widgets/equipment/equipment_status_badge.dart';
|
||||
import 'package:em2rp/utils/debug_log.dart';
|
||||
@@ -156,6 +160,17 @@ class _EquipmentManagementPageState extends State<EquipmentManagementPage>
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
// Bouton Scanner QR
|
||||
IconButton.filled(
|
||||
onPressed: _scanQRCode,
|
||||
icon: const Icon(Icons.qr_code_scanner),
|
||||
tooltip: 'Scanner un QR Code',
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: Colors.grey[700],
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
// Bouton Gérer les boîtes
|
||||
IconButton.filled(
|
||||
onPressed: () {
|
||||
@@ -256,6 +271,26 @@ class _EquipmentManagementPageState extends State<EquipmentManagementPage>
|
||||
),
|
||||
),
|
||||
),
|
||||
// Bouton Scanner QR
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16.0, 0, 16.0, 16.0),
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: _scanQRCode,
|
||||
icon: const Icon(Icons.qr_code_scanner, color: Colors.white),
|
||||
label: const Text(
|
||||
'Scanner QR Code',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.grey[700],
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 20,
|
||||
vertical: 16,
|
||||
),
|
||||
minimumSize: const Size(double.infinity, 50),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
// En-tête filtres
|
||||
Padding(
|
||||
@@ -992,4 +1027,120 @@ class _EquipmentManagementPageState extends State<EquipmentManagementPage>
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 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 é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 (mounted) {
|
||||
Navigator.of(context).pop(); // Fermer l'indicateur
|
||||
}
|
||||
|
||||
if (equipment.id.isNotEmpty) {
|
||||
// Équipement trouvé
|
||||
if (mounted) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => EquipmentDetailPage(equipment: equipment),
|
||||
),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Si pas trouvé dans les équipements, chercher 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 (container.id.isNotEmpty) {
|
||||
// Conteneur trouvé
|
||||
if (mounted) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ContainerDetailPage(container: container),
|
||||
),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Rien trouvé
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Aucun équipement ou conteneur trouvé avec l\'ID : $scannedCode'),
|
||||
backgroundColor: Colors.orange,
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
DebugLog.error('[EquipmentManagementPage] 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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user