feat: Mise à jour à la version 1.1.19 et amélioration du cache/pagination pour la sélection d'équipements
- Mise à jour de la version de l'application à `1.1.19` dans `app_version.dart` et `version.json`. - Correction d'un bug de cache dans `EquipmentSelectionDialog` qui empêchait l'affichage de certains équipements lors de la sélection. - Introduction d'une fonction utilitaire `shouldAutoLoadNextPage` et de tests unitaires associés pour fiabiliser le chargement automatique des données. - Ajout d'une gestion de préchargement automatique dans `EquipmentSelectionDialog` lorsque la liste n'est pas assez longue pour activer le défilement (évite les vues tronquées). - Amélioration de `ContainerFormPage` pour forcer le rechargement complet de la liste des équipements, évitant ainsi les conflits avec les états de pagination d'autres écrans. - Optimisation du chargement des conflits de disponibilité et des quantités via un chargement par lots (batch). - Nettoyage du code et amélioration de la lisibilité des fichiers `container_form_page.dart` et `equipment_selection_dialog.dart`.
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
# Changelog - EM2RP
|
||||
|
||||
Toutes les modifications notables de ce projet seront documentées dans ce fichier.
|
||||
|
||||
## 24/03/2026
|
||||
Fix BUG : Problème de cache avec les équipements non affichés dans le dialog de sélection d'équipement. Amélioration de la gestion du cache pour éviter les problèmes d'affichage.
|
||||
|
||||
## 12/03/2026bis
|
||||
Fix BUG : Ajout equipement à un evenement existant, boutons de modification de statut d'un evenement ne fonctionnaient pas. Refonte legere de la page calendrier.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/// Configuration de la version de l'application
|
||||
class AppVersion {
|
||||
static const String version = '1.1.18';
|
||||
static const String version = '1.1.19';
|
||||
|
||||
/// Retourne la version complète de l'application
|
||||
static String get fullVersion => 'v$version';
|
||||
|
||||
@@ -100,7 +100,6 @@ class _ContainerFormPageState extends State<ContainerFormPage> {
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(24),
|
||||
children: [
|
||||
|
||||
// Nom
|
||||
TextFormField(
|
||||
controller: _nameController,
|
||||
@@ -257,7 +256,8 @@ class _ContainerFormPageState extends State<ContainerFormPage> {
|
||||
border: OutlineInputBorder(),
|
||||
prefixIcon: Icon(Icons.scale),
|
||||
),
|
||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||||
keyboardType:
|
||||
const TextInputType.numberWithOptions(decimal: true),
|
||||
validator: (value) {
|
||||
if (value != null && value.isNotEmpty) {
|
||||
if (double.tryParse(value) == null) {
|
||||
@@ -279,7 +279,8 @@ class _ContainerFormPageState extends State<ContainerFormPage> {
|
||||
labelText: 'Longueur (cm)',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
keyboardType: TextInputType.numberWithOptions(decimal: true),
|
||||
keyboardType:
|
||||
TextInputType.numberWithOptions(decimal: true),
|
||||
validator: (value) {
|
||||
if (value != null && value.isNotEmpty) {
|
||||
if (double.tryParse(value) == null) {
|
||||
@@ -298,7 +299,8 @@ class _ContainerFormPageState extends State<ContainerFormPage> {
|
||||
labelText: 'Largeur (cm)',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
keyboardType: TextInputType.numberWithOptions(decimal: true),
|
||||
keyboardType:
|
||||
TextInputType.numberWithOptions(decimal: true),
|
||||
validator: (value) {
|
||||
if (value != null && value.isNotEmpty) {
|
||||
if (double.tryParse(value) == null) {
|
||||
@@ -317,7 +319,8 @@ class _ContainerFormPageState extends State<ContainerFormPage> {
|
||||
labelText: 'Hauteur (cm)',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
keyboardType: TextInputType.numberWithOptions(decimal: true),
|
||||
keyboardType:
|
||||
TextInputType.numberWithOptions(decimal: true),
|
||||
validator: (value) {
|
||||
if (value != null && value.isNotEmpty) {
|
||||
if (double.tryParse(value) == null) {
|
||||
@@ -452,6 +455,11 @@ class _ContainerFormPageState extends State<ContainerFormPage> {
|
||||
Future<void> _selectEquipment() async {
|
||||
final equipmentProvider = context.read<EquipmentProvider>();
|
||||
|
||||
// Toujours charger la liste complète pour éviter d'afficher uniquement
|
||||
// la page paginée active d'un autre écran.
|
||||
await equipmentProvider.loadEquipments();
|
||||
if (!mounted) return;
|
||||
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (context) => _EquipmentSelectorDialog(
|
||||
@@ -460,6 +468,7 @@ class _ContainerFormPageState extends State<ContainerFormPage> {
|
||||
),
|
||||
);
|
||||
|
||||
if (!mounted) return;
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@@ -535,7 +544,8 @@ class _ContainerFormPageState extends State<ContainerFormPage> {
|
||||
equipmentId: equipmentId,
|
||||
);
|
||||
} catch (e) {
|
||||
DebugLog.error('Erreur lors de l\'ajout de l\'équipement $equipmentId', e);
|
||||
DebugLog.error(
|
||||
'Erreur lors de l\'ajout de l\'équipement $equipmentId', e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -573,7 +583,8 @@ class _ContainerFormPageState extends State<ContainerFormPage> {
|
||||
});
|
||||
|
||||
// Gérer les équipements ajoutés
|
||||
final addedEquipment = _selectedEquipmentIds.difference(container.equipmentIds.toSet());
|
||||
final addedEquipment =
|
||||
_selectedEquipmentIds.difference(container.equipmentIds.toSet());
|
||||
for (final equipmentId in addedEquipment) {
|
||||
try {
|
||||
await provider.addEquipmentToContainer(
|
||||
@@ -581,12 +592,14 @@ class _ContainerFormPageState extends State<ContainerFormPage> {
|
||||
equipmentId: equipmentId,
|
||||
);
|
||||
} catch (e) {
|
||||
DebugLog.error('Erreur lors de l\'ajout de l\'équipement $equipmentId', e);
|
||||
DebugLog.error(
|
||||
'Erreur lors de l\'ajout de l\'équipement $equipmentId', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Gérer les équipements retirés
|
||||
final removedEquipment = container.equipmentIds.toSet().difference(_selectedEquipmentIds);
|
||||
final removedEquipment =
|
||||
container.equipmentIds.toSet().difference(_selectedEquipmentIds);
|
||||
for (final equipmentId in removedEquipment) {
|
||||
try {
|
||||
await provider.removeEquipmentFromContainer(
|
||||
@@ -594,7 +607,8 @@ class _ContainerFormPageState extends State<ContainerFormPage> {
|
||||
equipmentId: equipmentId,
|
||||
);
|
||||
} catch (e) {
|
||||
DebugLog.error('Erreur lors du retrait de l\'équipement $equipmentId', e);
|
||||
DebugLog.error(
|
||||
'Erreur lors du retrait de l\'équipement $equipmentId', e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -630,7 +644,8 @@ class _EquipmentSelectorDialog extends StatefulWidget {
|
||||
});
|
||||
|
||||
@override
|
||||
State<_EquipmentSelectorDialog> createState() => _EquipmentSelectorDialogState();
|
||||
State<_EquipmentSelectorDialog> createState() =>
|
||||
_EquipmentSelectorDialogState();
|
||||
}
|
||||
|
||||
class _EquipmentSelectorDialogState extends State<_EquipmentSelectorDialog> {
|
||||
@@ -638,12 +653,14 @@ class _EquipmentSelectorDialogState extends State<_EquipmentSelectorDialog> {
|
||||
EquipmentCategory? _filterCategory;
|
||||
String _searchQuery = '';
|
||||
late Set<String> _tempSelectedIds;
|
||||
late final Future<void> _loadingFuture;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Créer une copie temporaire des IDs sélectionnés
|
||||
_tempSelectedIds = Set<String>.from(widget.selectedIds);
|
||||
_loadingFuture = widget.equipmentProvider.loadEquipments();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -729,7 +746,8 @@ class _EquipmentSelectorDialogState extends State<_EquipmentSelectorDialog> {
|
||||
},
|
||||
selectedColor: AppColors.rouge,
|
||||
labelStyle: TextStyle(
|
||||
color: _filterCategory == null ? Colors.white : Colors.black,
|
||||
color:
|
||||
_filterCategory == null ? Colors.white : Colors.black,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
@@ -746,7 +764,9 @@ class _EquipmentSelectorDialogState extends State<_EquipmentSelectorDialog> {
|
||||
},
|
||||
selectedColor: AppColors.rouge,
|
||||
labelStyle: TextStyle(
|
||||
color: _filterCategory == category ? Colors.white : Colors.black,
|
||||
color: _filterCategory == category
|
||||
? Colors.white
|
||||
: Colors.black,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -778,8 +798,8 @@ class _EquipmentSelectorDialogState extends State<_EquipmentSelectorDialog> {
|
||||
|
||||
// Liste des équipements
|
||||
Expanded(
|
||||
child: StreamBuilder<List<EquipmentModel>>(
|
||||
stream: widget.equipmentProvider.equipmentStream,
|
||||
child: FutureBuilder<void>(
|
||||
future: _loadingFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
@@ -789,11 +809,15 @@ class _EquipmentSelectorDialogState extends State<_EquipmentSelectorDialog> {
|
||||
return Center(child: Text('Erreur: ${snapshot.error}'));
|
||||
}
|
||||
|
||||
var equipment = snapshot.data ?? [];
|
||||
var equipment = List<EquipmentModel>.from(
|
||||
widget.equipmentProvider.allEquipment,
|
||||
);
|
||||
|
||||
// Filtrer par catégorie
|
||||
if (_filterCategory != null) {
|
||||
equipment = equipment.where((e) => e.category == _filterCategory).toList();
|
||||
equipment = equipment
|
||||
.where((e) => e.category == _filterCategory)
|
||||
.toList();
|
||||
}
|
||||
|
||||
// Filtrer par recherche
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,13 @@
|
||||
bool shouldAutoLoadNextPage({
|
||||
required bool hasMoreData,
|
||||
required bool isLoadingMore,
|
||||
required bool hasClients,
|
||||
required double maxScrollExtent,
|
||||
}) {
|
||||
if (!hasMoreData || isLoadingMore) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the list cannot scroll yet, preload the next page to avoid a truncated view.
|
||||
return !hasClients || maxScrollExtent <= 0;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import 'package:em2rp/views/widgets/event/equipment_selection_pagination.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
group('shouldAutoLoadNextPage', () {
|
||||
test('returns false when there is no more data', () {
|
||||
final result = shouldAutoLoadNextPage(
|
||||
hasMoreData: false,
|
||||
isLoadingMore: false,
|
||||
hasClients: true,
|
||||
maxScrollExtent: 100,
|
||||
);
|
||||
|
||||
expect(result, isFalse);
|
||||
});
|
||||
|
||||
test('returns false while a page is already loading', () {
|
||||
final result = shouldAutoLoadNextPage(
|
||||
hasMoreData: true,
|
||||
isLoadingMore: true,
|
||||
hasClients: true,
|
||||
maxScrollExtent: 0,
|
||||
);
|
||||
|
||||
expect(result, isFalse);
|
||||
});
|
||||
|
||||
test('returns true when list has no scroll client yet', () {
|
||||
final result = shouldAutoLoadNextPage(
|
||||
hasMoreData: true,
|
||||
isLoadingMore: false,
|
||||
hasClients: false,
|
||||
maxScrollExtent: 0,
|
||||
);
|
||||
|
||||
expect(result, isTrue);
|
||||
});
|
||||
|
||||
test('returns true when list is not scrollable yet', () {
|
||||
final result = shouldAutoLoadNextPage(
|
||||
hasMoreData: true,
|
||||
isLoadingMore: false,
|
||||
hasClients: true,
|
||||
maxScrollExtent: 0,
|
||||
);
|
||||
|
||||
expect(result, isTrue);
|
||||
});
|
||||
|
||||
test('returns false when list is scrollable', () {
|
||||
final result = shouldAutoLoadNextPage(
|
||||
hasMoreData: true,
|
||||
isLoadingMore: false,
|
||||
hasClients: true,
|
||||
maxScrollExtent: 250,
|
||||
);
|
||||
|
||||
expect(result, isFalse);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": "1.1.18",
|
||||
"version": "1.1.19",
|
||||
"updateUrl": "https://app.em2events.fr",
|
||||
"forceUpdate": true,
|
||||
"releaseNotes": "Fix BUG : Ajout equipement à un evenement existant, boutons de modification de statut d'un evenement ne fonctionnaient pas. Refonte legere de la page calendrier.",
|
||||
"timestamp": "2026-03-12T20:11:54.548Z"
|
||||
"releaseNotes": "Fix BUG : Problème de cache avec les équipements non affichés dans le dialog de sélection d'équipement. Amélioration de la gestion du cache pour éviter les problèmes d'affichage.",
|
||||
"timestamp": "2026-03-24T11:14:01.828Z"
|
||||
}
|
||||
Reference in New Issue
Block a user