Files
EM2_ERP/em2rp/lib/views/pages/event_add_page.dart

411 lines
17 KiB
Dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:em2rp/providers/event_provider.dart';
import 'package:latlong2/latlong.dart';
import 'package:em2rp/models/event_model.dart';
import 'package:intl/intl.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:em2rp/views/widgets/inputs/int_stepper_field.dart';
class EventAddPage extends StatefulWidget {
const EventAddPage({super.key});
@override
State<EventAddPage> createState() => _EventAddPageState();
}
class _EventAddPageState extends State<EventAddPage> {
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();
DateTime? _startDateTime;
DateTime? _endDateTime;
bool _isLoading = false;
String? _error;
String? _success;
String? _selectedEventType;
final List<String> _eventTypes = ['Bal', 'Mariage', 'Anniversaire'];
int _descriptionMaxLines = 3;
@override
void initState() {
super.initState();
_descriptionController.addListener(_handleDescriptionChange);
}
void _handleDescriptionChange() {
final lines = '\n'.allMatches(_descriptionController.text).length + 1;
setState(() {
_descriptionMaxLines = lines.clamp(3, 6);
});
}
@override
void dispose() {
_nameController.dispose();
_descriptionController.dispose();
_priceController.dispose();
_installationController.dispose();
_disassemblyController.dispose();
_latitudeController.dispose();
_longitudeController.dispose();
super.dispose();
}
Future<void> _submit() async {
if (!_formKey.currentState!.validate() ||
_startDateTime == null ||
_endDateTime == null ||
_selectedEventType == 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!,
price: double.tryParse(_priceController.text) ?? 0.0,
installationTime: int.tryParse(_installationController.text) ?? 0,
disassemblyTime: int.tryParse(_disassemblyController.text) ?? 0,
eventTypeId: _selectedEventType!,
customerId: '', // à adapter si tu veux gérer les clients
address: LatLng(
double.tryParse(_latitudeController.text) ?? 0.0,
double.tryParse(_longitudeController.text) ?? 0.0,
),
workforce: [],
);
await eventProvider.addEvent(newEvent);
setState(() {
_success = "Événement créé avec succès !";
});
if (context.mounted) Navigator.of(context).pop();
} catch (e) {
setState(() {
_error = "Erreur lors de la création : $e";
});
} finally {
setState(() {
_isLoading = false;
});
}
}
Widget _buildSectionTitle(String title) {
return Padding(
padding: const EdgeInsets.only(top: 16.0, bottom: 8.0),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
title,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
),
);
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Scaffold(
appBar: AppBar(
title: const Text('Créer un événement'),
),
body: Center(
child: SingleChildScrollView(
child: Card(
elevation: 6,
margin: const EdgeInsets.all(24),
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(18)),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 32),
child: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.only(top: 0.0, bottom: 4.0),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
'Informations principales',
style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.bold),
),
),
),
TextFormField(
controller: _nameController,
decoration: const InputDecoration(
labelText: 'Nom de l\'événement',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.event),
),
validator: (v) =>
v == null || v.isEmpty ? 'Champ requis' : null,
),
const SizedBox(height: 16),
DropdownButtonFormField<String>(
value: _selectedEventType,
items: _eventTypes
.map((type) => DropdownMenuItem<String>(
value: type,
child: Text(type),
))
.toList(),
onChanged: (val) =>
setState(() => _selectedEventType = val),
decoration: const InputDecoration(
labelText: 'Type d\'événement',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.category),
),
validator: (v) =>
v == null ? 'Sélectionnez un type' : null,
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: GestureDetector(
onTap: () async {
final picked = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2020),
lastDate: DateTime(2099),
);
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: AbsorbPointer(
child: TextFormField(
readOnly: true,
decoration: InputDecoration(
labelText: 'Début',
border: const OutlineInputBorder(),
prefixIcon: const Icon(Icons.calendar_today),
suffixIcon: const Icon(Icons.edit_calendar),
),
controller: TextEditingController(
text: _startDateTime == null
? ''
: DateFormat('dd/MM/yyyy HH:mm')
.format(_startDateTime!),
),
validator: (v) => _startDateTime == null
? 'Champ requis'
: null,
),
),
),
),
const SizedBox(width: 16),
Expanded(
child: GestureDetector(
onTap: () async {
final picked = await showDatePicker(
context: context,
initialDate: _startDateTime ?? DateTime.now(),
firstDate: DateTime(2020),
lastDate: DateTime(2099),
);
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: AbsorbPointer(
child: TextFormField(
readOnly: true,
decoration: InputDecoration(
labelText: 'Fin',
border: const OutlineInputBorder(),
prefixIcon: const Icon(Icons.calendar_today),
suffixIcon: const Icon(Icons.edit_calendar),
),
controller: TextEditingController(
text: _endDateTime == null
? ''
: DateFormat('dd/MM/yyyy HH:mm')
.format(_endDateTime!),
),
validator: (v) => _endDateTime == null
? 'Champ requis'
: null,
),
),
),
),
],
),
const SizedBox(height: 16),
TextFormField(
controller: _priceController,
decoration: const InputDecoration(
labelText: 'Prix (€)',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.euro),
),
keyboardType: TextInputType.number,
),
_buildSectionTitle('Détails'),
AnimatedContainer(
duration: const Duration(milliseconds: 200),
constraints: BoxConstraints(
minHeight: 48,
maxHeight: 48.0 * 10,
),
child: TextFormField(
controller: _descriptionController,
minLines: 1,
maxLines: _descriptionMaxLines > 10
? 10
: _descriptionMaxLines,
decoration: const InputDecoration(
labelText: 'Description',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.description),
),
),
),
const SizedBox(height: 20),
Row(
children: [
Expanded(
child: IntStepperField(
label: 'Installation (h)',
controller: _installationController,
min: 0,
max: 24,
),
),
const SizedBox(width: 16),
Expanded(
child: IntStepperField(
label: 'Démontage (h)',
controller: _disassemblyController,
min: 0,
max: 24,
),
),
],
),
_buildSectionTitle('Localisation'),
Row(
children: [
Expanded(
child: TextFormField(
controller: _latitudeController,
decoration: const InputDecoration(
labelText: 'Latitude',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.location_on),
),
keyboardType: TextInputType.number,
),
),
const SizedBox(width: 16),
Expanded(
child: TextFormField(
controller: _longitudeController,
decoration: const InputDecoration(
labelText: 'Longitude',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.location_on),
),
keyboardType: TextInputType.number,
),
),
],
),
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)),
),
const SizedBox(height: 24),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: _isLoading
? null
: () => Navigator.of(context).pop(),
child: const Text('Annuler'),
),
const SizedBox(width: 8),
ElevatedButton.icon(
icon: const Icon(Icons.check),
onPressed: _isLoading ? null : _submit,
label: _isLoading
? const SizedBox(
width: 20,
height: 20,
child:
CircularProgressIndicator(strokeWidth: 2),
)
: const Text('Créer'),
),
],
),
],
),
),
),
),
),
),
);
}
}