Refactor: Centralisation des labels et icônes pour les enums

Centralise la gestion des libellés, couleurs et icônes pour `EquipmentStatus`, `EquipmentCategory`, et `ContainerType` en utilisant des extensions Dart.

- Ajout de nouvelles icônes SVG pour `flight-case`, `truss` et `tape`.
- Refactorisation des vues pour utiliser les nouvelles extensions, supprimant ainsi la logique d'affichage dupliquée.
- Mise à jour des `ChoiceChip` et des listes de filtres pour afficher les icônes à côté des labels.
This commit is contained in:
ElPoyo
2025-10-29 18:43:24 +01:00
parent 3fab69cb00
commit df6d54a007
16 changed files with 441 additions and 307 deletions

View File

@@ -1,4 +1,6 @@
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
@@ -57,6 +59,119 @@ String containerTypeLabel(ContainerType type) {
}
}
// 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)

View File

@@ -1,4 +1,6 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
enum EquipmentStatus {
available, // Disponible
@@ -99,6 +101,171 @@ EquipmentCategory equipmentCategoryFromString(String? category) {
}
}
// Extensions pour centraliser les informations d'affichage
extension EquipmentCategoryExtension on EquipmentCategory {
/// Retourne le label français de la catégorie
String get label {
switch (this) {
case EquipmentCategory.lighting:
return 'Lumière';
case EquipmentCategory.sound:
return 'Son';
case EquipmentCategory.video:
return 'Vidéo';
case EquipmentCategory.effect:
return 'Effets';
case EquipmentCategory.structure:
return 'Structure';
case EquipmentCategory.consumable:
return 'Consommable';
case EquipmentCategory.cable:
return 'Câble';
case EquipmentCategory.other:
return 'Autre';
}
}
/// Retourne l'icône Material de la catégorie
IconData get iconData {
switch (this) {
case EquipmentCategory.lighting:
return Icons.light_mode;
case EquipmentCategory.sound:
return Icons.volume_up;
case EquipmentCategory.video:
return Icons.videocam;
case EquipmentCategory.effect:
return Icons.auto_awesome;
case EquipmentCategory.structure:
return Icons.construction;
case EquipmentCategory.consumable:
return Icons.inventory_2;
case EquipmentCategory.cable:
return Icons.cable;
case EquipmentCategory.other:
return Icons.more_horiz;
}
}
/// Retourne le chemin de l'icône personnalisée (si disponible)
String? get customIconPath {
switch (this) {
case EquipmentCategory.structure:
return 'assets/icons/truss.svg';
case EquipmentCategory.consumable:
return 'assets/icons/tape.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 (sans ColorFilter si Material Icon)
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);
}
}
extension EquipmentStatusExtension on EquipmentStatus {
/// Retourne le label français du statut
String get label {
switch (this) {
case EquipmentStatus.available:
return 'Disponible';
case EquipmentStatus.inUse:
return 'En prestation';
case EquipmentStatus.rented:
return 'Loué';
case EquipmentStatus.lost:
return 'Perdu';
case EquipmentStatus.outOfService:
return 'HS';
case EquipmentStatus.maintenance:
return 'Maintenance';
}
}
/// Retourne la couleur associée au statut
Color get color {
switch (this) {
case EquipmentStatus.available:
return Colors.green;
case EquipmentStatus.inUse:
return Colors.blue;
case EquipmentStatus.rented:
return Colors.orange;
case EquipmentStatus.lost:
return Colors.red;
case EquipmentStatus.outOfService:
return Colors.red.shade900;
case EquipmentStatus.maintenance:
return Colors.amber;
}
}
}
class EquipmentModel {
final String id; // Identifiant unique (clé)
final String name; // Nom de l'équipement