424 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			424 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| import 'package:flutter/material.dart';
 | |
| import 'package:cloud_firestore/cloud_firestore.dart';
 | |
| import 'package:file_picker/file_picker.dart';
 | |
| import 'package:em2rp/models/event_model.dart';
 | |
| import 'package:em2rp/models/event_type_model.dart';
 | |
| import 'package:em2rp/models/user_model.dart';
 | |
| import 'package:em2rp/services/event_form_service.dart';
 | |
| import 'package:provider/provider.dart';
 | |
| import 'package:em2rp/providers/event_provider.dart';
 | |
| import 'package:em2rp/providers/local_user_provider.dart';
 | |
| 
 | |
| class EventFormController extends ChangeNotifier {
 | |
|   // Controllers
 | |
|   final TextEditingController nameController = TextEditingController();
 | |
|   final TextEditingController descriptionController = TextEditingController();
 | |
|   final TextEditingController basePriceController = TextEditingController();
 | |
|   final TextEditingController installationController = TextEditingController();
 | |
|   final TextEditingController disassemblyController = TextEditingController();
 | |
|   final TextEditingController addressController = TextEditingController();
 | |
| 
 | |
|   // State variables
 | |
|   DateTime? _startDateTime;
 | |
|   DateTime? _endDateTime;
 | |
|   bool _isLoading = false;
 | |
|   String? _error;
 | |
|   String? _success;
 | |
|   String? _selectedEventTypeId;
 | |
|   List<EventTypeModel> _eventTypes = [];
 | |
|   bool _isLoadingEventTypes = true;
 | |
|   List<String> _selectedUserIds = [];
 | |
|   List<UserModel> _allUsers = [];
 | |
|   bool _isLoadingUsers = true;
 | |
|   List<Map<String, String>> _uploadedFiles = [];
 | |
|   List<Map<String, dynamic>> _selectedOptions = [];
 | |
|   bool _formChanged = false;
 | |
|   EventStatus _selectedStatus = EventStatus.waitingForApproval;
 | |
| 
 | |
|   // Getters
 | |
|   DateTime? get startDateTime => _startDateTime;
 | |
|   DateTime? get endDateTime => _endDateTime;
 | |
|   bool get isLoading => _isLoading;
 | |
|   String? get error => _error;
 | |
|   String? get success => _success;
 | |
|   String? get selectedEventTypeId => _selectedEventTypeId;
 | |
|   List<EventTypeModel> get eventTypes => _eventTypes;
 | |
|   bool get isLoadingEventTypes => _isLoadingEventTypes;
 | |
|   List<String> get selectedUserIds => _selectedUserIds;
 | |
|   List<UserModel> get allUsers => _allUsers;
 | |
|   bool get isLoadingUsers => _isLoadingUsers;
 | |
|   List<Map<String, String>> get uploadedFiles => _uploadedFiles;
 | |
|   List<Map<String, dynamic>> get selectedOptions => _selectedOptions;
 | |
|   bool get formChanged => _formChanged;
 | |
|   EventStatus get selectedStatus => _selectedStatus;
 | |
| 
 | |
|   EventFormController() {
 | |
|     _setupListeners();
 | |
|   }
 | |
| 
 | |
|   void _setupListeners() {
 | |
|     nameController.addListener(_onAnyFieldChanged);
 | |
|     basePriceController.addListener(_onAnyFieldChanged);
 | |
|     installationController.addListener(_onAnyFieldChanged);
 | |
|     disassemblyController.addListener(_onAnyFieldChanged);
 | |
|     addressController.addListener(_onAnyFieldChanged);
 | |
|     descriptionController.addListener(_onAnyFieldChanged);
 | |
|   }
 | |
| 
 | |
|   void _onAnyFieldChanged() {
 | |
|     if (!_formChanged) {
 | |
|       _formChanged = true;
 | |
|       notifyListeners();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Future<void> initialize([EventModel? existingEvent]) async {
 | |
|     await Future.wait([
 | |
|       _fetchUsers(),
 | |
|       _fetchEventTypes(),
 | |
|     ]);
 | |
| 
 | |
|     if (existingEvent != null) {
 | |
|       _populateFromEvent(existingEvent);
 | |
|     } else {
 | |
|       _selectedStatus = EventStatus.waitingForApproval;
 | |
|     }
 | |
|     notifyListeners();
 | |
|   }
 | |
| 
 | |
|   void _populateFromEvent(EventModel event) {
 | |
|     nameController.text = event.name;
 | |
|     descriptionController.text = event.description;
 | |
|     basePriceController.text = event.basePrice.toStringAsFixed(2);
 | |
|     installationController.text = event.installationTime.toString();
 | |
|     disassemblyController.text = event.disassemblyTime.toString();
 | |
|     addressController.text = event.address;
 | |
|     _startDateTime = event.startDateTime;
 | |
|     _endDateTime = event.endDateTime;
 | |
|     _selectedEventTypeId = event.eventTypeId.isNotEmpty ? event.eventTypeId : null;
 | |
|     _selectedUserIds = event.workforce.map((ref) => ref.id).toList();
 | |
|     _uploadedFiles = List<Map<String, String>>.from(event.documents);
 | |
|     _selectedOptions = List<Map<String, dynamic>>.from(event.options);
 | |
|     _selectedStatus = event.status;
 | |
|   }
 | |
| 
 | |
|   Future<void> _fetchUsers() async {
 | |
|     try {
 | |
|       _allUsers = await EventFormService.fetchUsers();
 | |
|       _isLoadingUsers = false;
 | |
|     } catch (e) {
 | |
|       _error = e.toString();
 | |
|       _isLoadingUsers = false;
 | |
|     }
 | |
|     notifyListeners();
 | |
|   }
 | |
| 
 | |
|   Future<void> _fetchEventTypes() async {
 | |
|     try {
 | |
|       _eventTypes = await EventFormService.fetchEventTypes();
 | |
|       _isLoadingEventTypes = false;
 | |
|     } catch (e) {
 | |
|       _error = e.toString();
 | |
|       _isLoadingEventTypes = false;
 | |
|     }
 | |
|     notifyListeners();
 | |
|   }
 | |
| 
 | |
|   void setStartDateTime(DateTime? dateTime) {
 | |
|     _startDateTime = dateTime;
 | |
|     if (_endDateTime != null &&
 | |
|         dateTime != null &&
 | |
|         (_endDateTime!.isBefore(dateTime) || _endDateTime!.isAtSameMomentAs(dateTime))) {
 | |
|       _endDateTime = null;
 | |
|     }
 | |
|     _onAnyFieldChanged();
 | |
|     notifyListeners();
 | |
|   }
 | |
| 
 | |
|   void setEndDateTime(DateTime? dateTime) {
 | |
|     _endDateTime = dateTime;
 | |
|     _onAnyFieldChanged();
 | |
|     notifyListeners();
 | |
|   }
 | |
| 
 | |
|   void onEventTypeChanged(String? newTypeId, BuildContext context) {
 | |
|     if (newTypeId == _selectedEventTypeId) return;
 | |
| 
 | |
|     final oldEventTypeIndex = _selectedEventTypeId != null
 | |
|         ? _eventTypes.indexWhere((et) => et.id == _selectedEventTypeId)
 | |
|         : -1;
 | |
|     final EventTypeModel? oldEventType = oldEventTypeIndex != -1 ? _eventTypes[oldEventTypeIndex] : null;
 | |
| 
 | |
|     _selectedEventTypeId = newTypeId;
 | |
| 
 | |
|     if (newTypeId != null) {
 | |
|       final selectedType = _eventTypes.firstWhere((et) => et.id == newTypeId);
 | |
| 
 | |
|       // Utiliser le prix par défaut du type d'événement
 | |
|       final defaultPrice = selectedType.defaultPrice;
 | |
|       final currentPrice = double.tryParse(basePriceController.text.replaceAll(',', '.'));
 | |
|       final oldDefaultPrice = oldEventType?.defaultPrice;
 | |
| 
 | |
|       // Mettre à jour le prix si le champ est vide ou si c'était l'ancien prix par défaut
 | |
|       if (basePriceController.text.isEmpty ||
 | |
|           (currentPrice != null && oldDefaultPrice != null && currentPrice == oldDefaultPrice)) {
 | |
|         basePriceController.text = defaultPrice.toStringAsFixed(2);
 | |
|       }
 | |
| 
 | |
|       // Filtrer les options qui ne sont plus compatibles avec le nouveau type
 | |
|       final before = _selectedOptions.length;
 | |
|       _selectedOptions.removeWhere((opt) {
 | |
|         // Vérifier si cette option est compatible avec le type d'événement sélectionné
 | |
|         final optionEventTypes = opt['eventTypes'] as List<dynamic>? ?? [];
 | |
|         return !optionEventTypes.contains(selectedType.id);
 | |
|       });
 | |
| 
 | |
|       if (_selectedOptions.length < before) {
 | |
|         ScaffoldMessenger.of(context).showSnackBar(
 | |
|           SnackBar(
 | |
|               content: Text(
 | |
|                   'Certaines options ont été retirées car non compatibles avec "${selectedType.name}".')),
 | |
|         );
 | |
|       }
 | |
|     } else {
 | |
|       _selectedOptions.clear();
 | |
|     }
 | |
| 
 | |
|     _onAnyFieldChanged();
 | |
|     notifyListeners();
 | |
|   }
 | |
| 
 | |
|   void setSelectedUserIds(List<String> userIds) {
 | |
|     _selectedUserIds = userIds;
 | |
|     _onAnyFieldChanged();
 | |
|     notifyListeners();
 | |
|   }
 | |
| 
 | |
|   void setUploadedFiles(List<Map<String, String>> files) {
 | |
|     _uploadedFiles = files;
 | |
|     _onAnyFieldChanged();
 | |
|     notifyListeners();
 | |
|   }
 | |
| 
 | |
|   void setSelectedOptions(List<Map<String, dynamic>> options) {
 | |
|     _selectedOptions = options;
 | |
|     _onAnyFieldChanged();
 | |
|     notifyListeners();
 | |
|   }
 | |
| 
 | |
|   Future<void> pickAndUploadFiles() async {
 | |
|     final result = await FilePicker.platform.pickFiles(allowMultiple: true, withData: true);
 | |
|     if (result != null && result.files.isNotEmpty) {
 | |
|       _isLoading = true;
 | |
|       _error = null;
 | |
|       notifyListeners();
 | |
| 
 | |
|       try {
 | |
|         final files = await EventFormService.uploadFiles(result.files);
 | |
|         _uploadedFiles.addAll(files);
 | |
|         _onAnyFieldChanged();
 | |
|       } catch (e) {
 | |
|         _error = 'Erreur lors de l\'upload : $e';
 | |
|       } finally {
 | |
|         _isLoading = false;
 | |
|         notifyListeners();
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   bool validateForm() {
 | |
|     return nameController.text.isNotEmpty &&
 | |
|         _startDateTime != null &&
 | |
|         _endDateTime != null &&
 | |
|         _selectedEventTypeId != null &&
 | |
|         addressController.text.isNotEmpty &&
 | |
|         (_endDateTime!.isAfter(_startDateTime!));
 | |
|   }
 | |
| 
 | |
|   Future<bool> submitForm(BuildContext context, {EventModel? existingEvent}) async {
 | |
|     if (!validateForm()) {
 | |
|       _error = "Veuillez remplir tous les champs obligatoires.";
 | |
|       notifyListeners();
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     _isLoading = true;
 | |
|     _error = null;
 | |
|     _success = null;
 | |
|     notifyListeners();
 | |
| 
 | |
|     try {
 | |
|       final eventTypeRef = _selectedEventTypeId != null
 | |
|           ? FirebaseFirestore.instance.collection('eventTypes').doc(_selectedEventTypeId)
 | |
|           : null;
 | |
| 
 | |
|       if (existingEvent != null) {
 | |
|         // Mode édition
 | |
|         // Gérer les nouveaux fichiers uploadés s'il y en a
 | |
|         List<Map<String, String>> finalDocuments = List<Map<String, String>>.from(_uploadedFiles);
 | |
| 
 | |
|         // Identifier les nouveaux fichiers (ceux qui ont une URL temp)
 | |
|         final newFiles = _uploadedFiles.where((file) =>
 | |
|           file['url']?.contains('events/temp/') ?? false).toList();
 | |
| 
 | |
|         if (newFiles.isNotEmpty) {
 | |
|           // Déplacer les nouveaux fichiers vers le dossier de l'événement
 | |
|           final movedFiles = await EventFormService.moveFilesToEvent(newFiles, existingEvent.id);
 | |
| 
 | |
|           // Remplacer les URLs temporaires par les nouvelles URLs
 | |
|           for (int i = 0; i < finalDocuments.length; i++) {
 | |
|             final tempFile = finalDocuments[i];
 | |
|             final movedFile = movedFiles.firstWhere(
 | |
|               (moved) => moved['name'] == tempFile['name'],
 | |
|               orElse: () => tempFile,
 | |
|             );
 | |
|             finalDocuments[i] = movedFile;
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         final updatedEvent = EventModel(
 | |
|           id: existingEvent.id,
 | |
|           name: nameController.text.trim(),
 | |
|           description: descriptionController.text.trim(),
 | |
|           startDateTime: _startDateTime!,
 | |
|           endDateTime: _endDateTime!,
 | |
|           basePrice: double.tryParse(basePriceController.text.replaceAll(',', '.')) ?? 0.0,
 | |
|           installationTime: int.tryParse(installationController.text) ?? 0,
 | |
|           disassemblyTime: int.tryParse(disassemblyController.text) ?? 0,
 | |
|           eventTypeId: _selectedEventTypeId!,
 | |
|           eventTypeRef: eventTypeRef,
 | |
|           customerId: existingEvent.customerId,
 | |
|           address: addressController.text.trim(),
 | |
|           workforce: _selectedUserIds
 | |
|               .map((id) => FirebaseFirestore.instance.collection('users').doc(id))
 | |
|               .toList(),
 | |
|           latitude: existingEvent.latitude,
 | |
|           longitude: existingEvent.longitude,
 | |
|           documents: finalDocuments,
 | |
|           options: _selectedOptions,
 | |
|           status: _selectedStatus,
 | |
|         );
 | |
| 
 | |
|         await EventFormService.updateEvent(updatedEvent);
 | |
| 
 | |
|         // Recharger les événements après modification
 | |
|         final localUserProvider = Provider.of<LocalUserProvider>(context, listen: false);
 | |
|         final eventProvider = Provider.of<EventProvider>(context, listen: false);
 | |
|         final userId = localUserProvider.uid;
 | |
|         final canViewAllEvents = localUserProvider.hasPermission('view_all_events');
 | |
| 
 | |
|         if (userId != null) {
 | |
|           await eventProvider.loadUserEvents(userId, canViewAllEvents: canViewAllEvents);
 | |
|         }
 | |
| 
 | |
|         _success = "Événement modifié avec succès !";
 | |
|       } else {
 | |
|         // Mode création
 | |
|         final newEvent = EventModel(
 | |
|           id: '',
 | |
|           name: nameController.text.trim(),
 | |
|           description: descriptionController.text.trim(),
 | |
|           startDateTime: _startDateTime!,
 | |
|           endDateTime: _endDateTime!,
 | |
|           basePrice: double.tryParse(basePriceController.text.replaceAll(',', '.')) ?? 0.0,
 | |
|           installationTime: int.tryParse(installationController.text) ?? 0,
 | |
|           disassemblyTime: int.tryParse(disassemblyController.text) ?? 0,
 | |
|           eventTypeId: _selectedEventTypeId!,
 | |
|           eventTypeRef: eventTypeRef,
 | |
|           customerId: '',
 | |
|           address: addressController.text.trim(),
 | |
|           workforce: _selectedUserIds
 | |
|               .map((id) => FirebaseFirestore.instance.collection('users').doc(id))
 | |
|               .toList(),
 | |
|           latitude: 0.0,
 | |
|           longitude: 0.0,
 | |
|           documents: _uploadedFiles,
 | |
|           options: _selectedOptions,
 | |
|           status: _selectedStatus,
 | |
|         );
 | |
| 
 | |
|         final eventId = await EventFormService.createEvent(newEvent);
 | |
|         final newFiles = await EventFormService.moveFilesToEvent(_uploadedFiles, eventId);
 | |
|         await EventFormService.updateEventDocuments(eventId, newFiles);
 | |
| 
 | |
|         // Reload events
 | |
|         final localUserProvider = Provider.of<LocalUserProvider>(context, listen: false);
 | |
|         final eventProvider = Provider.of<EventProvider>(context, listen: false);
 | |
|         final userId = localUserProvider.uid;
 | |
|         final canViewAllEvents = localUserProvider.hasPermission('view_all_events');
 | |
| 
 | |
|         if (userId != null) {
 | |
|           await eventProvider.loadUserEvents(userId, canViewAllEvents: canViewAllEvents);
 | |
|         }
 | |
| 
 | |
|         _success = "Événement créé avec succès !";
 | |
|       }
 | |
| 
 | |
|       _formChanged = false;
 | |
|       notifyListeners();
 | |
|       return true;
 | |
|     } catch (e) {
 | |
|       _error = "Erreur lors de la sauvegarde : $e";
 | |
|       notifyListeners();
 | |
|       return false;
 | |
|     } finally {
 | |
|       _isLoading = false;
 | |
|       notifyListeners();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Future<bool> deleteEvent(BuildContext context, String eventId) async {
 | |
|     _isLoading = true;
 | |
|     _error = null;
 | |
|     _success = null;
 | |
|     notifyListeners();
 | |
| 
 | |
|     try {
 | |
|       // Supprimer l'événement de Firestore
 | |
|       await FirebaseFirestore.instance.collection('events').doc(eventId).delete();
 | |
| 
 | |
|       // Recharger la liste des événements
 | |
|       final localUserProvider = Provider.of<LocalUserProvider>(context, listen: false);
 | |
|       final eventProvider = Provider.of<EventProvider>(context, listen: false);
 | |
|       final userId = localUserProvider.uid;
 | |
|       final canViewAllEvents = localUserProvider.hasPermission('view_all_events');
 | |
| 
 | |
|       if (userId != null) {
 | |
|         await eventProvider.loadUserEvents(userId, canViewAllEvents: canViewAllEvents);
 | |
|       }
 | |
| 
 | |
|       _success = "Événement supprimé avec succès !";
 | |
|       notifyListeners();
 | |
|       return true;
 | |
|     } catch (e) {
 | |
|       _error = "Erreur lors de la suppression : $e";
 | |
|       notifyListeners();
 | |
|       return false;
 | |
|     } finally {
 | |
|       _isLoading = false;
 | |
|       notifyListeners();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void clearError() {
 | |
|     _error = null;
 | |
|     notifyListeners();
 | |
|   }
 | |
| 
 | |
|   void clearSuccess() {
 | |
|     _success = null;
 | |
|     notifyListeners();
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   void dispose() {
 | |
|     nameController.dispose();
 | |
|     descriptionController.dispose();
 | |
|     basePriceController.dispose();
 | |
|     installationController.dispose();
 | |
|     disassemblyController.dispose();
 | |
|     addressController.dispose();
 | |
|     super.dispose();
 | |
|   }
 | |
| }
 | 
