feat: export ICS
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user