Cette mise à jour structurelle améliore la classification des équipements en introduisant la notion de sous-catégories et supprime la gestion directe de l'appartenance d'un équipement à une boîte (`parentBoxIds`). L'appartenance est désormais uniquement définie côté conteneur. Une nouvelle catégorie "Régie / Backline" est également ajoutée.
**Changements majeurs :**
- **Suppression de `parentBoxIds` sur `EquipmentModel` :**
- Le champ `parentBoxIds` a été retiré du modèle de données `EquipmentModel` et de toutes les logiques associées (création, mise à jour, copie).
- La responsabilité de lier un équipement à un conteneur est désormais exclusivement gérée par le `ContainerModel` via sa liste `equipmentIds`.
- La logique de synchronisation complexe dans `EquipmentFormPage` qui mettait à jour les conteneurs lors de la modification d'un équipement a été entièrement supprimée, simplifiant considérablement le code.
- Le sélecteur de boîtes parentes (`ParentBoxesSelector`) a été retiré du formulaire d'équipement.
- **Ajout des sous-catégories :**
- Un champ optionnel `subCategory` (String) a été ajouté au `EquipmentModel`.
- Le formulaire de création/modification d'équipement inclut désormais un nouveau champ "Sous-catégorie" avec autocomplétion.
- Ce champ est contextuel : il propose des suggestions basées sur les sous-catégories existantes pour la catégorie principale sélectionnée (ex: "Console", "Micro" pour la catégorie "Son").
- La sous-catégorie est maintenant affichée sur les fiches de détail des équipements et dans les listes de la page de gestion, améliorant la visibilité du classement.
**Nouvelle catégorie d'équipement :**
- Une nouvelle catégorie `backline` ("Régie / Backline") a été ajoutée à `EquipmentCategory` avec une icône (`Icons.piano`) et une couleur associée.
**Refactorisation et nettoyage :**
- Le `EquipmentProvider` et `EquipmentService` ont été mis à jour pour charger et filtrer les sous-catégories.
- De nombreuses instanciations d'un `EquipmentModel` vide (`dummy`) à travers l'application ont été nettoyées pour retirer la référence à `parentBoxIds`.
- **Version de l'application :**
- La version a été incrémentée à `1.0.4`.
755 lines
23 KiB
Dart
755 lines
23 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:em2rp/utils/colors.dart';
|
|
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});
|
|
|
|
@override
|
|
State<ContainerManagementPage> createState() =>
|
|
_ContainerManagementPageState();
|
|
}
|
|
|
|
class _ContainerManagementPageState extends State<ContainerManagementPage>
|
|
with SelectionModeMixin<ContainerManagementPage> {
|
|
final TextEditingController _searchController = TextEditingController();
|
|
ContainerType? _selectedType;
|
|
EquipmentStatus? _selectedStatus;
|
|
List<ContainerModel>? _cachedContainers; // Cache pour éviter le rebuild
|
|
|
|
@override
|
|
void dispose() {
|
|
_searchController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final isMobile = MediaQuery.of(context).size.width < 800;
|
|
|
|
return PermissionGate(
|
|
requiredPermissions: const ['view_equipment'],
|
|
fallback: Scaffold(
|
|
appBar: const CustomAppBar(title: 'Accès refusé'),
|
|
drawer: const MainDrawer(currentPage: '/container_management'),
|
|
body: const Center(
|
|
child: Padding(
|
|
padding: EdgeInsets.all(24.0),
|
|
child: Text(
|
|
'Vous n\'avez pas les permissions nécessaires pour accéder à la gestion des containers.',
|
|
textAlign: TextAlign.center,
|
|
style: TextStyle(fontSize: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
child: Scaffold(
|
|
appBar: isSelectionMode
|
|
? AppBar(
|
|
backgroundColor: AppColors.rouge,
|
|
leading: IconButton(
|
|
icon: const Icon(Icons.close, color: Colors.white),
|
|
onPressed: toggleSelectionMode,
|
|
),
|
|
title: Text(
|
|
'$selectedCount sélectionné(s)',
|
|
style: const TextStyle(color: Colors.white),
|
|
),
|
|
actions: [
|
|
if (hasSelection) ...[
|
|
IconButton(
|
|
icon: const Icon(Icons.qr_code, color: Colors.white),
|
|
tooltip: 'Générer QR Codes',
|
|
onPressed: _generateQRCodesForSelected,
|
|
),
|
|
IconButton(
|
|
icon: const Icon(Icons.delete, color: Colors.white),
|
|
tooltip: 'Supprimer',
|
|
onPressed: _deleteSelectedContainers,
|
|
),
|
|
],
|
|
],
|
|
)
|
|
: AppBar(
|
|
title: const Text('Gestion des Containers'),
|
|
backgroundColor: AppColors.rouge,
|
|
leading: IconButton(
|
|
icon: const Icon(Icons.arrow_back),
|
|
tooltip: 'Retour à la gestion des équipements',
|
|
onPressed: () => Navigator.pushReplacementNamed(context, '/equipment_management'),
|
|
),
|
|
actions: [
|
|
IconButton(
|
|
icon: const Icon(Icons.logout, color: Colors.white),
|
|
onPressed: () async {
|
|
final shouldLogout = await showDialog<bool>(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: const Text('Déconnexion'),
|
|
content: const Text('Voulez-vous vraiment vous déconnecter ?'),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context, false),
|
|
child: const Text('Annuler'),
|
|
),
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context, true),
|
|
child: const Text('Déconnexion'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
if (shouldLogout == true && context.mounted) {
|
|
await context.read<LocalUserProvider>().signOut();
|
|
if (context.mounted) {
|
|
Navigator.pushReplacementNamed(context, '/login');
|
|
}
|
|
}
|
|
},
|
|
),
|
|
],
|
|
),
|
|
drawer: const MainDrawer(currentPage: '/container_management'),
|
|
floatingActionButton: !isSelectionMode
|
|
? FloatingActionButton.extended(
|
|
onPressed: () => _navigateToForm(context),
|
|
backgroundColor: AppColors.rouge,
|
|
icon: const Icon(Icons.add, color: Colors.white),
|
|
label: const Text(
|
|
'Nouveau Container',
|
|
style: TextStyle(color: Colors.white),
|
|
),
|
|
)
|
|
: null,
|
|
body: isMobile ? _buildMobileLayout() : _buildDesktopLayout(),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildMobileLayout() {
|
|
return Column(
|
|
children: [
|
|
_buildSearchBar(),
|
|
_buildMobileFilters(),
|
|
Expanded(child: _buildContainerList()),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildDesktopLayout() {
|
|
return Row(
|
|
children: [
|
|
SizedBox(
|
|
width: 250,
|
|
child: _buildSidebar(),
|
|
),
|
|
const VerticalDivider(width: 1, thickness: 1),
|
|
Expanded(
|
|
child: Column(
|
|
children: [
|
|
_buildSearchBar(),
|
|
Expanded(child: _buildContainerList()),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildSearchBar() {
|
|
return ManagementSearchBar(
|
|
controller: _searchController,
|
|
hintText: 'Rechercher un container...',
|
|
onChanged: (value) {
|
|
context.read<ContainerProvider>().setSearchQuery(value);
|
|
},
|
|
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,
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildMobileFilters() {
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
|
color: Colors.grey.shade50,
|
|
child: SingleChildScrollView(
|
|
scrollDirection: Axis.horizontal,
|
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
child: Row(
|
|
children: [
|
|
_buildTypeChip(null, 'Tous'),
|
|
const SizedBox(width: 8),
|
|
...ContainerType.values.map((type) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(right: 8),
|
|
child: _buildTypeChip(type, type.label),
|
|
);
|
|
}),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildTypeChip(ContainerType? type, String label) {
|
|
final isSelected = _selectedType == type;
|
|
final color = isSelected ? Colors.white : AppColors.noir;
|
|
|
|
return ChoiceChip(
|
|
label: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
if (type != null) ...[
|
|
type.getIcon(size: 16, color: color),
|
|
const SizedBox(width: 8),
|
|
],
|
|
Text(label),
|
|
],
|
|
),
|
|
selected: isSelected,
|
|
onSelected: (selected) {
|
|
setState(() {
|
|
_selectedType = selected ? type : null;
|
|
context.read<ContainerProvider>().setSelectedType(_selectedType);
|
|
});
|
|
},
|
|
selectedColor: AppColors.rouge,
|
|
labelStyle: TextStyle(
|
|
color: color,
|
|
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildSidebar() {
|
|
return Container(
|
|
color: Colors.grey.shade50,
|
|
child: ListView(
|
|
padding: const EdgeInsets.all(16),
|
|
children: [
|
|
Text(
|
|
'Filtres',
|
|
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
|
fontWeight: FontWeight.bold,
|
|
color: AppColors.noir,
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
// Filtre par type
|
|
Text(
|
|
'Type de container',
|
|
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
|
fontWeight: FontWeight.bold,
|
|
color: AppColors.noir,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
_buildFilterOption(null, 'Tous les types'),
|
|
...ContainerType.values.map((type) {
|
|
return _buildFilterOption(type, type.label);
|
|
}),
|
|
|
|
const Divider(height: 32),
|
|
|
|
// Filtre par statut
|
|
Text(
|
|
'Statut',
|
|
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
|
fontWeight: FontWeight.bold,
|
|
color: AppColors.noir,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
_buildStatusFilter(null, 'Tous les statuts'),
|
|
_buildStatusFilter(EquipmentStatus.available, 'Disponible'),
|
|
_buildStatusFilter(EquipmentStatus.inUse, 'En prestation'),
|
|
_buildStatusFilter(EquipmentStatus.maintenance, 'En maintenance'),
|
|
_buildStatusFilter(EquipmentStatus.outOfService, 'Hors service'),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildFilterOption(ContainerType? type, String label) {
|
|
final isSelected = _selectedType == type;
|
|
return RadioListTile<ContainerType?>(
|
|
title: Text(label),
|
|
value: type,
|
|
groupValue: _selectedType,
|
|
activeColor: AppColors.rouge,
|
|
dense: true,
|
|
contentPadding: EdgeInsets.zero,
|
|
onChanged: (value) {
|
|
setState(() {
|
|
_selectedType = value;
|
|
context.read<ContainerProvider>().setSelectedType(_selectedType);
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _buildStatusFilter(EquipmentStatus? status, String label) {
|
|
final isSelected = _selectedStatus == status;
|
|
return RadioListTile<EquipmentStatus?>(
|
|
title: Text(label),
|
|
value: status,
|
|
groupValue: _selectedStatus,
|
|
activeColor: AppColors.rouge,
|
|
dense: true,
|
|
contentPadding: EdgeInsets.zero,
|
|
onChanged: (value) {
|
|
setState(() {
|
|
_selectedStatus = value;
|
|
context.read<ContainerProvider>().setSelectedStatus(_selectedStatus);
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _buildContainerList() {
|
|
return Consumer<ContainerProvider>(
|
|
builder: (context, provider, child) {
|
|
return ManagementList<ContainerModel>(
|
|
stream: provider.containersStream,
|
|
cachedItems: _cachedContainers,
|
|
emptyMessage: 'Aucun container trouvé',
|
|
emptyIcon: Icons.inventory_2_outlined,
|
|
onDataReceived: (items) {
|
|
_cachedContainers = items;
|
|
},
|
|
itemBuilder: (container) => _buildContainerCard(container),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _buildContainerCard(ContainerModel container) {
|
|
final isSelected = isItemSelected(container.id);
|
|
|
|
return ManagementCard<ContainerModel>(
|
|
item: container,
|
|
getId: (c) => c.id,
|
|
getTitle: (c) => c.id,
|
|
getSubtitle: (c) => c.name,
|
|
getIcon: (c) => c.type.getIcon(
|
|
size: 40,
|
|
color: AppColors.rouge,
|
|
),
|
|
getInfoChips: (c) => [
|
|
InfoChip(
|
|
label: c.type.label,
|
|
icon: Icons.category,
|
|
),
|
|
InfoChip(
|
|
label: '${c.itemCount} items',
|
|
icon: Icons.inventory,
|
|
),
|
|
],
|
|
getStatusBadge: (c) => StatusBadge(
|
|
label: c.status.label,
|
|
color: c.status.color,
|
|
),
|
|
actions: const [
|
|
CardAction(
|
|
id: 'view',
|
|
label: 'Voir détails',
|
|
icon: Icons.visibility,
|
|
),
|
|
CardAction(
|
|
id: 'edit',
|
|
label: 'Modifier',
|
|
icon: Icons.edit,
|
|
),
|
|
CardAction(
|
|
id: 'qr',
|
|
label: 'QR Code',
|
|
icon: Icons.qr_code,
|
|
),
|
|
CardAction(
|
|
id: 'delete',
|
|
label: 'Supprimer',
|
|
icon: Icons.delete,
|
|
color: Colors.red,
|
|
),
|
|
],
|
|
onActionSelected: _handleMenuAction,
|
|
onTap: () {
|
|
if (isSelectionMode) {
|
|
toggleItemSelection(container.id);
|
|
} else {
|
|
_viewContainerDetails(container);
|
|
}
|
|
},
|
|
onLongPress: () {
|
|
if (!isSelectionMode) {
|
|
toggleSelectionMode();
|
|
toggleItemSelection(container.id);
|
|
}
|
|
},
|
|
isSelectionMode: isSelectionMode,
|
|
isSelected: isSelected,
|
|
onSelectionChanged: (value) {
|
|
toggleItemSelection(container.id);
|
|
},
|
|
);
|
|
}
|
|
|
|
|
|
void _handleMenuAction(String action, ContainerModel container) {
|
|
switch (action) {
|
|
case 'view':
|
|
_viewContainerDetails(container);
|
|
break;
|
|
case 'edit':
|
|
_editContainer(container);
|
|
break;
|
|
case 'qr':
|
|
_showQRCode(container);
|
|
break;
|
|
case 'delete':
|
|
_deleteContainer(container);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// 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) {
|
|
// Rafraîchir la liste
|
|
}
|
|
}
|
|
|
|
void _viewContainerDetails(ContainerModel container) async {
|
|
await Navigator.pushNamed(
|
|
context,
|
|
'/container_detail',
|
|
arguments: container,
|
|
);
|
|
}
|
|
|
|
void _editContainer(ContainerModel container) async {
|
|
await Navigator.pushNamed(
|
|
context,
|
|
'/container_form',
|
|
arguments: container,
|
|
);
|
|
}
|
|
|
|
|
|
Future<void> _generateQRCodesForSelected() async {
|
|
if (!hasSelection) return;
|
|
|
|
// Afficher un indicateur de chargement
|
|
showDialog(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (context) => const Center(
|
|
child: CircularProgressIndicator(color: AppColors.rouge),
|
|
),
|
|
);
|
|
|
|
try {
|
|
// Récupérer les containers sélectionnés
|
|
final containerProvider = context.read<ContainerProvider>();
|
|
final List<ContainerModel> selectedContainers = [];
|
|
final Map<String, List<EquipmentModel>> containerEquipmentMap = {};
|
|
|
|
for (final id in selectedIds) {
|
|
final container = await containerProvider.getContainerById(id);
|
|
if (container != null) {
|
|
selectedContainers.add(container);
|
|
// Charger les équipements pour ce container
|
|
final equipment = await containerProvider.getContainerEquipment(id);
|
|
containerEquipmentMap[id] = equipment;
|
|
}
|
|
}
|
|
|
|
// Fermer l'indicateur de chargement
|
|
if (mounted) {
|
|
Navigator.of(context).pop();
|
|
}
|
|
|
|
if (selectedContainers.isEmpty) {
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text('Aucun container trouvé')),
|
|
);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Afficher le dialogue de sélection de format avec le widget générique
|
|
if (mounted) {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => QRCodeFormatSelectorDialog<ContainerModel>(
|
|
itemList: selectedContainers,
|
|
getId: (c) => c.id,
|
|
getTitle: (c) => c.name,
|
|
getDetails: (ContainerModel c) {
|
|
final equipment = containerEquipmentMap[c.id] ?? <EquipmentModel>[];
|
|
return [
|
|
'Contenu (${equipment.length}):',
|
|
...equipment.take(5).map((eq) => '- ${eq.id}'),
|
|
if (equipment.length > 5) '... +${equipment.length - 5}',
|
|
];
|
|
},
|
|
dialogTitle: 'Générer ${selectedContainers.length} QR Code(s)',
|
|
),
|
|
);
|
|
}
|
|
} catch (e) {
|
|
// Fermer l'indicateur si une erreur survient
|
|
if (mounted) {
|
|
Navigator.of(context).pop();
|
|
}
|
|
|
|
DebugLog.error('[ContainerManagementPage] Error generating QR codes', e);
|
|
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text('Erreur lors de la génération : ${e.toString()}'),
|
|
backgroundColor: Colors.red,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> _deleteContainer(ContainerModel container) async {
|
|
final confirm = await showDialog<bool>(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: const Text('Confirmer la suppression'),
|
|
content: Text(
|
|
'Êtes-vous sûr de vouloir supprimer le container "${container.name}" ?\n\n'
|
|
'Cette action est irréversible.',
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context, false),
|
|
child: const Text('Annuler'),
|
|
),
|
|
ElevatedButton(
|
|
onPressed: () => Navigator.pop(context, true),
|
|
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
|
|
child: const Text('Supprimer'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
|
|
if (confirm == true && mounted) {
|
|
try {
|
|
await context.read<ContainerProvider>().deleteContainer(container.id);
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text('Container supprimé avec succès')),
|
|
);
|
|
}
|
|
} catch (e) {
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text('Erreur lors de la suppression: $e')),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> _deleteSelectedContainers() async {
|
|
final confirm = await showDialog<bool>(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: const Text('Confirmer la suppression'),
|
|
content: Text(
|
|
'Êtes-vous sûr de vouloir supprimer $selectedCount container(s) ?\n\n'
|
|
'Cette action est irréversible.',
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context, false),
|
|
child: const Text('Annuler'),
|
|
),
|
|
ElevatedButton(
|
|
onPressed: () => Navigator.pop(context, true),
|
|
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
|
|
child: const Text('Supprimer'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
|
|
if (confirm == true && mounted) {
|
|
try {
|
|
final provider = context.read<ContainerProvider>();
|
|
for (final id in selectedIds) {
|
|
await provider.deleteContainer(id);
|
|
}
|
|
disableSelectionMode();
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text('Containers supprimés avec succès')),
|
|
);
|
|
}
|
|
} catch (e) {
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text('Erreur lors de la suppression: $e')),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 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,
|
|
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,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|