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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M1793 5032 c-17 -9 -82 -75 -144 -147 l-112 -130 511 -3 c282 -1 742
-1 1024 0 l511 3 -112 130 c-62 72 -127 138 -144 147 -29 16 -93 17 -767 17
-674 0 -738 -1 -767 -17z"/>
<path d="M76 4426 c-75 -45 -75 -46 -76 -443 l0 -353 410 0 410 0 0 34 c0 43
36 96 80 118 31 16 71 18 363 18 356 0 374 -2 416 -56 12 -15 26 -47 32 -71
l11 -43 842 0 842 0 2 32 c3 41 29 85 65 112 27 20 41 21 360 24 217 2 345 -1
369 -8 46 -13 85 -59 99 -116 l11 -44 404 0 404 0 0 351 c0 345 0 352 -22 391
-14 24 -38 48 -60 59 -36 19 -101 19 -2480 19 l-2443 0 -39 -24z"/>
<path d="M1120 3295 l0 -205 150 0 150 0 0 205 0 205 -150 0 -150 0 0 -205z"/>
<path d="M3710 3295 l0 -205 150 0 150 0 0 205 0 205 -150 0 -150 0 0 -205z"/>
<path d="M0 3088 l0 -243 240 240 c132 132 240 241 240 242 0 2 -108 3 -240 3
l-240 0 0 -242z"/>
<path d="M1718 3118 c-3 -234 -8 -255 -71 -302 -27 -20 -41 -21 -370 -24 -382
-3 -388 -2 -434 67 -22 32 -23 44 -23 205 l0 171 -410 -410 -410 -410 0 -460
0 -460 601 -608 601 -607 1350 0 1350 0 609 602 609 603 0 470 1 470 -403 397
-403 397 -5 -157 c-6 -172 -15 -202 -73 -246 -27 -20 -41 -21 -370 -24 -382
-3 -388 -2 -434 67 -22 33 -23 42 -23 252 l0 219 -844 0 -845 0 -3 -212z"/>
<path d="M4875 3090 c132 -132 241 -240 242 -240 2 0 3 108 3 240 l0 240 -242
0 -243 0 240 -240z"/>
<path d="M0 731 c0 -321 1 -335 21 -371 40 -71 71 -80 283 -80 l186 0 0 -45
c0 -100 56 -160 147 -160 85 0 143 57 150 147 l4 53 -395 395 -396 395 0 -334z"/>
<path d="M4723 668 c-384 -384 -393 -394 -393 -432 0 -63 34 -121 84 -145 54
-27 81 -26 136 2 54 27 80 72 80 140 l0 47 185 0 c214 0 243 8 283 78 22 40
22 44 20 371 l-3 331 -392 -392z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M1979 5105 c-272 -46 -519 -179 -746 -403 -407 -401 -673 -1072 -701
-1767 -26 -644 82 -1147 345 -1610 131 -232 154 -409 69 -544 -39 -61 -145
-159 -201 -185 -104 -49 -330 -113 -585 -167 -58 -12 -106 -23 -108 -24 -4 -4
20 -99 27 -107 3 -4 59 0 124 7 160 19 167 16 231 -112 l36 -72 122 2 c140 2
157 -3 239 -80 l47 -44 116 26 c373 84 861 246 1020 339 94 55 141 166 139
331 -1 171 -47 317 -160 512 -254 440 -373 934 -373 1553 0 259 15 440 51 639
131 720 509 1334 1049 1703 24 16 8 17 -320 16 -231 0 -370 -4 -421 -13z"/>
<path d="M3276 5109 c-322 -47 -636 -237 -884 -534 -759 -909 -791 -2555 -68
-3530 86 -116 295 -319 401 -392 457 -309 952 -312 1410 -7 103 68 271 226
362 338 288 357 477 830 550 1381 24 182 24 628 0 810 -151 1141 -829 1953
-1622 1944 -49 -1 -116 -5 -149 -10z m399 -1047 c227 -79 439 -286 570 -557
282 -586 185 -1369 -222 -1796 -284 -297 -634 -357 -971 -167 -102 57 -260
212 -337 331 -337 521 -335 1288 7 1802 59 89 198 235 269 283 68 46 175 98
243 117 135 39 304 34 441 -13z"/>
<path d="M3307 3905 c-61 -15 -158 -60 -210 -97 -43 -30 -145 -127 -168 -159
l-19 -27 55 -113 c65 -138 107 -261 137 -411 19 -97 23 -144 23 -328 0 -184
-3 -231 -23 -328 -31 -152 -81 -302 -142 -423 -38 -76 -48 -105 -40 -118 5
-10 48 -54 95 -99 131 -125 263 -182 419 -182 350 0 651 328 756 825 27 130
37 373 21 514 -44 372 -212 694 -449 855 -132 90 -314 127 -455 91z"/>
<path d="M2327 628 c-3 -74 -11 -140 -20 -165 l-16 -43 226 0 226 0 -79 51
c-94 61 -164 118 -260 212 l-71 69 -6 -124z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M410 3418 c-70 -35 -90 -85 -90 -218 1 -152 35 -214 132 -235 l28 -7
0 -398 0 -398 -28 -7 c-97 -21 -131 -83 -132 -235 0 -133 20 -183 90 -217 l44
-23 2106 0 2106 0 44 23 c70 34 90 84 90 217 -1 152 -35 214 -132 235 l-28 7
0 398 0 398 28 7 c97 21 131 83 132 235 0 133 -20 183 -90 218 l-44 22 -2106
0 -2106 0 -44 -22z m4230 -218 l0 -80 -2080 0 -2080 0 0 80 0 80 2080 0 2080
0 0 -80z m-3770 -640 c-130 -260 -205 -400 -215 -400 -13 0 -15 51 -15 400 l0
400 215 0 215 0 -200 -400z m640 0 l200 -400 -430 0 -430 0 200 400 c197 393
201 400 230 400 29 0 33 -7 230 -400z m640 0 c-197 -393 -201 -400 -230 -400
-29 0 -33 7 -230 400 l-200 400 430 0 430 0 -200 -400z m640 0 l200 -400 -430
0 -430 0 200 400 c197 393 201 400 230 400 29 0 33 -7 230 -400z m640 0 c-197
-393 -201 -400 -230 -400 -29 0 -33 7 -230 400 l-200 400 430 0 430 0 -200
-400z m640 0 l200 -400 -430 0 -430 0 200 400 c197 393 201 400 230 400 29 0
33 -7 230 -400z m410 0 c0 -349 -2 -400 -15 -400 -10 0 -85 140 -215 400
l-200 400 215 0 215 0 0 -400z m160 -640 l0 -80 -2080 0 -2080 0 0 80 0 80
2080 0 2080 0 0 -80z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

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

View File

@@ -167,7 +167,7 @@ class _ContainerDetailPageState extends State<ContainerDetailPage> {
Expanded(
child: _buildInfoItem(
'Type',
containerTypeLabel(_container.type),
_container.type.label,
Icons.category,
),
),

View File

@@ -177,7 +177,7 @@ class _ContainerFormPageState extends State<ContainerFormPage> {
items: ContainerType.values.map((type) {
return DropdownMenuItem(
value: type,
child: Text(containerTypeLabel(type)),
child: Text(type.label),
);
}).toList(),
onChanged: (value) {

View File

@@ -190,7 +190,7 @@ class _ContainerManagementPageState extends State<ContainerManagementPage>
...ContainerType.values.map((type) {
return Padding(
padding: const EdgeInsets.only(right: 8),
child: _buildTypeChip(type, containerTypeLabel(type)),
child: _buildTypeChip(type, type.label),
);
}),
],
@@ -201,8 +201,19 @@ class _ContainerManagementPageState extends State<ContainerManagementPage>
Widget _buildTypeChip(ContainerType? type, String label) {
final isSelected = _selectedType == type;
final color = isSelected ? Colors.white : AppColors.noir;
return ChoiceChip(
label: Text(label),
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(() {
@@ -212,7 +223,7 @@ class _ContainerManagementPageState extends State<ContainerManagementPage>
},
selectedColor: AppColors.rouge,
labelStyle: TextStyle(
color: isSelected ? Colors.white : AppColors.noir,
color: color,
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
),
);
@@ -244,7 +255,7 @@ class _ContainerManagementPageState extends State<ContainerManagementPage>
const SizedBox(height: 8),
_buildFilterOption(null, 'Tous les types'),
...ContainerType.values.map((type) {
return _buildFilterOption(type, containerTypeLabel(type));
return _buildFilterOption(type, type.label);
}),
const Divider(height: 32),
@@ -403,8 +414,7 @@ class _ContainerManagementPageState extends State<ContainerManagementPage>
),
// Icône du type de container
Icon(
_getTypeIcon(container.type),
container.type.getIcon(
size: 40,
color: AppColors.rouge,
),
@@ -434,7 +444,7 @@ class _ContainerManagementPageState extends State<ContainerManagementPage>
Row(
children: [
_buildInfoChip(
containerTypeLabel(container.type),
container.type.label,
Icons.category,
),
const SizedBox(width: 8),
@@ -534,42 +544,17 @@ class _ContainerManagementPageState extends State<ContainerManagementPage>
}
Widget _buildStatusBadge(EquipmentStatus status) {
Color color;
String label;
switch (status) {
case EquipmentStatus.available:
color = Colors.green;
label = 'Disponible';
break;
case EquipmentStatus.inUse:
color = Colors.orange;
label = 'En prestation';
break;
case EquipmentStatus.maintenance:
color = Colors.blue;
label = 'Maintenance';
break;
case EquipmentStatus.outOfService:
color = Colors.red;
label = 'Hors service';
break;
default:
color = Colors.grey;
label = 'Autre';
}
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
color: status.color.withOpacity(0.1),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: color),
border: Border.all(color: status.color),
),
child: Text(
label,
status.label,
style: TextStyle(
color: color,
color: status.color,
fontWeight: FontWeight.bold,
fontSize: 12,
),
@@ -577,21 +562,6 @@ class _ContainerManagementPageState extends State<ContainerManagementPage>
);
}
IconData _getTypeIcon(ContainerType type) {
switch (type) {
case ContainerType.flightCase:
return Icons.work;
case ContainerType.pelicase:
return Icons.work_outline;
case ContainerType.bag:
return Icons.shopping_bag;
case ContainerType.openCrate:
return Icons.inventory_2;
case ContainerType.toolbox:
return Icons.handyman;
}
}
void _handleMenuAction(String action, ContainerModel container) {
switch (action) {

View File

@@ -7,7 +7,6 @@ import 'package:em2rp/providers/local_user_provider.dart';
import 'package:em2rp/services/equipment_service.dart';
import 'package:em2rp/services/qr_code_service.dart';
import 'package:em2rp/utils/colors.dart';
import 'package:em2rp/utils/permission_gate.dart';
import 'package:em2rp/views/widgets/nav/custom_app_bar.dart';
import 'package:em2rp/views/equipment_form_page.dart';
import 'package:em2rp/views/widgets/equipment/equipment_parent_containers.dart';
@@ -144,10 +143,9 @@ class _EquipmentDetailPageState extends State<EquipmentDetailPage> {
CircleAvatar(
backgroundColor: Colors.white,
radius: 30,
child: Icon(
_getCategoryIcon(widget.equipment.category),
color: AppColors.rouge,
child: widget.equipment.category.getIcon(
size: 32,
color: AppColors.rouge,
),
),
const SizedBox(width: 16),
@@ -187,7 +185,7 @@ class _EquipmentDetailPageState extends State<EquipmentDetailPage> {
}
Widget _buildStatusBadge() {
final statusInfo = _getStatusInfo(widget.equipment.status);
final status = widget.equipment.status;
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
@@ -201,17 +199,17 @@ class _EquipmentDetailPageState extends State<EquipmentDetailPage> {
width: 8,
height: 8,
decoration: BoxDecoration(
color: statusInfo.$2,
color: status.color,
shape: BoxShape.circle,
),
),
const SizedBox(width: 8),
Text(
statusInfo.$1,
status.label,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: statusInfo.$2,
color: status.color,
),
),
],
@@ -240,14 +238,14 @@ class _EquipmentDetailPageState extends State<EquipmentDetailPage> {
],
),
const Divider(height: 24),
_buildInfoRow('Catégorie', _getCategoryName(widget.equipment.category)),
_buildInfoRow('Catégorie', widget.equipment.category.label),
if (widget.equipment.brand != null && widget.equipment.brand!.isNotEmpty)
_buildInfoRow('Marque', widget.equipment.brand!),
if (widget.equipment.model != null && widget.equipment.model!.isNotEmpty)
_buildInfoRow('Modèle', widget.equipment.model!),
if (widget.equipment.category != EquipmentCategory.consumable &&
widget.equipment.category != EquipmentCategory.cable)
_buildInfoRow('Statut', _getStatusInfo(widget.equipment.status).$1),
_buildInfoRow('Statut', widget.equipment.status.label),
],
),
),
@@ -808,65 +806,6 @@ class _EquipmentDetailPageState extends State<EquipmentDetailPage> {
);
}
IconData _getCategoryIcon(EquipmentCategory category) {
switch (category) {
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;
}
}
String _getCategoryName(EquipmentCategory category) {
switch (category) {
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';
}
}
(String, Color) _getStatusInfo(EquipmentStatus status) {
switch (status) {
case EquipmentStatus.available:
return ('Disponible', Colors.green);
case EquipmentStatus.inUse:
return ('En prestation', Colors.blue);
case EquipmentStatus.rented:
return ('Loué', Colors.orange);
case EquipmentStatus.lost:
return ('Perdu', Colors.red);
case EquipmentStatus.outOfService:
return ('HS', Colors.red[900]!);
case EquipmentStatus.maintenance:
return ('Maintenance', Colors.amber);
}
}
(String, IconData) _getMaintenanceTypeInfo(MaintenanceType type) {
switch (type) {
case MaintenanceType.preventive:

View File

@@ -275,7 +275,7 @@ class _EquipmentFormPageState extends State<EquipmentFormPage> {
items: EquipmentCategory.values.map((category) {
return DropdownMenuItem(
value: category,
child: Text(_getCategoryLabel(category)),
child: Text(category.label),
);
}).toList(),
onChanged: (value) {
@@ -301,7 +301,7 @@ class _EquipmentFormPageState extends State<EquipmentFormPage> {
items: EquipmentStatus.values.map((status) {
return DropdownMenuItem(
value: status,
child: Text(_getStatusLabel(status)),
child: Text(status.label),
);
}).toList(),
onChanged: (value) {
@@ -654,47 +654,4 @@ class _EquipmentFormPageState extends State<EquipmentFormPage> {
if (mounted) setState(() => _isLoading = false);
}
}
// Correction des enums dans _getCategoryLabel
String _getCategoryLabel(EquipmentCategory category) {
switch (category) {
case EquipmentCategory.lighting:
return 'Lumière';
case EquipmentCategory.sound:
return 'Son';
case EquipmentCategory.video:
return 'Vidéo';
case EquipmentCategory.effect:
return 'Effet';
case EquipmentCategory.structure:
return 'Structure';
case EquipmentCategory.consumable:
return 'Consommable';
case EquipmentCategory.cable:
return 'Câble';
case EquipmentCategory.other:
default:
return 'Autre';
}
}
// Correction des enums dans _getStatusLabel
String _getStatusLabel(EquipmentStatus status) {
switch (status) {
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';
default:
return 'Autre';
}
}
}

View File

@@ -293,7 +293,6 @@ class _EquipmentManagementPageState extends State<EquipmentManagementPage>
},
),
),
const Divider(),
// Filtres par catégorie
Padding(
padding:
@@ -353,44 +352,34 @@ class _EquipmentManagementPageState extends State<EquipmentManagementPage>
}
List<Widget> _buildCategoryChips() {
final categories = [
(EquipmentCategory.lighting, Icons.light_mode, 'Lumière'),
(EquipmentCategory.sound, Icons.volume_up, 'Son'),
(EquipmentCategory.video, Icons.videocam, 'Vidéo'),
(EquipmentCategory.effect, Icons.auto_awesome, 'Effets'),
(EquipmentCategory.structure, Icons.construction, 'Structure'),
(EquipmentCategory.consumable, Icons.inventory_2, 'Consommable'),
(EquipmentCategory.cable, Icons.cable, 'Câble'),
(EquipmentCategory.other, Icons.more_horiz, 'Autre'),
];
return EquipmentCategory.values.map((category) {
final isSelected = _selectedCategory == category;
final color = isSelected ? Colors.white : AppColors.rouge;
return categories.map((cat) {
final isSelected = _selectedCategory == cat.$1;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4.0),
child: ChoiceChip(
label: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
cat.$2,
category.getIcon(
size: 16,
color: isSelected ? Colors.white : AppColors.rouge,
color: color,
),
const SizedBox(width: 8),
Text(cat.$3),
Text(category.label),
],
),
selected: isSelected,
onSelected: (selected) {
if (selected) {
setState(() => _selectedCategory = cat.$1);
context.read<EquipmentProvider>().setSelectedCategory(cat.$1);
setState(() => _selectedCategory = category);
context.read<EquipmentProvider>().setSelectedCategory(category);
}
},
selectedColor: AppColors.rouge,
labelStyle: TextStyle(
color: isSelected ? Colors.white : AppColors.rouge,
color: color,
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
),
),
@@ -399,26 +388,17 @@ class _EquipmentManagementPageState extends State<EquipmentManagementPage>
}
List<Widget> _buildCategoryListTiles() {
final categories = [
(EquipmentCategory.lighting, Icons.light_mode, 'Lumière'),
(EquipmentCategory.sound, Icons.volume_up, 'Son'),
(EquipmentCategory.video, Icons.videocam, 'Vidéo'),
(EquipmentCategory.effect, Icons.auto_awesome, 'Effets'),
(EquipmentCategory.structure, Icons.construction, 'Structure'),
(EquipmentCategory.consumable, Icons.inventory_2, 'Consommable'),
(EquipmentCategory.cable, Icons.cable, 'Câble'),
(EquipmentCategory.other, Icons.more_horiz, 'Autre'),
];
return EquipmentCategory.values.map((category) {
final isSelected = _selectedCategory == category;
final color = isSelected ? AppColors.rouge : Colors.grey[600]!;
return categories.map((cat) {
final isSelected = _selectedCategory == cat.$1;
return ListTile(
leading: Icon(
cat.$2,
color: isSelected ? AppColors.rouge : Colors.grey[600],
leading: category.getIcon(
size: 24,
color: color,
),
title: Text(
cat.$3,
category.label,
style: TextStyle(
color: isSelected ? AppColors.rouge : Colors.black87,
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
@@ -427,8 +407,8 @@ class _EquipmentManagementPageState extends State<EquipmentManagementPage>
selected: isSelected,
selectedTileColor: AppColors.rouge.withOpacity(0.1),
onTap: () {
setState(() => _selectedCategory = cat.$1);
context.read<EquipmentProvider>().setSelectedCategory(cat.$1);
setState(() => _selectedCategory = category);
context.read<EquipmentProvider>().setSelectedCategory(category);
},
);
}).toList();
@@ -506,10 +486,10 @@ class _EquipmentManagementPageState extends State<EquipmentManagementPage>
)
: CircleAvatar(
backgroundColor:
_getStatusColor(equipment.status).withOpacity(0.2),
child: Icon(
_getCategoryIcon(equipment.category),
color: _getStatusColor(equipment.status),
equipment.status.color.withOpacity(0.2),
child: equipment.category.getIcon(
size: 20,
color: Colors.black,
),
),
title: Row(
@@ -659,67 +639,24 @@ class _EquipmentManagementPageState extends State<EquipmentManagementPage>
}
Widget _buildStatusBadge(EquipmentStatus status) {
final statusInfo = _getStatusInfo(status);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: statusInfo.$2.withOpacity(0.2),
color: status.color.withOpacity(0.2),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: statusInfo.$2),
border: Border.all(color: status.color),
),
child: Text(
statusInfo.$1,
status.label,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: statusInfo.$2,
color: status.color,
),
),
);
}
(String, Color) _getStatusInfo(EquipmentStatus status) {
switch (status) {
case EquipmentStatus.available:
return ('Disponible', Colors.green);
case EquipmentStatus.inUse:
return ('En prestation', Colors.blue);
case EquipmentStatus.rented:
return ('Loué', Colors.orange);
case EquipmentStatus.lost:
return ('Perdu', Colors.red);
case EquipmentStatus.outOfService:
return ('HS', Colors.red[900]!);
case EquipmentStatus.maintenance:
return ('Maintenance', Colors.amber);
}
}
Color _getStatusColor(EquipmentStatus status) {
return _getStatusInfo(status).$2;
}
IconData _getCategoryIcon(EquipmentCategory category) {
switch (category) {
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;
}
}
// Actions
void _createNewEquipment() {
Navigator.push(

View File

@@ -25,8 +25,7 @@ class ContainerHeaderCard extends StatelessWidget {
children: [
Row(
children: [
Icon(
_getTypeIcon(container.type),
container.type.getIcon(
size: 60,
color: AppColors.rouge,
),
@@ -62,7 +61,7 @@ class ContainerHeaderCard extends StatelessWidget {
child: _buildInfoItem(
context,
'Type',
containerTypeLabel(container.type),
container.type.label,
Icons.category,
),
),
@@ -70,9 +69,9 @@ class ContainerHeaderCard extends StatelessWidget {
child: _buildInfoItem(
context,
'Statut',
_getStatusLabel(container.status),
container.status.label,
Icons.info,
statusColor: _getStatusColor(container.status),
statusColor: container.status.color,
),
),
],
@@ -148,50 +147,5 @@ class ContainerHeaderCard extends StatelessWidget {
final totalWeight = container.calculateTotalWeight(equipmentList);
return '${totalWeight.toStringAsFixed(1)} kg';
}
IconData _getTypeIcon(ContainerType type) {
switch (type) {
case ContainerType.flightCase:
return Icons.work;
case ContainerType.pelicase:
return Icons.work_outline;
case ContainerType.bag:
return Icons.shopping_bag;
case ContainerType.openCrate:
return Icons.inventory_2;
case ContainerType.toolbox:
return Icons.handyman;
}
}
String _getStatusLabel(EquipmentStatus status) {
switch (status) {
case EquipmentStatus.available:
return 'Disponible';
case EquipmentStatus.inUse:
return 'En prestation';
case EquipmentStatus.maintenance:
return 'Maintenance';
case EquipmentStatus.outOfService:
return 'Hors service';
default:
return 'Autre';
}
}
Color _getStatusColor(EquipmentStatus status) {
switch (status) {
case EquipmentStatus.available:
return Colors.green;
case EquipmentStatus.inUse:
return Colors.orange;
case EquipmentStatus.maintenance:
return Colors.blue;
case EquipmentStatus.outOfService:
return Colors.red;
default:
return Colors.grey;
}
}
}

View File

@@ -40,7 +40,7 @@ dependencies:
http: ^1.1.2
flutter_dotenv: ^6.0.0
google_fonts: ^6.1.0
flutter_svg: ^2.0.9
flutter_svg: ^2.2.1
cached_network_image: ^3.3.1
flutter_staggered_grid_view: ^0.7.0
shimmer: ^3.0.0
@@ -67,3 +67,5 @@ flutter:
assets:
- assets/
- assets/images/
- assets/logos/
- assets/icons/