feat: Gestion complète des containers et refactorisation du matériel

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.).
This commit is contained in:
ElPoyo
2025-10-29 10:57:42 +01:00
parent ae3a1b7227
commit 3fab69cb00
31 changed files with 6540 additions and 656 deletions

View File

@@ -0,0 +1,193 @@
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;
}
}