feat: mise à jour de la version à 1.1.18 et amélioration de la page calendrier avec ajout de la fonctionnalité de rafraîchissement des événements

This commit is contained in:
ElPoyo
2026-03-12 21:14:44 +01:00
parent 6737ad80e4
commit ecf4a5cede
11 changed files with 434 additions and 221 deletions

View File

@@ -1,3 +1,5 @@
import 'dart:math' as math;
import 'package:em2rp/providers/local_user_provider.dart';
import 'package:em2rp/providers/event_provider.dart';
import 'package:em2rp/utils/performance_monitor.dart';
@@ -24,13 +26,22 @@ class CalendarPage extends StatefulWidget {
}
class _CalendarPageState extends State<CalendarPage> {
static const double _minDetailsPaneFraction = 0.25;
static const double _maxDetailsPaneFraction = 0.5;
static const double _desktopResizeHandleWidth = 12;
static const double _minCalendarPaneWidth = 480;
static const double _minDetailsPaneWidth = 320;
CalendarFormat _calendarFormat = CalendarFormat.month;
DateTime _focusedDay = DateTime.now();
DateTime? _selectedDay;
EventModel? _selectedEvent;
bool _calendarCollapsed = false;
int _selectedEventIndex = 0;
String? _selectedUserId; // Filtre par utilisateur (null = tous les événements)
String?
_selectedUserId; // Filtre par utilisateur (null = tous les événements)
bool _isRefreshing = false;
double _detailsPaneFraction = 0.35;
@override
void initState() {
@@ -46,13 +57,15 @@ class _CalendarPageState extends State<CalendarPage> {
Future<void> _loadCurrentMonthEvents() async {
PerformanceMonitor.start('CalendarPage.loadCurrentMonthEvents');
final localAuthProvider = Provider.of<LocalUserProvider>(context, listen: false);
final localAuthProvider =
Provider.of<LocalUserProvider>(context, listen: false);
final eventProvider = Provider.of<EventProvider>(context, listen: false);
final userId = localAuthProvider.uid;
final canViewAllEvents = localAuthProvider.hasPermission('view_all_events');
if (userId != null) {
print('[CalendarPage] Loading events for ${_focusedDay.year}-${_focusedDay.month}');
print(
'[CalendarPage] Loading events for ${_focusedDay.year}-${_focusedDay.month}');
await eventProvider.loadMonthEvents(
userId,
@@ -79,6 +92,19 @@ class _CalendarPageState extends State<CalendarPage> {
PerformanceMonitor.end('CalendarPage.loadCurrentMonthEvents');
}
/// Vide le cache et recharge les événements du mois courant
Future<void> _refreshEvents() async {
if (_isRefreshing) return;
setState(() => _isRefreshing = true);
try {
final eventProvider = Provider.of<EventProvider>(context, listen: false);
eventProvider.clearAllCache();
await _loadCurrentMonthEvents();
} finally {
if (mounted) setState(() => _isRefreshing = false);
}
}
/// Charge les événements de manière asynchrone et sélectionne l'événement approprié
/// DEPRECATED: Utiliser _loadCurrentMonthEvents à la place
Future<void> _loadEventsAsync() async {
@@ -107,9 +133,10 @@ class _CalendarPageState extends State<CalendarPage> {
final todayEvents = events.where((e) {
final start = e.startDateTime;
return start.year == now.year &&
start.month == now.month &&
start.day == now.day;
}).toList()..sort((a, b) => a.startDateTime.compareTo(b.startDateTime));
start.month == now.month &&
start.day == now.day;
}).toList()
..sort((a, b) => a.startDateTime.compareTo(b.startDateTime));
EventModel? selected;
DateTime? selectedDay;
@@ -121,7 +148,8 @@ class _CalendarPageState extends State<CalendarPage> {
// Chercher le prochain événement à venir
final futureEvents = events
.where((e) => e.startDateTime.isAfter(now))
.toList()..sort((a, b) => a.startDateTime.compareTo(b.startDateTime));
.toList()
..sort((a, b) => a.startDateTime.compareTo(b.startDateTime));
if (futureEvents.isNotEmpty) {
selected = futureEvents[0];
@@ -186,21 +214,98 @@ class _CalendarPageState extends State<CalendarPage> {
});
}
double _clampDetailsPaneFraction(double fraction, double totalWidth) {
if (totalWidth <= 0) {
return fraction.clamp(_minDetailsPaneFraction, _maxDetailsPaneFraction);
}
final minFractionFromPixels = _minDetailsPaneWidth / totalWidth;
final maxFractionFromPixels =
(totalWidth - _desktopResizeHandleWidth - _minCalendarPaneWidth) /
totalWidth;
final minFraction =
math.max(_minDetailsPaneFraction, minFractionFromPixels);
final maxFraction =
math.min(_maxDetailsPaneFraction, maxFractionFromPixels);
if (maxFraction < minFraction) {
return fraction.clamp(_minDetailsPaneFraction, _maxDetailsPaneFraction);
}
return fraction.clamp(minFraction, maxFraction);
}
Widget _buildDesktopDetailsPane(List<EventModel> filteredEvents) {
if (_selectedEvent != null) {
return EventDetails(
event: _selectedEvent!,
selectedDate: _selectedDay,
events: filteredEvents,
onSelectEvent: (event, date) {
setState(() {
_selectedEvent = event;
_selectedDay = date;
});
},
);
}
return Center(
child: _selectedDay != null
? const Text('Aucun événement ne démarre à cette date')
: const Text('Sélectionnez un événement pour voir les détails'),
);
}
Widget _buildDesktopResizeHandle(double totalWidth) {
return MouseRegion(
cursor: SystemMouseCursors.resizeLeftRight,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onHorizontalDragUpdate: (details) {
setState(() {
_detailsPaneFraction = _clampDetailsPaneFraction(
_detailsPaneFraction - (details.delta.dx / totalWidth),
totalWidth,
);
});
},
child: SizedBox(
width: _desktopResizeHandleWidth,
child: Center(
child: Container(
width: 4,
margin: const EdgeInsets.symmetric(vertical: 16),
decoration: BoxDecoration(
color: Colors.grey.shade300,
borderRadius: BorderRadius.circular(999),
),
),
),
),
),
);
}
@override
Widget build(BuildContext context) {
final eventProvider = Provider.of<EventProvider>(context);
final localUserProvider = Provider.of<LocalUserProvider>(context);
final canCreateEvents = localUserProvider.hasPermission('create_events');
final canViewAllUserEvents = localUserProvider.hasPermission('view_all_user_events');
final canViewAllUserEvents =
localUserProvider.hasPermission('view_all_user_events');
final isMobile = MediaQuery.of(context).size.width < 600;
// Appliquer le filtre utilisateur si actif
final filteredEvents = _getFilteredEvents(eventProvider.events);
// Debug logs
print('[CalendarPage.build] Total events: ${eventProvider.events.length}, Filtered: ${filteredEvents.length}');
print(
'[CalendarPage.build] Total events: ${eventProvider.events.length}, Filtered: ${filteredEvents.length}');
if (eventProvider.events.isNotEmpty) {
print('[CalendarPage.build] First event: ${eventProvider.events.first.name} at ${eventProvider.events.first.startDateTime}');
print(
'[CalendarPage.build] First event: ${eventProvider.events.first.name} at ${eventProvider.events.first.startDateTime}');
}
if (eventProvider.isLoading) {
@@ -214,6 +319,26 @@ class _CalendarPageState extends State<CalendarPage> {
return Scaffold(
appBar: CustomAppBar(
title: "Calendrier",
actions: [
if (_isRefreshing)
const Padding(
padding: EdgeInsets.symmetric(horizontal: 12),
child: SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
),
)
else
IconButton(
icon: const Icon(Icons.refresh, color: Colors.white),
tooltip: 'Mettre à jour les événements',
onPressed: _refreshEvents,
),
],
),
drawer: const MainDrawer(currentPage: '/calendar'),
body: Column(
@@ -247,7 +372,9 @@ class _CalendarPageState extends State<CalendarPage> {
),
// Corps du calendrier
Expanded(
child: isMobile ? _buildMobileLayout(filteredEvents) : _buildDesktopLayout(filteredEvents),
child: isMobile
? _buildMobileLayout(filteredEvents)
: _buildDesktopLayout(filteredEvents),
),
],
),
@@ -271,36 +398,30 @@ class _CalendarPageState extends State<CalendarPage> {
}
Widget _buildDesktopLayout(List<EventModel> filteredEvents) {
return Row(
children: [
// Calendrier (65% de la largeur)
Expanded(
flex: 65,
child: _buildCalendar(filteredEvents),
),
// Détails de l'événement (35% de la largeur)
Expanded(
flex: 35,
child: _selectedEvent != null
? EventDetails(
event: _selectedEvent!,
selectedDate: _selectedDay,
events: filteredEvents,
onSelectEvent: (event, date) {
setState(() {
_selectedEvent = event;
_selectedDay = date;
});
},
)
: Center(
child: _selectedDay != null
? Text('Aucun événement ne démarre à cette date')
: const Text(
'Sélectionnez un événement pour voir les détails'),
),
),
],
return LayoutBuilder(
builder: (context, constraints) {
final totalWidth = constraints.maxWidth;
final detailsPaneFraction =
_clampDetailsPaneFraction(_detailsPaneFraction, totalWidth);
final detailsWidth = totalWidth * detailsPaneFraction;
final calendarWidth =
totalWidth - _desktopResizeHandleWidth - detailsWidth;
return Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(
width: calendarWidth,
child: _buildCalendar(filteredEvents),
),
_buildDesktopResizeHandle(totalWidth),
SizedBox(
width: detailsWidth,
child: _buildDesktopDetailsPane(filteredEvents),
),
],
);
},
);
}
@@ -341,19 +462,23 @@ class _CalendarPageState extends State<CalendarPage> {
if (details.primaryVelocity != null) {
if (details.primaryVelocity! < -200) {
// Swipe gauche : mois suivant
final newMonth = DateTime(_focusedDay.year, _focusedDay.month + 1, 1);
final newMonth =
DateTime(_focusedDay.year, _focusedDay.month + 1, 1);
setState(() {
_focusedDay = newMonth;
});
print('[CalendarPage] Month changed to ${newMonth.year}-${newMonth.month}');
print(
'[CalendarPage] Month changed to ${newMonth.year}-${newMonth.month}');
_loadCurrentMonthEvents();
} else if (details.primaryVelocity! > 200) {
// Swipe droite : mois précédent
final newMonth = DateTime(_focusedDay.year, _focusedDay.month - 1, 1);
final newMonth =
DateTime(_focusedDay.year, _focusedDay.month - 1, 1);
setState(() {
_focusedDay = newMonth;
});
print('[CalendarPage] Month changed to ${newMonth.year}-${newMonth.month}');
print(
'[CalendarPage] Month changed to ${newMonth.year}-${newMonth.month}');
_loadCurrentMonthEvents();
}
}
@@ -385,7 +510,8 @@ class _CalendarPageState extends State<CalendarPage> {
setState(() {
_focusedDay = newMonth;
});
print('[CalendarPage] Month changed to ${newMonth.year}-${newMonth.month}');
print(
'[CalendarPage] Month changed to ${newMonth.year}-${newMonth.month}');
_loadCurrentMonthEvents();
} else if (details.primaryVelocity! > 200) {
// Swipe droite : mois précédent
@@ -394,7 +520,8 @@ class _CalendarPageState extends State<CalendarPage> {
setState(() {
_focusedDay = newMonth;
});
print('[CalendarPage] Month changed to ${newMonth.year}-${newMonth.month}');
print(
'[CalendarPage] Month changed to ${newMonth.year}-${newMonth.month}');
_loadCurrentMonthEvents();
}
}
@@ -557,11 +684,13 @@ class _CalendarPageState extends State<CalendarPage> {
icon: const Icon(Icons.chevron_left,
color: AppColors.rouge, size: 28),
onPressed: () {
final newMonth = DateTime(_focusedDay.year, _focusedDay.month - 1, 1);
final newMonth =
DateTime(_focusedDay.year, _focusedDay.month - 1, 1);
setState(() {
_focusedDay = newMonth;
});
print('[CalendarPage] Month changed to ${newMonth.year}-${newMonth.month}');
print(
'[CalendarPage] Month changed to ${newMonth.year}-${newMonth.month}');
_loadCurrentMonthEvents();
},
),
@@ -600,11 +729,13 @@ class _CalendarPageState extends State<CalendarPage> {
icon: const Icon(Icons.chevron_right,
color: AppColors.rouge, size: 28),
onPressed: () {
final newMonth = DateTime(_focusedDay.year, _focusedDay.month + 1, 1);
final newMonth =
DateTime(_focusedDay.year, _focusedDay.month + 1, 1);
setState(() {
_focusedDay = newMonth;
});
print('[CalendarPage] Month changed to ${newMonth.year}-${newMonth.month}');
print(
'[CalendarPage] Month changed to ${newMonth.year}-${newMonth.month}');
_loadCurrentMonthEvents();
},
),
@@ -721,7 +852,7 @@ class _CalendarPageState extends State<CalendarPage> {
onPageChanged: (focusedDay) {
// Détecter si on a changé de mois
final monthChanged = focusedDay.year != _focusedDay.year ||
focusedDay.month != _focusedDay.month;
focusedDay.month != _focusedDay.month;
setState(() {
_focusedDay = focusedDay;
@@ -729,7 +860,8 @@ class _CalendarPageState extends State<CalendarPage> {
// Charger les événements du nouveau mois si nécessaire
if (monthChanged) {
print('[CalendarPage] Month changed to ${focusedDay.year}-${focusedDay.month}');
print(
'[CalendarPage] Month changed to ${focusedDay.year}-${focusedDay.month}');
_loadCurrentMonthEvents();
}
},