V1 calendrier
This commit is contained in:
		
							
								
								
									
										386
									
								
								em2rp/lib/controllers/event_form_controller.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										386
									
								
								em2rp/lib/controllers/event_form_controller.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,386 @@ | ||||
| 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<EventType> _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<EventType> 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 EventType? oldEventType = oldEventTypeIndex != -1 ? _eventTypes[oldEventTypeIndex] : null; | ||||
|  | ||||
|     _selectedEventTypeId = newTypeId; | ||||
|  | ||||
|     if (newTypeId != null) { | ||||
|       final selectedType = _eventTypes.firstWhere((et) => et.id == newTypeId); | ||||
|       final defaultPrice = selectedType.defaultPrice; | ||||
|       final currentPrice = double.tryParse(basePriceController.text.replaceAll(',', '.')); | ||||
|       final oldDefaultPrice = oldEventType?.defaultPrice; | ||||
|  | ||||
|       if (basePriceController.text.isEmpty || | ||||
|           (currentPrice != null && oldDefaultPrice != null && currentPrice == oldDefaultPrice)) { | ||||
|         basePriceController.text = defaultPrice.toStringAsFixed(2); | ||||
|       } | ||||
|  | ||||
|       final before = _selectedOptions.length; | ||||
|       _selectedOptions.removeWhere((opt) { | ||||
|         final types = opt['compatibleTypes'] as List<dynamic>?; | ||||
|         if (types == null) return true; | ||||
|         return !types.contains(selectedType.name); | ||||
|       }); | ||||
|  | ||||
|       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(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   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(); | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 ElPoyo
					ElPoyo