 3fab69cb00
			
		
	
	3fab69cb00
	
	
	
		
			
			Ajout de la gestion des containers (création, édition, suppression, affichage des détails).
Introduction d'un système de génération de QR codes unifié et d'un mode de sélection multiple.
**Features:**
- **Gestion des Containers :**
    - Nouvelle page de gestion des containers (`container_management_page.dart`) avec recherche et filtres.
    - Formulaire de création/édition de containers (`container_form_page.dart`) avec génération d'ID automatique.
    - Page de détails d'un container (`container_detail_page.dart`) affichant son contenu et ses caractéristiques.
    - Ajout des routes et du provider (`ContainerProvider`) nécessaires.
- **Modèle de Données :**
    - Ajout du `ContainerModel` pour représenter les boîtes, flight cases, etc.
    - Le modèle `EquipmentModel` a été enrichi avec des caractéristiques physiques (poids, dimensions).
- **QR Codes :**
    - Nouveau service unifié (`UnifiedPDFGeneratorService`) pour générer des PDFs de QR codes pour n'importe quelle entité.
    - Services `PDFGeneratorService` et `ContainerPDFGeneratorService` transformés en wrappers pour maintenir la compatibilité.
    - Amélioration de la performance de la génération de QR codes en masse.
- **Interface Utilisateur (UI/UX) :**
    - Nouvelle page de détails pour le matériel (`equipment_detail_page.dart`).
    - Ajout d'un `SelectionModeMixin` pour gérer la sélection multiple dans les pages de gestion.
    - Dialogues réutilisables pour l'affichage de QR codes (`QRCodeDialog`) et la sélection de format d'impression (`QRCodeFormatSelectorDialog`).
    - Ajout d'un bouton "Gérer les boîtes" sur la page de gestion du matériel.
**Refactorisation :**
- L' `IdGenerator` a été déplacé dans le répertoire `utils` et étendu pour gérer les containers.
- Mise à jour de nombreuses dépendances `pubspec.yaml` vers des versions plus récentes.
- Séparation de la logique d'affichage des containers et du matériel dans des widgets dédiés (`ContainerHeaderCard`, `EquipmentParentContainers`, etc.).
		
	
		
			
				
	
	
		
			194 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			194 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| import 'package:flutter/material.dart';
 | |
| import 'package:qr_flutter/qr_flutter.dart';
 | |
| import 'package:em2rp/utils/colors.dart';
 | |
| import 'package:em2rp/services/qr_code_service.dart';
 | |
| import 'package:printing/printing.dart';
 | |
| 
 | |
| /// Widget réutilisable pour afficher un QR code avec option de téléchargement
 | |
| /// Utilisable pour équipements, containers, et autres entités
 | |
| class QRCodeDialog<T> extends StatelessWidget {
 | |
|   final T item;
 | |
|   final String Function(T) getId;
 | |
|   final String Function(T) getTitle;
 | |
|   final List<Widget> Function(T)? buildSubtitle;
 | |
| 
 | |
|   const QRCodeDialog({
 | |
|     super.key,
 | |
|     required this.item,
 | |
|     required this.getId,
 | |
|     required this.getTitle,
 | |
|     this.buildSubtitle,
 | |
|   });
 | |
| 
 | |
|   @override
 | |
|   Widget build(BuildContext context) {
 | |
|     final id = getId(item);
 | |
|     final title = getTitle(item);
 | |
| 
 | |
|     return Dialog(
 | |
|       child: Container(
 | |
|         padding: const EdgeInsets.all(24),
 | |
|         constraints: const BoxConstraints(maxWidth: 400),
 | |
|         child: Column(
 | |
|           mainAxisSize: MainAxisSize.min,
 | |
|           children: [
 | |
|             // En-tête
 | |
|             Row(
 | |
|               children: [
 | |
|                 const Icon(Icons.qr_code, color: AppColors.rouge, size: 32),
 | |
|                 const SizedBox(width: 12),
 | |
|                 Expanded(
 | |
|                   child: Text(
 | |
|                     'QR Code - $id',
 | |
|                     style: const TextStyle(
 | |
|                       fontSize: 20,
 | |
|                       fontWeight: FontWeight.bold,
 | |
|                     ),
 | |
|                   ),
 | |
|                 ),
 | |
|                 IconButton(
 | |
|                   icon: const Icon(Icons.close),
 | |
|                   onPressed: () => Navigator.pop(context),
 | |
|                 ),
 | |
|               ],
 | |
|             ),
 | |
|             const SizedBox(height: 24),
 | |
| 
 | |
|             // QR Code
 | |
|             Container(
 | |
|               padding: const EdgeInsets.all(16),
 | |
|               decoration: BoxDecoration(
 | |
|                 color: Colors.white,
 | |
|                 borderRadius: BorderRadius.circular(8),
 | |
|                 border: Border.all(color: Colors.grey[300]!),
 | |
|               ),
 | |
|               child: QrImageView(
 | |
|                 data: id,
 | |
|                 version: QrVersions.auto,
 | |
|                 size: 250,
 | |
|                 backgroundColor: Colors.white,
 | |
|               ),
 | |
|             ),
 | |
|             const SizedBox(height: 16),
 | |
| 
 | |
|             // Informations
 | |
|             Container(
 | |
|               padding: const EdgeInsets.all(12),
 | |
|               decoration: BoxDecoration(
 | |
|                 color: Colors.grey[100],
 | |
|                 borderRadius: BorderRadius.circular(8),
 | |
|               ),
 | |
|               child: Column(
 | |
|                 crossAxisAlignment: CrossAxisAlignment.start,
 | |
|                 children: [
 | |
|                   Text(
 | |
|                     id,
 | |
|                     style: const TextStyle(
 | |
|                       fontWeight: FontWeight.bold,
 | |
|                       fontSize: 16,
 | |
|                     ),
 | |
|                   ),
 | |
|                   const SizedBox(height: 4),
 | |
|                   Text(
 | |
|                     title,
 | |
|                     style: TextStyle(color: Colors.grey[700]),
 | |
|                   ),
 | |
|                   if (buildSubtitle != null) ...[
 | |
|                     const SizedBox(height: 4),
 | |
|                     ...buildSubtitle!(item),
 | |
|                   ],
 | |
|                 ],
 | |
|               ),
 | |
|             ),
 | |
|             const SizedBox(height: 24),
 | |
| 
 | |
|             // Bouton télécharger
 | |
|             ElevatedButton.icon(
 | |
|               onPressed: () => _downloadQRCode(context, id),
 | |
|               style: ElevatedButton.styleFrom(
 | |
|                 backgroundColor: AppColors.rouge,
 | |
|                 minimumSize: const Size(double.infinity, 48),
 | |
|               ),
 | |
|               icon: const Icon(Icons.download, color: Colors.white),
 | |
|               label: const Text(
 | |
|                 'Télécharger l\'image',
 | |
|                 style: TextStyle(color: Colors.white),
 | |
|               ),
 | |
|             ),
 | |
|           ],
 | |
|         ),
 | |
|       ),
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   Future<void> _downloadQRCode(BuildContext context, String id) async {
 | |
|     try {
 | |
|       // Générer l'image QR code en haute résolution
 | |
|       final qrImage = await QRCodeService.generateQRCode(
 | |
|         id,
 | |
|         size: 1024,
 | |
|         useCache: false,
 | |
|       );
 | |
| 
 | |
|       // Utiliser la bibliothèque printing pour sauvegarder l'image
 | |
|       await Printing.sharePdf(
 | |
|         bytes: qrImage,
 | |
|         filename: 'QRCode_$id.png',
 | |
|       );
 | |
| 
 | |
|       if (context.mounted) {
 | |
|         ScaffoldMessenger.of(context).showSnackBar(
 | |
|           const SnackBar(
 | |
|             content: Text('Image QR Code téléchargée avec succès'),
 | |
|             backgroundColor: Colors.green,
 | |
|           ),
 | |
|         );
 | |
|       }
 | |
|     } catch (e) {
 | |
|       if (context.mounted) {
 | |
|         ScaffoldMessenger.of(context).showSnackBar(
 | |
|           SnackBar(
 | |
|             content: Text('Erreur lors du téléchargement: $e'),
 | |
|             backgroundColor: Colors.red,
 | |
|           ),
 | |
|         );
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /// Factory pour équipement
 | |
|   static QRCodeDialog forEquipment(dynamic equipment) {
 | |
|     return QRCodeDialog(
 | |
|       item: equipment,
 | |
|       getId: (eq) => eq.id,
 | |
|       getTitle: (eq) => '${eq.brand ?? ''} ${eq.model ?? ''}'.trim(),
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   /// Factory pour container
 | |
|   static QRCodeDialog forContainer(dynamic container) {
 | |
|     return QRCodeDialog(
 | |
|       item: container,
 | |
|       getId: (c) => c.id,
 | |
|       getTitle: (c) => c.name,
 | |
|       buildSubtitle: (c) {
 | |
|         return [
 | |
|           Text(
 | |
|             _getContainerTypeLabel(c.type),
 | |
|             style: TextStyle(
 | |
|               color: Colors.grey[600],
 | |
|               fontSize: 12,
 | |
|             ),
 | |
|           ),
 | |
|         ];
 | |
|       },
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   static String _getContainerTypeLabel(dynamic type) {
 | |
|     // Simple fallback - à améliorer avec import du model
 | |
|     return type.toString().split('.').last;
 | |
|   }
 | |
| }
 | |
| 
 |