feat: export ICS

This commit is contained in:
ElPoyo
2025-12-20 15:56:57 +01:00
parent df9e24d3b3
commit fa1d6a4295
8 changed files with 466 additions and 8 deletions

View File

@@ -130,7 +130,9 @@ class _CalendarPageState extends State<CalendarPage> {
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const EventAddEditPage(),
builder: (context) => EventAddEditPage(
selectedDate: _selectedDay ?? DateTime.now(),
),
),
);
},

View File

@@ -11,7 +11,9 @@ import 'package:em2rp/views/widgets/inputs/option_selector_widget.dart';
class EventAddEditPage extends StatefulWidget {
final EventModel? event;
const EventAddEditPage({super.key, this.event});
final DateTime? selectedDate;
const EventAddEditPage({super.key, this.event, this.selectedDate});
@override
State<EventAddEditPage> createState() => _EventAddEditPageState();
@@ -27,7 +29,10 @@ class _EventAddEditPageState extends State<EventAddEditPage> {
void initState() {
super.initState();
_controller = EventFormController();
_controller.initialize(widget.event);
_controller.initialize(
existingEvent: widget.event,
selectedDate: widget.selectedDate,
);
}
@override

View File

@@ -5,6 +5,9 @@ import 'package:em2rp/utils/colors.dart';
import 'package:provider/provider.dart';
import 'package:em2rp/providers/local_user_provider.dart';
import 'package:em2rp/views/event_add_page.dart';
import 'package:em2rp/services/ics_export_service.dart';
import 'dart:html' as html;
import 'dart:convert';
class EventDetailsHeader extends StatefulWidget {
final EventModel event;
@@ -93,6 +96,12 @@ class _EventDetailsHeaderState extends State<EventDetailsHeader> {
),
const SizedBox(width: 12),
_buildStatusIcon(widget.event.status),
const SizedBox(width: 8),
IconButton(
icon: const Icon(Icons.calendar_today, color: AppColors.rouge),
tooltip: 'Exporter vers Google Calendar',
onPressed: _exportToCalendar,
),
if (Provider.of<LocalUserProvider>(context, listen: false)
.hasPermission('edit_event')) ...[
const SizedBox(width: 8),
@@ -112,6 +121,63 @@ class _EventDetailsHeaderState extends State<EventDetailsHeader> {
);
}
Future<void> _exportToCalendar() async {
try {
// Afficher un indicateur de chargement
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Row(
children: [
SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white),
),
SizedBox(width: 16),
Text('Génération du fichier ICS...'),
],
),
duration: Duration(seconds: 2),
),
);
// Générer le contenu ICS
final icsContent = await IcsExportService.generateIcsContent(widget.event);
final fileName = IcsExportService.generateFileName(widget.event);
// Créer un blob et télécharger le fichier
final bytes = utf8.encode(icsContent);
final blob = html.Blob([bytes], 'text/calendar');
final url = html.Url.createObjectUrlFromBlob(blob);
html.AnchorElement(href: url)
..setAttribute('download', fileName)
..click();
html.Url.revokeObjectUrl(url);
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Événement exporté : $fileName'),
backgroundColor: Colors.green,
action: SnackBarAction(
label: 'OK',
textColor: Colors.white,
onPressed: () {},
),
),
);
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Erreur lors de l\'export : $e'),
backgroundColor: Colors.red,
),
);
}
}
Widget _buildStatusIcon(EventStatus status) {
Color color;
IconData icon;

View File

@@ -181,16 +181,24 @@ class EventBasicInfoSection extends StatelessWidget {
}
Future<void> _selectStartDateTime(BuildContext context) async {
// Utiliser la date actuelle de l'événement ou aujourd'hui
final initialDate = startDateTime ?? DateTime.now();
final picked = await showDatePicker(
context: context,
initialDate: DateTime.now(),
initialDate: initialDate,
firstDate: DateTime(2020),
lastDate: DateTime(2099),
);
if (picked != null) {
// Utiliser l'heure actuelle de l'événement ou l'heure actuelle
final initialTime = startDateTime != null
? TimeOfDay(hour: startDateTime!.hour, minute: startDateTime!.minute)
: TimeOfDay.now();
final time = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
initialTime: initialTime,
);
if (time != null) {
final newDateTime = DateTime(
@@ -206,16 +214,24 @@ class EventBasicInfoSection extends StatelessWidget {
}
Future<void> _selectEndDateTime(BuildContext context) async {
// Utiliser la date actuelle de fin ou date de début + 1h
final initialDate = endDateTime ?? startDateTime!.add(const Duration(hours: 1));
final picked = await showDatePicker(
context: context,
initialDate: startDateTime!.add(const Duration(hours: 1)),
initialDate: initialDate,
firstDate: startDateTime!,
lastDate: DateTime(2099),
);
if (picked != null) {
// Utiliser l'heure actuelle de l'événement ou l'heure actuelle
final initialTime = endDateTime != null
? TimeOfDay(hour: endDateTime!.hour, minute: endDateTime!.minute)
: TimeOfDay.now();
final time = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
initialTime: initialTime,
);
if (time != null) {
final newDateTime = DateTime(