import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:table_calendar/table_calendar.dart'; import 'package:intl/intl.dart'; import '../../../models/trip.dart'; import '../../../models/activity.dart'; import '../../../blocs/activity/activity_bloc.dart'; import '../../../blocs/activity/activity_state.dart'; import '../../../blocs/activity/activity_event.dart'; class CalendarPage extends StatefulWidget { final Trip trip; const CalendarPage({super.key, required this.trip}); @override State createState() => _CalendarPageState(); } class _CalendarPageState extends State { late DateTime _focusedDay; DateTime? _selectedDay; CalendarFormat _calendarFormat = CalendarFormat.week; @override void initState() { super.initState(); _focusedDay = widget.trip.startDate; _selectedDay = _focusedDay; } List _getActivitiesForDay(DateTime day, List activities) { return activities.where((activity) { if (activity.date == null) return false; return isSameDay(activity.date, day); }).toList(); } Future _selectTimeAndSchedule(Activity activity, DateTime date) async { final TimeOfDay? pickedTime = await showTimePicker( context: context, initialTime: TimeOfDay.now(), builder: (BuildContext context, Widget? child) { return MediaQuery( data: MediaQuery.of(context).copyWith(alwaysUse24HourFormat: true), child: child!, ); }, ); if (pickedTime != null && mounted) { final scheduledDate = DateTime( date.year, date.month, date.day, pickedTime.hour, pickedTime.minute, ); context.read().add( UpdateActivityDate( tripId: widget.trip.id!, activityId: activity.id, date: scheduledDate, ), ); } } @override Widget build(BuildContext context) { final theme = Theme.of(context); final isDarkMode = theme.brightness == Brightness.dark; return Scaffold( backgroundColor: isDarkMode ? theme.scaffoldBackgroundColor : Colors.white, appBar: AppBar( title: Text( widget.trip.title, style: theme.textTheme.titleLarge?.copyWith( fontWeight: FontWeight.bold, ), ), centerTitle: true, backgroundColor: Colors.transparent, elevation: 0, leading: IconButton( icon: Icon(Icons.arrow_back_ios, color: theme.colorScheme.onSurface), onPressed: () => Navigator.pop(context), ), actions: [ IconButton( icon: Icon(Icons.people, color: theme.colorScheme.onSurface), onPressed: () {}, // TODO: Show participants ), ], ), body: BlocBuilder( builder: (context, state) { if (state is ActivityLoading) { return const Center(child: CircularProgressIndicator()); } List allActivities = []; if (state is ActivityLoaded) { allActivities = state.activities; } else if (state is ActivitySearchResults) { // Fallback if we are in search state } // Filter approved activities final approvedActivities = allActivities.where((a) { return a.isApprovedByAllParticipants([ ...widget.trip.participants, widget.trip.createdBy, ]); }).toList(); final scheduledActivities = approvedActivities .where((a) => a.date != null) .toList(); final unscheduledActivities = approvedActivities .where((a) => a.date == null) .toList(); final selectedActivities = _getActivitiesForDay( _selectedDay ?? _focusedDay, scheduledActivities, ); // Sort by time selectedActivities.sort((a, b) => a.date!.compareTo(b.date!)); return Column( children: [ // Calendar Strip Container( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: isDarkMode ? theme.cardColor : Colors.grey[100], borderRadius: BorderRadius.circular(12), ), child: TableCalendar( firstDay: DateTime.now().subtract(const Duration(days: 365)), lastDay: DateTime.now().add(const Duration(days: 365)), focusedDay: _focusedDay, calendarFormat: _calendarFormat, headerStyle: HeaderStyle( formatButtonVisible: false, titleCentered: true, titleTextStyle: theme.textTheme.titleLarge!.copyWith( fontWeight: FontWeight.bold, ), leftChevronIcon: const Icon(Icons.chevron_left), rightChevronIcon: const Icon(Icons.chevron_right), ), calendarStyle: CalendarStyle( todayDecoration: BoxDecoration( color: Colors.blue.withValues(alpha: 0.3), shape: BoxShape.circle, ), selectedDecoration: const BoxDecoration( color: Colors.blue, shape: BoxShape.circle, ), markerDecoration: const BoxDecoration( color: Colors.purple, shape: BoxShape.circle, ), ), selectedDayPredicate: (day) { return isSameDay(_selectedDay, day); }, onDaySelected: (selectedDay, focusedDay) { setState(() { _selectedDay = selectedDay; _focusedDay = focusedDay; }); }, onPageChanged: (focusedDay) { _focusedDay = focusedDay; }, eventLoader: (day) { return _getActivitiesForDay(day, scheduledActivities); }, ), ), const SizedBox(height: 16), // Timeline View Expanded( child: SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Timeline if (selectedActivities.isEmpty) Padding( padding: const EdgeInsets.symmetric(vertical: 32), child: Center( child: Text( 'Aucune activité prévue ce jour', style: theme.textTheme.bodyLarge?.copyWith( color: theme.colorScheme.onSurface.withValues( alpha: 0.5, ), ), ), ), ) else ...selectedActivities.map((activity) { return _buildTimelineItem(activity, theme); }), const SizedBox(height: 32), // Unscheduled Activities Section Text( 'Activités à ajouter', style: theme.textTheme.titleLarge?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), if (unscheduledActivities.isEmpty) Padding( padding: const EdgeInsets.symmetric(vertical: 16), child: Text( 'Toutes les activités sont planifiées !', style: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.onSurface.withValues( alpha: 0.5, ), ), ), ) else ...unscheduledActivities.map((activity) { return _buildUnscheduledActivityCard(activity, theme); }), const SizedBox(height: 32), ], ), ), ), ], ); }, ), ); } Widget _buildTimelineItem(Activity activity, ThemeData theme) { final timeFormat = DateFormat('HH:mm'); // 10:00 final endTimeFormat = DateFormat('HH:mm'); // 11:30 (simulated duration) // Simulate duration (1h30) final endTime = activity.date!.add(const Duration(hours: 1, minutes: 30)); return IntrinsicHeight( child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Time Column SizedBox( width: 50, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '${activity.date!.hour}h', style: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.onSurface.withValues(alpha: 0.5), ), ), ], ), ), // Timeline Line // Expanded(child: Container()), // Placeholder for line if needed // Activity Card Expanded( child: Container( margin: const EdgeInsets.only(bottom: 24), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: _getCategoryColor( activity.category, ).withValues(alpha: 0.1), borderRadius: BorderRadius.circular(12), border: Border( left: BorderSide( color: _getCategoryColor(activity.category), width: 4, ), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( activity.name, style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, color: theme.colorScheme.onSurface, ), ), const SizedBox(height: 4), Text( '${timeFormat.format(activity.date!)} - ${endTimeFormat.format(endTime)}', style: theme.textTheme.bodyMedium?.copyWith( color: _getCategoryColor(activity.category), fontWeight: FontWeight.w500, ), ), ], ), ), ), ], ), ); } Widget _buildUnscheduledActivityCard(Activity activity, ThemeData theme) { return Container( margin: const EdgeInsets.only(bottom: 12), decoration: BoxDecoration( color: theme.cardColor, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.05), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: ListTile( contentPadding: const EdgeInsets.all(16), leading: Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: _getCategoryColor(activity.category).withValues(alpha: 0.1), shape: BoxShape.circle, ), child: Icon( _getCategoryIcon(activity.category), color: _getCategoryColor(activity.category), ), ), title: Text( activity.name, style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), subtitle: Text( activity.category, style: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.onSurface.withValues(alpha: 0.6), ), ), trailing: IconButton( icon: const Icon(Icons.grid_view), // Drag handle icon onPressed: () { if (_selectedDay != null) { _selectTimeAndSchedule(activity, _selectedDay!); } }, ), ), ); } Color _getCategoryColor(String category) { // Simple mapping based on category name // You might want to use the enum if possible, but category is String in Activity model if (category.toLowerCase().contains('musée') || category.toLowerCase().contains('museum')) return Colors.blue; if (category.toLowerCase().contains('restaurant') || category.toLowerCase().contains('food')) return Colors.orange; if (category.toLowerCase().contains('nature') || category.toLowerCase().contains('park')) return Colors.green; if (category.toLowerCase().contains('photo') || category.toLowerCase().contains('attraction')) return Colors.purple; if (category.toLowerCase().contains('détente') || category.toLowerCase().contains('relax')) return Colors.pink; return Colors.teal; } IconData _getCategoryIcon(String category) { if (category.toLowerCase().contains('musée')) return Icons.museum; if (category.toLowerCase().contains('restaurant')) return Icons.restaurant; if (category.toLowerCase().contains('nature')) return Icons.nature; if (category.toLowerCase().contains('photo')) return Icons.camera_alt; if (category.toLowerCase().contains('détente')) return Icons.icecream; // Gelato icon :) return Icons.place; } }