330 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			330 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| import 'package:flutter/material.dart';
 | |
| import 'package:em2rp/models/event_model.dart';
 | |
| import 'package:intl/intl.dart';
 | |
| import 'package:provider/provider.dart';
 | |
| import 'package:em2rp/providers/local_user_provider.dart';
 | |
| import 'package:em2rp/providers/event_provider.dart';
 | |
| import 'package:em2rp/views/widgets/calendar_widgets/event_details_components/event_details_navigation.dart';
 | |
| import 'package:em2rp/views/widgets/calendar_widgets/event_details_components/event_details_header.dart';
 | |
| import 'package:em2rp/views/widgets/calendar_widgets/event_details_components/event_status_button.dart';
 | |
| import 'package:em2rp/views/widgets/calendar_widgets/event_details_components/event_details_info.dart';
 | |
| import 'package:em2rp/views/widgets/calendar_widgets/event_details_components/event_details_description.dart';
 | |
| import 'package:em2rp/views/widgets/calendar_widgets/event_details_components/event_details_documents.dart';
 | |
| import 'package:em2rp/views/widgets/calendar_widgets/event_details_components/event_details_equipe.dart';
 | |
| 
 | |
| class EventDetails extends StatelessWidget {
 | |
|   final EventModel event;
 | |
|   final DateTime? selectedDate;
 | |
|   final List<EventModel> events;
 | |
|   final void Function(EventModel, DateTime) onSelectEvent;
 | |
| 
 | |
|   const EventDetails({
 | |
|     super.key,
 | |
|     required this.event,
 | |
|     required this.selectedDate,
 | |
|     required this.events,
 | |
|     required this.onSelectEvent,
 | |
|   });
 | |
| 
 | |
|   @override
 | |
|   Widget build(BuildContext context) {
 | |
|     // Trie les événements par date de début
 | |
|     final sortedEvents = List<EventModel>.from(events)
 | |
|       ..sort((a, b) => a.startDateTime.compareTo(b.startDateTime));
 | |
|     final currentIndex = sortedEvents.indexWhere((e) => e.id == event.id);
 | |
|     final localUserProvider = Provider.of<LocalUserProvider>(context);
 | |
|     final canViewPrices = localUserProvider.hasPermission('view_event_prices');
 | |
| 
 | |
|     return Card(
 | |
|       margin: const EdgeInsets.all(16),
 | |
|       child: Padding(
 | |
|         padding: const EdgeInsets.all(16),
 | |
|         child: Column(
 | |
|           crossAxisAlignment: CrossAxisAlignment.start,
 | |
|           children: [
 | |
|             EventDetailsNavigation(
 | |
|               sortedEvents: sortedEvents,
 | |
|               currentIndex: currentIndex,
 | |
|               selectedDate: selectedDate,
 | |
|               onSelectEvent: onSelectEvent,
 | |
|             ),
 | |
|             const SizedBox(height: 16),
 | |
|             EventDetailsHeader(event: event),
 | |
|             if (Provider.of<LocalUserProvider>(context, listen: false)
 | |
|                 .hasPermission('change_event_status'))
 | |
|               Padding(
 | |
|                 padding: const EdgeInsets.symmetric(vertical: 12.0),
 | |
|                 child: EventStatusButton(
 | |
|                   event: event,
 | |
|                   selectedDate: selectedDate,
 | |
|                   onSelectEvent: onSelectEvent,
 | |
|                 ),
 | |
|               ),
 | |
|             const SizedBox(height: 16),
 | |
|             Expanded(
 | |
|               child: SingleChildScrollView(
 | |
|                 child: Column(
 | |
|                   crossAxisAlignment: CrossAxisAlignment.start,
 | |
|                   children: [
 | |
|                     EventDetailsInfo(
 | |
|                       event: event,
 | |
|                       canViewPrices: canViewPrices,
 | |
|                     ),
 | |
|                     const SizedBox(height: 16),
 | |
|                     EventDetailsDescription(event: event),
 | |
|                     EventDetailsDocuments(documents: event.documents),
 | |
|                     const SizedBox(height: 16),
 | |
|                     EventDetailsEquipe(workforce: event.workforce),
 | |
|                   ],
 | |
|                 ),
 | |
|               ),
 | |
|             ),
 | |
|           ],
 | |
|         ),
 | |
|       ),
 | |
|     );
 | |
|   }
 | |
| }
 | |
| 
 | |
| // La classe EventAddDialog reste inchangée car elle n'est pas liée aux détails d'événement
 | |
| class EventAddDialog extends StatefulWidget {
 | |
|   const EventAddDialog({super.key});
 | |
| 
 | |
|   @override
 | |
|   State<EventAddDialog> createState() => _EventAddDialogState();
 | |
| }
 | |
| 
 | |
| class _EventAddDialogState extends State<EventAddDialog> {
 | |
|   final _formKey = GlobalKey<FormState>();
 | |
|   final TextEditingController _nameController = TextEditingController();
 | |
|   final TextEditingController _descriptionController = TextEditingController();
 | |
|   final TextEditingController _priceController = TextEditingController();
 | |
|   final TextEditingController _installationController = TextEditingController();
 | |
|   final TextEditingController _disassemblyController = TextEditingController();
 | |
|   final TextEditingController _latitudeController = TextEditingController();
 | |
|   final TextEditingController _longitudeController = TextEditingController();
 | |
|   final TextEditingController _addressController = TextEditingController();
 | |
|   DateTime? _startDateTime;
 | |
|   DateTime? _endDateTime;
 | |
|   bool _isLoading = false;
 | |
|   String? _error;
 | |
|   String? _success;
 | |
| 
 | |
|   @override
 | |
|   void dispose() {
 | |
|     _nameController.dispose();
 | |
|     _descriptionController.dispose();
 | |
|     _priceController.dispose();
 | |
|     _installationController.dispose();
 | |
|     _disassemblyController.dispose();
 | |
|     _latitudeController.dispose();
 | |
|     _longitudeController.dispose();
 | |
|     _addressController.dispose();
 | |
|     super.dispose();
 | |
|   }
 | |
| 
 | |
|   Future<void> _submit() async {
 | |
|     if (!_formKey.currentState!.validate() ||
 | |
|         _startDateTime == null ||
 | |
|         _endDateTime == null) {
 | |
|       return;
 | |
|     }
 | |
|     setState(() {
 | |
|       _isLoading = true;
 | |
|       _error = null;
 | |
|       _success = null;
 | |
|     });
 | |
|     try {
 | |
|       final eventProvider = Provider.of<EventProvider>(context, listen: false);
 | |
|       final newEvent = EventModel(
 | |
|         id: '',
 | |
|         name: _nameController.text.trim(),
 | |
|         description: _descriptionController.text.trim(),
 | |
|         startDateTime: _startDateTime!,
 | |
|         endDateTime: _endDateTime!,
 | |
|         basePrice: double.tryParse(_priceController.text) ?? 0.0,
 | |
|         installationTime: int.tryParse(_installationController.text) ?? 0,
 | |
|         disassemblyTime: int.tryParse(_disassemblyController.text) ?? 0,
 | |
|         eventTypeId: '', // à adapter si tu veux gérer les types
 | |
|         customerId: '', // à adapter si tu veux gérer les clients
 | |
|         address: _addressController.text.trim(),
 | |
|         latitude: double.tryParse(_latitudeController.text) ?? 0.0,
 | |
|         longitude: double.tryParse(_longitudeController.text) ?? 0.0,
 | |
|         workforce: [],
 | |
|         documents: [],
 | |
|       );
 | |
|       await eventProvider.addEvent(newEvent);
 | |
|       setState(() {
 | |
|         _success = "Événement créé avec succès !";
 | |
|       });
 | |
|       Navigator.of(context).pop();
 | |
|     } catch (e) {
 | |
|       setState(() {
 | |
|         _error = "Erreur lors de la création : $e";
 | |
|       });
 | |
|     } finally {
 | |
|       setState(() {
 | |
|         _isLoading = false;
 | |
|       });
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   Widget build(BuildContext context) {
 | |
|     return AlertDialog(
 | |
|       title: const Text('Créer un événement'),
 | |
|       content: SingleChildScrollView(
 | |
|         child: Form(
 | |
|           key: _formKey,
 | |
|           child: Column(
 | |
|             mainAxisSize: MainAxisSize.min,
 | |
|             children: [
 | |
|               TextFormField(
 | |
|                 controller: _nameController,
 | |
|                 decoration: const InputDecoration(labelText: 'Nom'),
 | |
|                 validator: (v) =>
 | |
|                     v == null || v.isEmpty ? 'Champ requis' : null,
 | |
|               ),
 | |
|               TextFormField(
 | |
|                 controller: _descriptionController,
 | |
|                 decoration: const InputDecoration(labelText: 'Description'),
 | |
|                 maxLines: 2,
 | |
|               ),
 | |
|               TextFormField(
 | |
|                 controller: _priceController,
 | |
|                 decoration: const InputDecoration(labelText: 'Prix (€)'),
 | |
|                 keyboardType: TextInputType.number,
 | |
|               ),
 | |
|               TextFormField(
 | |
|                 controller: _installationController,
 | |
|                 decoration:
 | |
|                     const InputDecoration(labelText: 'Installation (h)'),
 | |
|                 keyboardType: TextInputType.number,
 | |
|               ),
 | |
|               TextFormField(
 | |
|                 controller: _disassemblyController,
 | |
|                 decoration: const InputDecoration(labelText: 'Démontage (h)'),
 | |
|                 keyboardType: TextInputType.number,
 | |
|               ),
 | |
|               TextFormField(
 | |
|                 controller: _latitudeController,
 | |
|                 decoration: const InputDecoration(labelText: 'Latitude'),
 | |
|                 keyboardType: TextInputType.number,
 | |
|               ),
 | |
|               TextFormField(
 | |
|                 controller: _longitudeController,
 | |
|                 decoration: const InputDecoration(labelText: 'Longitude'),
 | |
|                 keyboardType: TextInputType.number,
 | |
|               ),
 | |
|               TextFormField(
 | |
|                 controller: _addressController,
 | |
|                 decoration: const InputDecoration(labelText: 'Adresse'),
 | |
|               ),
 | |
|               const SizedBox(height: 8),
 | |
|               Row(
 | |
|                 children: [
 | |
|                   Expanded(
 | |
|                     child: OutlinedButton(
 | |
|                       onPressed: () async {
 | |
|                         final picked = await showDatePicker(
 | |
|                           context: context,
 | |
|                           initialDate: DateTime.now(),
 | |
|                           firstDate: DateTime(2020),
 | |
|                           lastDate: DateTime(2030),
 | |
|                         );
 | |
|                         if (picked != null) {
 | |
|                           final time = await showTimePicker(
 | |
|                             context: context,
 | |
|                             initialTime: TimeOfDay.now(),
 | |
|                           );
 | |
|                           if (time != null) {
 | |
|                             setState(() {
 | |
|                               _startDateTime = DateTime(
 | |
|                                 picked.year,
 | |
|                                 picked.month,
 | |
|                                 picked.day,
 | |
|                                 time.hour,
 | |
|                                 time.minute,
 | |
|                               );
 | |
|                             });
 | |
|                           }
 | |
|                         }
 | |
|                       },
 | |
|                       child: Text(_startDateTime == null
 | |
|                           ? 'Début'
 | |
|                           : DateFormat('dd/MM/yyyy HH:mm')
 | |
|                               .format(_startDateTime!)),
 | |
|                     ),
 | |
|                   ),
 | |
|                   const SizedBox(width: 8),
 | |
|                   Expanded(
 | |
|                     child: OutlinedButton(
 | |
|                       onPressed: () async {
 | |
|                         final picked = await showDatePicker(
 | |
|                           context: context,
 | |
|                           initialDate: _startDateTime ?? DateTime.now(),
 | |
|                           firstDate: DateTime(2020),
 | |
|                           lastDate: DateTime(2030),
 | |
|                         );
 | |
|                         if (picked != null) {
 | |
|                           final time = await showTimePicker(
 | |
|                             context: context,
 | |
|                             initialTime: TimeOfDay.now(),
 | |
|                           );
 | |
|                           if (time != null) {
 | |
|                             setState(() {
 | |
|                               _endDateTime = DateTime(
 | |
|                                 picked.year,
 | |
|                                 picked.month,
 | |
|                                 picked.day,
 | |
|                                 time.hour,
 | |
|                                 time.minute,
 | |
|                               );
 | |
|                             });
 | |
|                           }
 | |
|                         }
 | |
|                       },
 | |
|                       child: Text(_endDateTime == null
 | |
|                           ? 'Fin'
 | |
|                           : DateFormat('dd/MM/yyyy HH:mm')
 | |
|                               .format(_endDateTime!)),
 | |
|                     ),
 | |
|                   ),
 | |
|                 ],
 | |
|               ),
 | |
|               if (_error != null)
 | |
|                 Padding(
 | |
|                   padding: const EdgeInsets.only(top: 8.0),
 | |
|                   child:
 | |
|                       Text(_error!, style: const TextStyle(color: Colors.red)),
 | |
|                 ),
 | |
|               if (_success != null)
 | |
|                 Padding(
 | |
|                   padding: const EdgeInsets.only(top: 8.0),
 | |
|                   child: Text(_success!,
 | |
|                       style: const TextStyle(color: Colors.green)),
 | |
|                 ),
 | |
|             ],
 | |
|           ),
 | |
|         ),
 | |
|       ),
 | |
|       actions: [
 | |
|         TextButton(
 | |
|           onPressed: _isLoading ? null : () => Navigator.of(context).pop(),
 | |
|           child: const Text('Annuler'),
 | |
|         ),
 | |
|         ElevatedButton(
 | |
|           onPressed: _isLoading ? null : _submit,
 | |
|           child: _isLoading
 | |
|               ? const SizedBox(
 | |
|                   width: 20,
 | |
|                   height: 20,
 | |
|                   child: CircularProgressIndicator(strokeWidth: 2),
 | |
|                 )
 | |
|               : const Text('Créer'),
 | |
|         ),
 | |
|       ],
 | |
|     );
 | |
|   }
 | |
| }
 | 
