import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:em2rp/models/equipment_model.dart'; /// Type de container enum ContainerType { flightCase, // Flight case pelicase, // Pelicase bag, // Sac openCrate, // Caisse ouverte toolbox, // Boîte à outils } String containerTypeToString(ContainerType type) { switch (type) { case ContainerType.flightCase: return 'FLIGHT_CASE'; case ContainerType.pelicase: return 'PELICASE'; case ContainerType.bag: return 'BAG'; case ContainerType.openCrate: return 'OPEN_CRATE'; case ContainerType.toolbox: return 'TOOLBOX'; } } ContainerType containerTypeFromString(String? type) { switch (type) { case 'FLIGHT_CASE': return ContainerType.flightCase; case 'PELICASE': return ContainerType.pelicase; case 'BAG': return ContainerType.bag; case 'OPEN_CRATE': return ContainerType.openCrate; case 'TOOLBOX': return ContainerType.toolbox; default: return ContainerType.flightCase; } } String containerTypeLabel(ContainerType type) { switch (type) { case ContainerType.flightCase: return 'Flight Case'; case ContainerType.pelicase: return 'Pelicase'; case ContainerType.bag: return 'Sac'; case ContainerType.openCrate: return 'Caisse Ouverte'; case ContainerType.toolbox: return 'Boîte à Outils'; } } // Extensions pour centraliser les informations d'affichage extension ContainerTypeExtension on ContainerType { /// Retourne le label français du type de container String get label { switch (this) { case ContainerType.flightCase: return 'Flight Case'; case ContainerType.pelicase: return 'Pelicase'; case ContainerType.bag: return 'Sac'; case ContainerType.openCrate: return 'Caisse Ouverte'; case ContainerType.toolbox: return 'Boîte à Outils'; } } /// Retourne l'icône Material du type de container IconData get iconData { switch (this) { case ContainerType.flightCase: return Icons.work; case ContainerType.pelicase: return Icons.inventory_2; case ContainerType.bag: return Icons.shopping_bag; case ContainerType.openCrate: return Icons.inventory; case ContainerType.toolbox: return Icons.build_circle; } } /// Retourne le chemin de l'icône personnalisée (si disponible) String? get customIconPath { switch (this) { case ContainerType.flightCase: return 'assets/icons/flight-case.svg'; default: return null; } } /// Vérifie si une icône personnalisée est disponible bool get hasCustomIcon => customIconPath != null; /// Retourne l'icône Widget à afficher (unifié pour Material et personnalisé) Widget getIcon({double size = 24, Color? color}) { final customPath = customIconPath; if (customPath != null) { // Détection automatique du format (SVG ou PNG) final isSvg = customPath.toLowerCase().endsWith('.svg'); if (isSvg) { // SVG : on peut appliquer la couleur sans dégrader la qualité return SvgPicture.asset( customPath, width: size, height: size, colorFilter: color != null ? ColorFilter.mode(color, BlendMode.srcIn) : null, placeholderBuilder: (context) => Icon(iconData, size: size, color: color), ); } else { // PNG : on n'applique PAS le color filter pour préserver la qualité return Image.asset( customPath, width: size, height: size, filterQuality: FilterQuality.high, errorBuilder: (context, error, stackTrace) { return Icon(iconData, size: size, color: color); }, ); } } return Icon(iconData, size: size, color: color); } /// Version pour CircleAvatar et contextes similaires Widget getIconForAvatar({double size = 24, Color? color}) { final customPath = customIconPath; if (customPath != null) { final isSvg = customPath.toLowerCase().endsWith('.svg'); if (isSvg) { return SvgPicture.asset( customPath, width: size, height: size, colorFilter: color != null ? ColorFilter.mode(color, BlendMode.srcIn) : null, placeholderBuilder: (context) => Icon(iconData, size: size, color: color), ); } else { return Image.asset( customPath, width: size, height: size, filterQuality: FilterQuality.high, errorBuilder: (context, error, stackTrace) { return Icon(iconData, size: size, color: color); }, ); } } return Icon(iconData, size: size, color: color); } } /// Modèle de container/boîte pour le matériel class ContainerModel { final String id; // Identifiant unique (généré comme pour équipement) final String name; // Nom du container final ContainerType type; // Type de container final EquipmentStatus status; // Statut actuel (même que équipement) // Caractéristiques physiques final double? weight; // Poids à vide (kg) final double? length; // Longueur (cm) final double? width; // Largeur (cm) final double? height; // Hauteur (cm) // Contenu final List equipmentIds; // IDs des équipements contenus // Événement final String? eventId; // ID de l'événement actuel (si en prestation) // Métadonnées final String? notes; // Notes additionnelles final DateTime createdAt; // Date de création final DateTime updatedAt; // Date de mise à jour // Historique simple (optionnel) final List history; // Historique des modifications ContainerModel({ required this.id, required this.name, required this.type, this.status = EquipmentStatus.available, this.weight, this.length, this.width, this.height, this.equipmentIds = const [], this.eventId, this.notes, required this.createdAt, required this.updatedAt, this.history = const [], }); /// Vérifier si le container est vide bool get isEmpty => equipmentIds.isEmpty; /// Nombre d'équipements dans le container int get itemCount => equipmentIds.length; /// Calculer le volume (m³) double? get volume { if (length == null || width == null || height == null) return null; return (length! * width! * height!) / 1000000; // cm³ to m³ } /// Calculer le poids total (poids vide + équipements) /// Nécessite la liste des équipements double calculateTotalWeight(List equipment) { double total = weight ?? 0.0; for (final eq in equipment) { if (equipmentIds.contains(eq.id) && eq.weight != null) { total += eq.weight!; } } return total; } /// Factory depuis Firestore factory ContainerModel.fromMap(Map map, String id) { final List equipmentIdsRaw = map['equipmentIds'] ?? []; final List equipmentIds = equipmentIdsRaw.map((e) => e.toString()).toList(); final List historyRaw = map['history'] ?? []; final List history = historyRaw .map((e) => ContainerHistoryEntry.fromMap(e as Map)) .toList(); return ContainerModel( id: id, name: map['name'] ?? '', type: containerTypeFromString(map['type']), status: equipmentStatusFromString(map['status']), weight: map['weight']?.toDouble(), length: map['length']?.toDouble(), width: map['width']?.toDouble(), height: map['height']?.toDouble(), equipmentIds: equipmentIds, eventId: map['eventId'], notes: map['notes'], createdAt: (map['createdAt'] as Timestamp?)?.toDate() ?? DateTime.now(), updatedAt: (map['updatedAt'] as Timestamp?)?.toDate() ?? DateTime.now(), history: history, ); } /// Convertir en Map pour Firestore Map toMap() { return { 'name': name, 'type': containerTypeToString(type), 'status': equipmentStatusToString(status), 'weight': weight, 'length': length, 'width': width, 'height': height, 'equipmentIds': equipmentIds, 'eventId': eventId, 'notes': notes, 'createdAt': Timestamp.fromDate(createdAt), 'updatedAt': Timestamp.fromDate(updatedAt), 'history': history.map((e) => e.toMap()).toList(), }; } /// Copier avec modifications ContainerModel copyWith({ String? id, String? name, ContainerType? type, EquipmentStatus? status, double? weight, double? length, double? width, double? height, List? equipmentIds, String? eventId, String? notes, DateTime? createdAt, DateTime? updatedAt, List? history, }) { return ContainerModel( id: id ?? this.id, name: name ?? this.name, type: type ?? this.type, status: status ?? this.status, weight: weight ?? this.weight, length: length ?? this.length, width: width ?? this.width, height: height ?? this.height, equipmentIds: equipmentIds ?? this.equipmentIds, eventId: eventId ?? this.eventId, notes: notes ?? this.notes, createdAt: createdAt ?? this.createdAt, updatedAt: updatedAt ?? this.updatedAt, history: history ?? this.history, ); } } /// Entrée d'historique pour un container class ContainerHistoryEntry { final DateTime timestamp; final String action; // 'added', 'removed', 'status_change', etc. final String? equipmentId; // ID de l'équipement concerné (si applicable) final String? previousValue; // Valeur précédente final String? newValue; // Nouvelle valeur final String? userId; // ID de l'utilisateur ayant fait la modification ContainerHistoryEntry({ required this.timestamp, required this.action, this.equipmentId, this.previousValue, this.newValue, this.userId, }); factory ContainerHistoryEntry.fromMap(Map map) { return ContainerHistoryEntry( timestamp: (map['timestamp'] as Timestamp?)?.toDate() ?? DateTime.now(), action: map['action'] ?? '', equipmentId: map['equipmentId'], previousValue: map['previousValue'], newValue: map['newValue'], userId: map['userId'], ); } Map toMap() { return { 'timestamp': Timestamp.fromDate(timestamp), 'action': action, 'equipmentId': equipmentId, 'previousValue': previousValue, 'newValue': newValue, 'userId': userId, }; } }