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