feat: implement equipment and container loading rollback functionality with corresponding backend cloud functions
This commit is contained in:
+162
-6
@@ -2,7 +2,9 @@ import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:em2rp/models/event_model.dart';
|
||||
import 'package:em2rp/providers/event_provider.dart';
|
||||
import 'package:em2rp/providers/local_user_provider.dart';
|
||||
import 'package:em2rp/views/event_preparation_page.dart';
|
||||
import 'package:em2rp/services/api_service.dart';
|
||||
import 'package:em2rp/utils/colors.dart';
|
||||
|
||||
/// Boutons de préparation et retour d'événement
|
||||
@@ -52,16 +54,16 @@ class _EventPreparationButtonsState extends State<EventPreparationButtons> {
|
||||
IconData buttonIcon;
|
||||
bool isCompleted = false;
|
||||
|
||||
if (prep != PreparationStatus.completed) {
|
||||
if (prep != PreparationStatus.completed && prep != PreparationStatus.completedWithMissing) {
|
||||
buttonText = 'Préparation dépôt';
|
||||
buttonIcon = Icons.inventory_2;
|
||||
} else if (loading != LoadingStatus.completed) {
|
||||
} else if (loading != LoadingStatus.completed && loading != LoadingStatus.completedWithMissing) {
|
||||
buttonText = 'Chargement aller';
|
||||
buttonIcon = Icons.local_shipping;
|
||||
} else if (unloading != UnloadingStatus.completed) {
|
||||
} else if (unloading != UnloadingStatus.completed && unloading != UnloadingStatus.completedWithMissing) {
|
||||
buttonText = 'Chargement retour';
|
||||
buttonIcon = Icons.unarchive;
|
||||
} else if (returnStatus != ReturnStatus.completed) {
|
||||
} else if (returnStatus != ReturnStatus.completed && returnStatus != ReturnStatus.completedWithMissing) {
|
||||
buttonText = 'Retour dépôt';
|
||||
buttonIcon = Icons.assignment_return;
|
||||
} else {
|
||||
@@ -131,9 +133,9 @@ class _EventPreparationButtonsState extends State<EventPreparationButtons> {
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: Colors.green, width: 1),
|
||||
),
|
||||
child: Row(
|
||||
child: const Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: const [
|
||||
children: [
|
||||
Icon(Icons.check_circle, color: Colors.green, size: 20),
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
@@ -147,9 +149,163 @@ class _EventPreparationButtonsState extends State<EventPreparationButtons> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Bouton de retour en arrière si au moins une étape est commencée/validée
|
||||
if (prep != PreparationStatus.notStarted) ...[
|
||||
const SizedBox(height: 8),
|
||||
Center(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 400),
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () => _showRollbackDialog(context, event),
|
||||
icon: const Icon(Icons.undo, size: 18),
|
||||
label: const Text('Revenir à une étape précédente'),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: Colors.orange[800],
|
||||
side: BorderSide(color: Colors.orange[300]!),
|
||||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _showRollbackDialog(BuildContext context, EventModel event) async {
|
||||
final prep = event.preparationStatus ?? PreparationStatus.notStarted;
|
||||
final loading = event.loadingStatus ?? LoadingStatus.notStarted;
|
||||
final unloading = event.unloadingStatus ?? UnloadingStatus.notStarted;
|
||||
final returnStatus = event.returnStatus ?? ReturnStatus.notStarted;
|
||||
|
||||
final List<Map<String, String>> steps = [];
|
||||
|
||||
if (prep == PreparationStatus.completed || prep == PreparationStatus.completedWithMissing) {
|
||||
steps.add({'key': 'PREPARATION', 'label': 'Préparation dépôt'});
|
||||
}
|
||||
if (loading == LoadingStatus.completed || loading == LoadingStatus.completedWithMissing) {
|
||||
steps.add({'key': 'LOADING', 'label': 'Chargement aller'});
|
||||
}
|
||||
if (unloading == UnloadingStatus.completed || unloading == UnloadingStatus.completedWithMissing) {
|
||||
steps.add({'key': 'UNLOADING', 'label': 'Chargement retour'});
|
||||
}
|
||||
if (returnStatus == ReturnStatus.completed || returnStatus == ReturnStatus.completedWithMissing) {
|
||||
steps.add({'key': 'RETURN', 'label': 'Retour dépôt'});
|
||||
}
|
||||
|
||||
if (steps.isEmpty) return;
|
||||
|
||||
final String? selectedStep = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Row(
|
||||
children: [
|
||||
Icon(Icons.undo, color: Colors.orange),
|
||||
SizedBox(width: 8),
|
||||
Text('Revenir en arrière'),
|
||||
],
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('Sélectionnez l\'étape à laquelle vous souhaitez revenir :'),
|
||||
const SizedBox(height: 12),
|
||||
...steps.map((step) {
|
||||
return ListTile(
|
||||
leading: const Icon(Icons.arrow_back, color: AppColors.rouge),
|
||||
title: Text(step['label']!),
|
||||
onTap: () => Navigator.of(context).pop(step['key']),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('Annuler'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (selectedStep != null && context.mounted) {
|
||||
final confirm = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Confirmer le retour en arrière'),
|
||||
content: const Text(
|
||||
'Êtes-vous sûr de vouloir revenir à cette étape ?\n\n'
|
||||
'Toutes les validations des étapes ultérieures seront effacées, '
|
||||
'et si le retour était terminé, les stocks restaurés seront annulés.'
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
child: const Text('Annuler'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.rouge,
|
||||
),
|
||||
child: const Text('Confirmer'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (confirm == true && context.mounted) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext context) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
},
|
||||
);
|
||||
|
||||
try {
|
||||
final apiService = FirebaseFunctionsApiService();
|
||||
await apiService.call('rollbackEventStep', {
|
||||
'eventId': event.id,
|
||||
'targetStep': selectedStep,
|
||||
});
|
||||
|
||||
if (context.mounted) {
|
||||
final eventProvider = Provider.of<EventProvider>(context, listen: false);
|
||||
final userProvider = Provider.of<LocalUserProvider>(context, listen: false);
|
||||
if (userProvider.currentUser != null) {
|
||||
await eventProvider.refreshEvents(userProvider.currentUser!.uid);
|
||||
}
|
||||
|
||||
Navigator.of(context).pop(); // Fermer le loader
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Retour en arrière effectué avec succès'),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop(); // Fermer le loader
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Erreur : $e'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user