Début page calendrier
This commit is contained in:
@ -4,34 +4,755 @@ import 'package:em2rp/widgets/custom_app_bar.dart';
|
||||
import 'package:em2rp/views/widgets/nav/main_drawer.dart';
|
||||
import 'package:provider/provider.dart'; // Import Provider
|
||||
import 'package:em2rp/utils/colors.dart';
|
||||
import 'package:table_calendar/table_calendar.dart';
|
||||
import 'package:em2rp/models/event_model.dart';
|
||||
import 'package:em2rp/widgets/event_details.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
|
||||
class CalendarPage extends StatelessWidget {
|
||||
class CalendarPage extends StatefulWidget {
|
||||
const CalendarPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<CalendarPage> createState() => _CalendarPageState();
|
||||
}
|
||||
|
||||
class _CalendarPageState extends State<CalendarPage> {
|
||||
CalendarFormat _calendarFormat = CalendarFormat.month;
|
||||
DateTime _focusedDay = DateTime.now();
|
||||
DateTime? _selectedDay;
|
||||
EventModel? _selectedEvent;
|
||||
|
||||
// Événements de test
|
||||
final List<EventModel> _testEvents = [
|
||||
EventModel(
|
||||
id: '1',
|
||||
name: 'Bal a Grammond',
|
||||
description: 'Lorem Ipsum',
|
||||
startDateTime: DateTime(2025, 3, 28, 12, 30),
|
||||
endDateTime: DateTime(2025, 3, 28, 23, 30),
|
||||
price: 950.34,
|
||||
installationTime: 3,
|
||||
disassemblyTime: 2,
|
||||
eventTypeId: '/eventTypes/Bal',
|
||||
customerId: '/customers/DnjJ1HOPBLqEeExs0nDl',
|
||||
address: const LatLng(45.566521035268224, 4.439601075086365),
|
||||
),
|
||||
EventModel(
|
||||
id: '2',
|
||||
name: 'Mariage à Lyon',
|
||||
description: 'Cérémonie et réception',
|
||||
startDateTime: DateTime(2025, 3, 28, 15, 0),
|
||||
endDateTime: DateTime(2025, 3, 29, 4, 0),
|
||||
price: 2500.00,
|
||||
installationTime: 4,
|
||||
disassemblyTime: 3,
|
||||
eventTypeId: '/eventTypes/Mariage',
|
||||
customerId: '/customers/Test123',
|
||||
address: const LatLng(45.7578137, 4.8320114),
|
||||
),
|
||||
];
|
||||
|
||||
void _changeWeek(int delta) {
|
||||
setState(() {
|
||||
_focusedDay = _focusedDay.add(Duration(days: 7 * delta));
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localAuthProvider = Provider.of<LocalUserProvider>(context);
|
||||
final isMobile = MediaQuery.of(context).size.width < 600;
|
||||
|
||||
return Scaffold(
|
||||
appBar: const CustomAppBar(
|
||||
title: 'Calendrier',
|
||||
),
|
||||
drawer: const MainDrawer(currentPage: '/calendar'),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Text('Page Calendrier', style: TextStyle(fontSize: 24)),
|
||||
const SizedBox(height: 20),
|
||||
if (localAuthProvider.role == 'ADMIN') // Get role from UserProvider
|
||||
const Text('Vue Admin du Calendrier',
|
||||
style: TextStyle(fontSize: 18, color: AppColors.rouge))
|
||||
else
|
||||
const Text('Vue Utilisateur du Calendrier',
|
||||
style: TextStyle(fontSize: 18, color: Colors.blueGrey)),
|
||||
],
|
||||
body: isMobile ? _buildMobileLayout() : _buildDesktopLayout(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDesktopLayout() {
|
||||
return Row(
|
||||
children: [
|
||||
// Calendrier (65% de la largeur)
|
||||
Expanded(
|
||||
flex: 65,
|
||||
child: _buildCalendar(),
|
||||
),
|
||||
// Détails de l'événement (35% de la largeur)
|
||||
Expanded(
|
||||
flex: 35,
|
||||
child: _selectedEvent != null
|
||||
? EventDetails(event: _selectedEvent!)
|
||||
: const Center(
|
||||
child:
|
||||
Text('Sélectionnez un événement pour voir les détails'),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMobileLayout() {
|
||||
return Column(
|
||||
children: [
|
||||
// Calendrier
|
||||
Expanded(
|
||||
child: _buildCalendar(),
|
||||
),
|
||||
// Détails de l'événement
|
||||
if (_selectedEvent != null)
|
||||
Expanded(
|
||||
child: EventDetails(event: _selectedEvent!),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCalendar() {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
if (_calendarFormat == CalendarFormat.week) {
|
||||
return _buildWeekView(constraints);
|
||||
} else {
|
||||
return _buildMonthView(constraints);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMonthView(BoxConstraints constraints) {
|
||||
// Calculer la hauteur des lignes en fonction de la hauteur disponible
|
||||
// Ajustement pour les mois à 6 semaines
|
||||
final rowHeight = (constraints.maxHeight - 100) /
|
||||
6; // Augmenté de 80 à 100 pour donner plus d'espace
|
||||
|
||||
return Container(
|
||||
height: constraints.maxHeight,
|
||||
padding:
|
||||
const EdgeInsets.all(8), // Réduit de 16 à 8 pour gagner de l'espace
|
||||
child: TableCalendar(
|
||||
firstDay: DateTime.utc(2020, 1, 1),
|
||||
lastDay: DateTime.utc(2030, 12, 31),
|
||||
focusedDay: _focusedDay,
|
||||
calendarFormat: _calendarFormat,
|
||||
startingDayOfWeek: StartingDayOfWeek.monday,
|
||||
availableCalendarFormats: const {
|
||||
CalendarFormat.month: 'Mois',
|
||||
CalendarFormat.week: 'Semaine',
|
||||
},
|
||||
selectedDayPredicate: (day) {
|
||||
return isSameDay(_selectedDay, day);
|
||||
},
|
||||
onDaySelected: (selectedDay, focusedDay) {
|
||||
setState(() {
|
||||
_selectedDay = selectedDay;
|
||||
_focusedDay = focusedDay;
|
||||
});
|
||||
},
|
||||
onFormatChanged: (format) {
|
||||
setState(() {
|
||||
_calendarFormat = format;
|
||||
});
|
||||
},
|
||||
onPageChanged: (focusedDay) {
|
||||
_focusedDay = focusedDay;
|
||||
},
|
||||
calendarStyle: 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.withOpacity(0.1),
|
||||
border: Border.all(color: AppColors.rouge),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
outsideDecoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey.shade300),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
cellMargin: EdgeInsets.zero,
|
||||
cellPadding: EdgeInsets.zero,
|
||||
),
|
||||
rowHeight: rowHeight,
|
||||
headerStyle: 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),
|
||||
),
|
||||
calendarBuilders: CalendarBuilders(
|
||||
defaultBuilder: (context, day, focusedDay) {
|
||||
final events = _getEventsForDay(day);
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey.shade300),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
Center(
|
||||
child: Text(
|
||||
day.day.toString(),
|
||||
style: TextStyle(
|
||||
color:
|
||||
isSameDay(day, _selectedDay) ? Colors.white : null,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (events.isNotEmpty)
|
||||
Positioned(
|
||||
bottom: 2,
|
||||
left: 2,
|
||||
right: 2,
|
||||
top: 24, // Espace pour le numéro du jour
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: events
|
||||
.map((event) => GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_selectedEvent = event;
|
||||
_selectedDay = day;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(bottom: 2),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.rouge.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
event.name,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.rouge,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
if (_isMultiDayEvent(event))
|
||||
Text(
|
||||
'Jour ${_calculateDayNumber(event.startDateTime, day)}/${_calculateTotalDays(event)}',
|
||||
style: const TextStyle(
|
||||
fontSize: 10,
|
||||
color: AppColors.rouge,
|
||||
),
|
||||
maxLines: 1,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
selectedBuilder: (context, day, focusedDay) {
|
||||
final events = _getEventsForDay(day);
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.rouge,
|
||||
border: Border.all(color: AppColors.rouge),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
Center(
|
||||
child: Text(
|
||||
day.day.toString(),
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
if (events.isNotEmpty)
|
||||
Positioned(
|
||||
bottom: 2,
|
||||
left: 2,
|
||||
right: 2,
|
||||
top: 24, // Espace pour le numéro du jour
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: events
|
||||
.map((event) => GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_selectedEvent = event;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(bottom: 2),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.2),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
event.name,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
if (_isMultiDayEvent(event))
|
||||
Text(
|
||||
'Jour ${_calculateDayNumber(event.startDateTime, day)}/${_calculateTotalDays(event)}',
|
||||
style: const TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.white,
|
||||
),
|
||||
maxLines: 1,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildWeekView(BoxConstraints constraints) {
|
||||
final weekStart =
|
||||
_focusedDay.subtract(Duration(days: _focusedDay.weekday - 1));
|
||||
final weekEnd = weekStart.add(const Duration(days: 6));
|
||||
|
||||
// Ajustement de la hauteur pour éviter l'overflow
|
||||
double availableHeight =
|
||||
constraints.maxHeight - 80; // Réserver de l'espace pour les en-têtes
|
||||
final hourHeight = availableHeight / 24;
|
||||
final dayWidth = (constraints.maxWidth - 50) / 7;
|
||||
|
||||
// Préparer les événements par jour (en tenant compte des multi-jours)
|
||||
List<List<_PositionedEvent>> eventsByDay = List.generate(7, (i) => []);
|
||||
for (final event in _testEvents) {
|
||||
// Pour chaque jour de la semaine
|
||||
for (int i = 0; i < 7; i++) {
|
||||
final day = weekStart.add(Duration(days: i));
|
||||
final dayStart = DateTime(day.year, day.month, day.day, 0, 0);
|
||||
final dayEnd = DateTime(day.year, day.month, day.day, 23, 59, 59);
|
||||
// Si l'événement recouvre ce jour
|
||||
if (!(event.endDateTime.isBefore(dayStart) ||
|
||||
event.startDateTime.isAfter(dayEnd))) {
|
||||
// Tronquer les heures de début/fin si besoin
|
||||
final start = event.startDateTime.isBefore(dayStart)
|
||||
? dayStart
|
||||
: event.startDateTime;
|
||||
final end =
|
||||
event.endDateTime.isAfter(dayEnd) ? dayEnd : event.endDateTime;
|
||||
eventsByDay[i].add(_PositionedEvent(event, start, end));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pour chaque jour, calculer les "colonnes" d'événements qui se chevauchent
|
||||
List<List<_PositionedEventWithColumn>> eventsWithColumnsByDay = [];
|
||||
for (final dayEvents in eventsByDay) {
|
||||
final columns = _assignColumns(dayEvents);
|
||||
eventsWithColumnsByDay.add(columns);
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
// Barre d'en-tête semaine
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.chevron_left),
|
||||
onPressed: () => _changeWeek(-1),
|
||||
),
|
||||
Text(
|
||||
_getMonthYearString(weekStart, weekEnd),
|
||||
style:
|
||||
const TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_calendarFormat = CalendarFormat.month;
|
||||
});
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: AppColors.rouge,
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12, vertical: 8),
|
||||
),
|
||||
child: const Text('Mois'),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.chevron_right),
|
||||
onPressed: () => _changeWeek(1),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// En-tête avec les jours
|
||||
SizedBox(
|
||||
height: 40,
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 50,
|
||||
color: Colors.transparent,
|
||||
),
|
||||
...List.generate(7, (index) {
|
||||
final day = weekStart.add(Duration(days: index));
|
||||
return Container(
|
||||
width: dayWidth,
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
right: BorderSide(
|
||||
color: index < 6
|
||||
? Colors.grey.shade300
|
||||
: Colors.transparent,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(_getDayName(day.weekday),
|
||||
style:
|
||||
const TextStyle(fontWeight: FontWeight.bold)),
|
||||
Text('${day.day}',
|
||||
style: const TextStyle(fontSize: 13)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
// Grille des heures + jours
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: SizedBox(
|
||||
height: 24 * hourHeight,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Colonne des heures
|
||||
Column(
|
||||
children: List.generate(24, (index) {
|
||||
return Container(
|
||||
width: 50,
|
||||
height: hourHeight,
|
||||
alignment: Alignment.topRight,
|
||||
padding: const EdgeInsets.only(right: 4),
|
||||
child: Text(
|
||||
'${index.toString().padLeft(2, '0')}:00',
|
||||
style: TextStyle(
|
||||
color: Colors.grey.shade600,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
// Grille des jours
|
||||
Expanded(
|
||||
child: Stack(
|
||||
children: [
|
||||
// Lignes horizontales
|
||||
Column(
|
||||
children: List.generate(24, (index) {
|
||||
return Container(
|
||||
height: hourHeight,
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: Colors.grey.shade300,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
// Bordures verticales entre jours
|
||||
Positioned.fill(
|
||||
child: Row(
|
||||
children: List.generate(7, (i) {
|
||||
return Container(
|
||||
width: dayWidth,
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
right: BorderSide(
|
||||
color: i < 6
|
||||
? Colors.grey.shade300
|
||||
: Colors.transparent,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
// Événements (chevauchements et multi-jours)
|
||||
...List.generate(7, (dayIdx) {
|
||||
final dayEvents = eventsWithColumnsByDay[dayIdx];
|
||||
return Stack(
|
||||
children: dayEvents.map((e) {
|
||||
final startHour =
|
||||
e.start.hour + e.start.minute / 60;
|
||||
final endHour = e.end.hour + e.end.minute / 60;
|
||||
final duration = endHour - startHour;
|
||||
final width = dayWidth / e.totalColumns;
|
||||
return Positioned(
|
||||
left: dayIdx * dayWidth + e.column * width,
|
||||
top: startHour * hourHeight,
|
||||
width: width,
|
||||
height: duration * hourHeight,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_selectedEvent = e.event;
|
||||
_selectedDay =
|
||||
weekStart.add(Duration(days: dayIdx));
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(2),
|
||||
padding: const EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.rouge.withOpacity(0.2),
|
||||
border:
|
||||
Border.all(color: AppColors.rouge),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
e.event.name,
|
||||
style: const TextStyle(
|
||||
color: AppColors.rouge,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
if (_isMultiDayEvent(e.event))
|
||||
Text(
|
||||
'Jour ${_calculateDayNumber(e.event.startDateTime, weekStart.add(Duration(days: dayIdx)))}/${_calculateTotalDays(e.event)}',
|
||||
style: const TextStyle(
|
||||
color: AppColors.rouge,
|
||||
fontSize: 10,
|
||||
),
|
||||
maxLines: 1,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// Méthode pour récupérer les événements pour un jour donné (inclut les multi-jours)
|
||||
List<EventModel> _getEventsForDay(DateTime day) {
|
||||
final dayStart = DateTime(day.year, day.month, day.day, 0, 0);
|
||||
final dayEnd = DateTime(day.year, day.month, day.day, 23, 59, 59);
|
||||
|
||||
return _testEvents.where((event) {
|
||||
return !(event.endDateTime.isBefore(dayStart) ||
|
||||
event.startDateTime.isAfter(dayEnd));
|
||||
}).toList();
|
||||
}
|
||||
|
||||
// Méthodes pour gérer les événements multi-jours
|
||||
bool _isMultiDayEvent(EventModel event) {
|
||||
return event.startDateTime.day != event.endDateTime.day ||
|
||||
event.startDateTime.month != event.endDateTime.month ||
|
||||
event.startDateTime.year != event.endDateTime.year;
|
||||
}
|
||||
|
||||
int _calculateTotalDays(EventModel event) {
|
||||
final startDate = DateTime(event.startDateTime.year,
|
||||
event.startDateTime.month, event.startDateTime.day);
|
||||
final endDate = DateTime(
|
||||
event.endDateTime.year, event.endDateTime.month, event.endDateTime.day);
|
||||
return endDate.difference(startDate).inDays + 1;
|
||||
}
|
||||
|
||||
int _calculateDayNumber(DateTime startDate, DateTime currentDay) {
|
||||
final start = DateTime(startDate.year, startDate.month, startDate.day);
|
||||
final current = DateTime(currentDay.year, currentDay.month, currentDay.day);
|
||||
return current.difference(start).inDays + 1;
|
||||
}
|
||||
|
||||
String _getDayName(int weekday) {
|
||||
switch (weekday) {
|
||||
case DateTime.monday:
|
||||
return 'Lun';
|
||||
case DateTime.tuesday:
|
||||
return 'Mar';
|
||||
case DateTime.wednesday:
|
||||
return 'Mer';
|
||||
case DateTime.thursday:
|
||||
return 'Jeu';
|
||||
case DateTime.friday:
|
||||
return 'Ven';
|
||||
case DateTime.saturday:
|
||||
return 'Sam';
|
||||
case DateTime.sunday:
|
||||
return 'Dim';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
String _getMonthYearString(DateTime weekStart, DateTime weekEnd) {
|
||||
final months = [
|
||||
'',
|
||||
'Janvier',
|
||||
'Février',
|
||||
'Mars',
|
||||
'Avril',
|
||||
'Mai',
|
||||
'Juin',
|
||||
'Juillet',
|
||||
'Août',
|
||||
'Septembre',
|
||||
'Octobre',
|
||||
'Novembre',
|
||||
'Décembre'
|
||||
];
|
||||
if (weekStart.month == weekEnd.month) {
|
||||
return '${months[weekStart.month]} ${weekStart.year}';
|
||||
} else {
|
||||
return '${months[weekStart.month]} - ${months[weekEnd.month]} ${weekEnd.year}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _PositionedEvent {
|
||||
final EventModel event;
|
||||
final DateTime start;
|
||||
final DateTime end;
|
||||
_PositionedEvent(this.event, this.start, this.end);
|
||||
}
|
||||
|
||||
class _PositionedEventWithColumn extends _PositionedEvent {
|
||||
final int column;
|
||||
final int totalColumns;
|
||||
_PositionedEventWithColumn(EventModel event, DateTime start, DateTime end,
|
||||
this.column, this.totalColumns)
|
||||
: super(event, start, end);
|
||||
}
|
||||
|
||||
List<_PositionedEventWithColumn> _assignColumns(List<_PositionedEvent> events) {
|
||||
// Algorithme simple :
|
||||
// - Trier par heure de début
|
||||
// - Pour chaque événement, trouver la première colonne libre
|
||||
// - Attribuer le nombre total de colonnes pour le groupe de chevauchement
|
||||
events.sort((a, b) => a.start.compareTo(b.start));
|
||||
List<_PositionedEventWithColumn> result = [];
|
||||
List<List<_PositionedEventWithColumn>> columns = [];
|
||||
for (final e in events) {
|
||||
bool placed = false;
|
||||
for (int col = 0; col < columns.length; col++) {
|
||||
if (columns[col].isEmpty || !(_overlap(columns[col].last, e))) {
|
||||
columns[col]
|
||||
.add(_PositionedEventWithColumn(e.event, e.start, e.end, col, 0));
|
||||
placed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!placed) {
|
||||
columns.add([
|
||||
_PositionedEventWithColumn(e.event, e.start, e.end, columns.length, 0)
|
||||
]);
|
||||
}
|
||||
}
|
||||
// Mettre à jour le nombre total de colonnes pour chaque événement
|
||||
int totalCols = columns.length;
|
||||
for (final col in columns) {
|
||||
for (final e in col) {
|
||||
result.add(_PositionedEventWithColumn(
|
||||
e.event, e.start, e.end, e.column, totalCols));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool _overlap(_PositionedEvent a, _PositionedEvent b) {
|
||||
return a.end.isAfter(b.start) && a.start.isBefore(b.end);
|
||||
}
|
||||
|
Reference in New Issue
Block a user