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:
@@ -5,6 +5,11 @@ import 'package:em2rp/models/event_model.dart';
|
||||
import 'package:em2rp/utils/calendar_utils.dart';
|
||||
|
||||
class MonthView extends StatelessWidget {
|
||||
static const double _calendarPadding = 8.0;
|
||||
static const double _headerHeight = 52.0;
|
||||
static const double _headerVerticalPadding = 16.0;
|
||||
static const double _daysOfWeekHeight = 16.0;
|
||||
|
||||
final DateTime focusedDay;
|
||||
final DateTime? selectedDay;
|
||||
final CalendarFormat calendarFormat;
|
||||
@@ -30,11 +35,17 @@ class MonthView extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final rowHeight = (constraints.maxHeight - 100) / 6;
|
||||
final rowCount = _computeRowCount(focusedDay);
|
||||
final availableHeight = constraints.maxHeight -
|
||||
(_calendarPadding * 2) -
|
||||
_headerHeight -
|
||||
_headerVerticalPadding -
|
||||
_daysOfWeekHeight;
|
||||
final rowHeight = availableHeight / rowCount;
|
||||
|
||||
return Container(
|
||||
height: constraints.maxHeight,
|
||||
padding: const EdgeInsets.all(8),
|
||||
padding: const EdgeInsets.all(_calendarPadding),
|
||||
child: TableCalendar(
|
||||
firstDay: DateTime.utc(2020, 1, 1),
|
||||
lastDay: DateTime.utc(2030, 12, 31),
|
||||
@@ -42,6 +53,7 @@ class MonthView extends StatelessWidget {
|
||||
calendarFormat: calendarFormat,
|
||||
startingDayOfWeek: StartingDayOfWeek.monday,
|
||||
locale: 'fr_FR',
|
||||
daysOfWeekHeight: _daysOfWeekHeight,
|
||||
availableCalendarFormats: const {
|
||||
CalendarFormat.month: 'Mois',
|
||||
CalendarFormat.week: 'Semaine',
|
||||
@@ -132,10 +144,9 @@ class MonthView extends StatelessWidget {
|
||||
|
||||
Widget _buildDayCell(DateTime day, bool isSelected, {bool isToday = false}) {
|
||||
final dayEvents = CalendarUtils.getEventsForDay(day, events);
|
||||
final statusCounts = _getStatusCounts(dayEvents);
|
||||
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) {
|
||||
@@ -161,56 +172,125 @@ class MonthView extends StatelessWidget {
|
||||
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: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
day.day.toString(),
|
||||
style: TextStyle(color: textColor),
|
||||
),
|
||||
child: Text(
|
||||
dayEvents.length.toString(),
|
||||
style: TextStyle(
|
||||
color: badgeTextColor,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
const SizedBox(width: 4),
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: Wrap(
|
||||
spacing: 4,
|
||||
runSpacing: 2,
|
||||
alignment: WrapAlignment.end,
|
||||
children: _buildStatusBadges(statusCounts),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (dayEvents.isNotEmpty) ...[
|
||||
const SizedBox(height: 4),
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: dayEvents
|
||||
.map((event) => _buildEventItem(event, isSelected, day))
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
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(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Map<EventStatus, int> _getStatusCounts(List<EventModel> dayEvents) {
|
||||
final counts = <EventStatus, int>{
|
||||
EventStatus.confirmed: 0,
|
||||
EventStatus.waitingForApproval: 0,
|
||||
EventStatus.canceled: 0,
|
||||
};
|
||||
|
||||
for (final event in dayEvents) {
|
||||
counts[event.status] = (counts[event.status] ?? 0) + 1;
|
||||
}
|
||||
|
||||
return counts;
|
||||
}
|
||||
|
||||
List<Widget> _buildStatusBadges(Map<EventStatus, int> statusCounts) {
|
||||
final badges = <Widget>[];
|
||||
|
||||
void addBadge({
|
||||
required EventStatus status,
|
||||
required Color backgroundColor,
|
||||
required Color textColor,
|
||||
required String tooltipLabel,
|
||||
}) {
|
||||
final count = statusCounts[status] ?? 0;
|
||||
if (count <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
badges.add(
|
||||
Tooltip(
|
||||
message: '$count $tooltipLabel',
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor,
|
||||
borderRadius: BorderRadius.circular(999),
|
||||
),
|
||||
child: Text(
|
||||
count.toString(),
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
addBadge(
|
||||
status: EventStatus.confirmed,
|
||||
backgroundColor: Colors.green,
|
||||
textColor: Colors.white,
|
||||
tooltipLabel:
|
||||
'validé${(statusCounts[EventStatus.confirmed] ?? 0) > 1 ? 's' : ''}',
|
||||
);
|
||||
addBadge(
|
||||
status: EventStatus.waitingForApproval,
|
||||
backgroundColor: Colors.amber,
|
||||
textColor: Colors.black,
|
||||
tooltipLabel: 'en attente',
|
||||
);
|
||||
addBadge(
|
||||
status: EventStatus.canceled,
|
||||
backgroundColor: Colors.red,
|
||||
textColor: Colors.white,
|
||||
tooltipLabel:
|
||||
'annulé${(statusCounts[EventStatus.canceled] ?? 0) > 1 ? 's' : ''}',
|
||||
);
|
||||
|
||||
return badges;
|
||||
}
|
||||
|
||||
Widget _buildEventItem(
|
||||
EventModel event, bool isSelected, DateTime currentDay) {
|
||||
Color color;
|
||||
@@ -228,7 +308,6 @@ class MonthView extends StatelessWidget {
|
||||
icon = Icons.close;
|
||||
break;
|
||||
case EventStatus.waitingForApproval:
|
||||
default:
|
||||
color = Colors.amber;
|
||||
textColor = Colors.black;
|
||||
icon = Icons.hourglass_empty;
|
||||
@@ -243,7 +322,8 @@ class MonthView extends StatelessWidget {
|
||||
margin: const EdgeInsets.only(bottom: 2),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected ? color.withAlpha(220) : color.withOpacity(0.18),
|
||||
color:
|
||||
isSelected ? color.withAlpha(220) : color.withValues(alpha: 0.18),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Row(
|
||||
@@ -282,4 +362,13 @@ class MonthView extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Calcule le nombre de rangées affichées pour le mois de [focusedDay]
|
||||
/// (calendrier commençant le lundi : offset = weekday - 1)
|
||||
int _computeRowCount(DateTime focusedDay) {
|
||||
final firstOfMonth = DateTime(focusedDay.year, focusedDay.month, 1);
|
||||
final daysInMonth = DateTime(focusedDay.year, focusedDay.month + 1, 0).day;
|
||||
final offset = (firstOfMonth.weekday - 1) % 7; // 0 = lundi, 6 = dimanche
|
||||
return ((daysInMonth + offset) / 7).ceil();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user