import 'package:flutter/material.dart'; import 'package:table_calendar/table_calendar.dart'; import 'package:em2rp/utils/colors.dart'; import 'package:em2rp/models/event_model.dart'; import 'package:em2rp/utils/calendar_utils.dart'; class MonthView extends StatelessWidget { final DateTime focusedDay; final DateTime? selectedDay; final CalendarFormat calendarFormat; final Function(DateTime, DateTime) onDaySelected; final Function(CalendarFormat) onFormatChanged; final Function(DateTime) onPageChanged; final List events; final Function(EventModel) onEventSelected; const MonthView({ super.key, required this.focusedDay, required this.selectedDay, required this.calendarFormat, required this.onDaySelected, required this.onFormatChanged, required this.onPageChanged, required this.events, required this.onEventSelected, }); @override Widget build(BuildContext context) { return LayoutBuilder( builder: (context, constraints) { final rowHeight = (constraints.maxHeight - 100) / 6; return Container( height: constraints.maxHeight, padding: const EdgeInsets.all(8), child: TableCalendar( firstDay: DateTime.utc(2020, 1, 1), lastDay: DateTime.utc(2030, 12, 31), focusedDay: focusedDay, calendarFormat: calendarFormat, startingDayOfWeek: StartingDayOfWeek.monday, locale: 'fr_FR', availableCalendarFormats: const { CalendarFormat.month: 'Mois', CalendarFormat.week: 'Semaine', }, selectedDayPredicate: (day) => isSameDay(selectedDay, day), onDaySelected: onDaySelected, onFormatChanged: onFormatChanged, onPageChanged: onPageChanged, calendarStyle: _buildCalendarStyle(), rowHeight: rowHeight, headerStyle: _buildHeaderStyle(), calendarBuilders: _buildCalendarBuilders(), ), ); }, ); } CalendarStyle _buildCalendarStyle() { return CalendarStyle( defaultDecoration: BoxDecoration( border: Border.all(color: Colors.grey.shade300), borderRadius: BorderRadius.circular(4), ), selectedDecoration: BoxDecoration( color: AppColors.rouge, border: Border.all(color: AppColors.rouge), borderRadius: BorderRadius.circular(4), ), todayDecoration: BoxDecoration( color: AppColors.rouge.withAlpha(26), border: Border.all(color: AppColors.rouge), borderRadius: BorderRadius.circular(4), ), outsideDecoration: BoxDecoration( border: Border.all(color: Colors.grey.shade300), borderRadius: BorderRadius.circular(4), ), outsideDaysVisible: false, cellMargin: EdgeInsets.zero, cellPadding: EdgeInsets.zero, ); } HeaderStyle _buildHeaderStyle() { return HeaderStyle( formatButtonVisible: true, titleCentered: true, formatButtonShowsNext: false, formatButtonDecoration: BoxDecoration( color: AppColors.rouge, borderRadius: BorderRadius.circular(16), ), formatButtonTextStyle: const TextStyle(color: Colors.white), leftChevronIcon: const Icon(Icons.chevron_left, color: AppColors.rouge), rightChevronIcon: const Icon(Icons.chevron_right, color: AppColors.rouge), headerPadding: const EdgeInsets.symmetric(vertical: 8), titleTextStyle: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ); } CalendarBuilders _buildCalendarBuilders() { return CalendarBuilders( dowBuilder: (context, day) { return Center( child: Text( CalendarUtils.getShortDayName(day.weekday), style: const TextStyle( fontWeight: FontWeight.bold, ), ), ); }, defaultBuilder: (context, day, focusedDay) { return _buildDayCell(day, false); }, selectedBuilder: (context, day, focusedDay) { return _buildDayCell(day, true); }, todayBuilder: (context, day, focusedDay) { return _buildDayCell(day, false, isToday: true); }, ); } Widget _buildDayCell(DateTime day, bool isSelected, {bool isToday = false}) { final dayEvents = CalendarUtils.getEventsForDay(day, events); final textColor = isSelected ? Colors.white : (isToday ? AppColors.rouge : null); final badgeColor = isSelected ? Colors.white : AppColors.rouge; final badgeTextColor = isSelected ? AppColors.rouge : Colors.white; BoxDecoration decoration; if (isSelected) { decoration = BoxDecoration( color: AppColors.rouge, border: Border.all(color: AppColors.rouge), borderRadius: BorderRadius.circular(4), ); } else if (isToday) { decoration = BoxDecoration( color: AppColors.rouge.withAlpha(26), border: Border.all(color: AppColors.rouge), borderRadius: BorderRadius.circular(4), ); } else { decoration = BoxDecoration( color: null, border: Border.all(color: Colors.grey.shade300), borderRadius: BorderRadius.circular(4), ); } return Container( margin: const EdgeInsets.all(4), decoration: decoration, child: Stack( children: [ Positioned( top: 4, left: 4, child: Text( day.day.toString(), style: TextStyle(color: textColor), ), ), if (dayEvents.isNotEmpty) Positioned( top: 4, right: 4, child: Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( color: badgeColor, borderRadius: BorderRadius.circular(10), ), child: Text( dayEvents.length.toString(), style: TextStyle( color: badgeTextColor, fontSize: 12, fontWeight: FontWeight.bold, ), ), ), ), if (dayEvents.isNotEmpty) Positioned( bottom: 2, left: 2, right: 2, top: 28, child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: dayEvents .map((event) => _buildEventItem(event, isSelected, day)) .toList(), ), ), ), ], ), ); } Widget _buildEventItem( EventModel event, bool isSelected, DateTime currentDay) { return GestureDetector( onTap: () { onDaySelected(currentDay, currentDay); onEventSelected(event); }, child: Container( margin: const EdgeInsets.only(bottom: 2), padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), decoration: BoxDecoration( color: isSelected ? Colors.white.withAlpha(51) : AppColors.rouge.withAlpha(26), borderRadius: BorderRadius.circular(4), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( event.name, style: TextStyle( fontSize: 12, color: isSelected ? Colors.white : AppColors.rouge, fontWeight: FontWeight.bold, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), if (CalendarUtils.isMultiDayEvent(event)) Text( 'Jour ${CalendarUtils.calculateDayNumber(event.startDateTime, event.startDateTime)}/${CalendarUtils.calculateTotalDays(event)}', style: TextStyle( fontSize: 10, color: isSelected ? Colors.white : AppColors.rouge, ), maxLines: 1, ), ], ), ), ); } }