import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:travel_mate/components/home/create_trip_content.dart'; import '../home/show_trip_details_content.dart'; import '../../blocs/user/user_bloc.dart'; import '../../blocs/user/user_state.dart'; import '../../blocs/trip/trip_bloc.dart'; import '../../blocs/trip/trip_state.dart'; import '../../blocs/trip/trip_event.dart'; import '../../data/models/trip.dart'; class HomeContent extends StatefulWidget { const HomeContent({super.key}); @override State createState() => _HomeContentState(); } class _HomeContentState extends State { @override void initState() { super.initState(); // Charger les trips quand le widget est initialisé _loadTripsIfUserLoaded(); } void _loadTripsIfUserLoaded() { final userState = context.read().state; if (userState is UserLoaded) { context.read().add(TripLoadRequested(userId: userState.user.id)); } } @override Widget build(BuildContext context) { return BlocBuilder( builder: (context, userState) { if (userState is UserLoading) { return Scaffold( body: Center( child: CircularProgressIndicator(), ), ); } if (userState is UserError) { return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.error, size: 64, color: Colors.red), SizedBox(height: 16), Text('Erreur: ${userState.message}'), ], ), ), ); } if (userState is! UserLoaded) { return Scaffold( body: Center( child: Text('Veuillez vous connecter'), ), ); } final user = userState.user; // Charger les trips si ce n'est pas déjà fait if (context.read().state is TripInitial) { context.read().add(TripLoadRequested(userId: user.id)); } return BlocConsumer( listener: (context, tripState) { if (tripState is TripOperationSuccess) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(tripState.message), backgroundColor: Colors.green, ), ); // Recharger les trips après une opération réussie context.read().add(TripLoadRequested(userId: user.id)); } else if (tripState is TripError) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(tripState.message), backgroundColor: Colors.red, ), ); } }, builder: (context, tripState) { return Scaffold( body: RefreshIndicator( onRefresh: () async { context.read().add(TripLoadRequested(userId: user.id)); }, child: SingleChildScrollView( physics: AlwaysScrollableScrollPhysics(), padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header de bienvenue Text( 'Bonjour ${user.prenom} !', style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold), ), SizedBox(height: 8), Text( 'Vos voyages', style: TextStyle(fontSize: 16, color: Colors.grey[600]), ), SizedBox(height: 20), // Contenu principal basé sur l'état du TripBloc if (tripState is TripLoading) _buildLoadingState() else if (tripState is TripError) _buildErrorState(tripState.message, user.id) else if (tripState is TripLoaded) tripState.trips.isEmpty ? _buildEmptyState() : _buildTripsList(tripState.trips) else _buildEmptyState(), // Espacement en bas pour éviter que le FAB cache le contenu const SizedBox(height: 80), ], ), ), ), // FloatingActionButton floatingActionButton: FloatingActionButton( onPressed: () async { final result = await Navigator.push( context, MaterialPageRoute(builder: (context) => CreateTripContent()), ); if (result == true) { // Recharger les trips context.read().add(TripLoadRequested(userId: user.id)); } }, backgroundColor: Theme.of(context).colorScheme.primary, foregroundColor: Colors.white, child: const Icon(Icons.add), ), floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, ); }, ); }, ); } Widget _buildLoadingState() { return Center( child: Padding( padding: EdgeInsets.all(32), child: CircularProgressIndicator(), ), ); } Widget _buildErrorState(String error, String userId) { return Center( child: Padding( padding: EdgeInsets.all(32), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.error, size: 64, color: Colors.red), SizedBox(height: 16), Text('Erreur lors du chargement des voyages'), SizedBox(height: 8), Text( error, textAlign: TextAlign.center, style: TextStyle(fontSize: 12, color: Colors.grey), ), SizedBox(height: 16), ElevatedButton( onPressed: () { context.read().add(TripLoadRequested(userId: userId)); }, child: Text('Réessayer'), ), ], ), ), ); } Widget _buildEmptyState() { return Center( child: Padding( padding: EdgeInsets.all(32), child: Column( children: [ Icon( Icons.travel_explore, size: 64, color: Colors.grey[400], ), SizedBox(height: 16), Text( 'Aucun voyage pour le moment', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w500, color: Colors.grey[600], ), ), SizedBox(height: 8), Text( 'Créez votre premier voyage en appuyant sur le bouton +', textAlign: TextAlign.center, style: TextStyle( fontSize: 14, color: Colors.grey[500], ), ), ], ), ), ); } Widget _buildTripsList(List trips) { return Column( children: trips.map((trip) => _buildTravelCard(trip)).toList(), ); } Widget _buildTravelCard(Trip trip) { final colors = [Colors.blue, Colors.orange, Colors.green, Colors.purple, Colors.red]; final color = colors[trip.title.hashCode.abs() % colors.length]; final isDarkMode = Theme.of(context).brightness == Brightness.dark; final textColor = isDarkMode ? Colors.white : Colors.black; final secondaryTextColor = isDarkMode ? Colors.white70 : Colors.grey[700]; final iconColor = isDarkMode ? Colors.white70 : Colors.grey[600]; return Card( elevation: 4, margin: const EdgeInsets.only(bottom: 16), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: InkWell( onTap: () { Navigator.push( context, MaterialPageRoute(builder: (context) => ShowTripDetailsContent(trip: trip)), ); }, borderRadius: BorderRadius.circular(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Image d'en-tête avec titre overlay Container( height: 150, decoration: BoxDecoration( borderRadius: const BorderRadius.vertical(top: Radius.circular(12)), gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ color.withValues(alpha: 0.7), color.withValues(alpha: 0.9), ], ), ), child: Stack( children: [ Container( width: double.infinity, decoration: BoxDecoration( borderRadius: const BorderRadius.vertical( top: Radius.circular(12), ), color: color.withValues(alpha: 0.3), ), ), Positioned( bottom: 16, left: 16, right: 16, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( trip.title, style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: Colors.white, ), ), const SizedBox(height: 4), Row( children: [ const Icon( Icons.location_on, color: Colors.white, size: 16, ), const SizedBox(width: 4), Expanded( child: Text( trip.location, style: const TextStyle( fontSize: 14, color: Colors.white, ), overflow: TextOverflow.ellipsis, ), ), ], ), ], ), ), ], ), ), // Contenu de la carte Padding( padding: EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Description if (trip.description.isNotEmpty) ...[ Text( trip.description, style: TextStyle( fontSize: 14, color: secondaryTextColor, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), SizedBox(height: 12), ], // Dates Row( children: [ Icon( Icons.calendar_today, size: 16, color: iconColor, ), SizedBox(width: 8), Text( '${trip.startDate.day}/${trip.startDate.month}/${trip.startDate.year} - ${trip.endDate.day}/${trip.endDate.month}/${trip.endDate.year}', style: TextStyle( fontSize: 14, color: iconColor, fontWeight: FontWeight.w500, ), ), ], ), SizedBox(height: 12), // Participants Row( children: [ Icon(Icons.group, size: 16, color: iconColor), SizedBox(width: 8), Text( '${trip.participants.length} participant${trip.participants.length > 1 ? 's' : ''}', style: TextStyle( fontSize: 14, color: iconColor, fontWeight: FontWeight.w500, ), ), ], ), SizedBox(height: 12), // Budget et statut Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ if (trip.budget! > 0) Row( children: [ Icon(Icons.euro, size: 16, color: iconColor), SizedBox(width: 8), Text( 'Budget: ${trip.budget!.toStringAsFixed(2)}€', style: TextStyle( fontSize: 14, color: iconColor, fontWeight: FontWeight.w500, ), ), ], ), Container( padding: EdgeInsets.symmetric( horizontal: 8, vertical: 4, ), decoration: BoxDecoration( color: _getStatusColor(trip).withValues(alpha: 0.1), borderRadius: BorderRadius.circular(12), ), child: Text( _getStatusText(trip), style: TextStyle( fontSize: 12, color: _getStatusColor(trip), fontWeight: FontWeight.w500, ), ), ), ], ), ], ), ), ], ), ), ); } Color _getStatusColor(Trip trip) { final now = DateTime.now(); if (trip.endDate.isBefore(now)) { return Colors.grey; } else if (trip.startDate.isBefore(now) && trip.endDate.isAfter(now)) { return Colors.green; } else { return Colors.blue; } } String _getStatusText(Trip trip) { final now = DateTime.now(); if (trip.endDate.isBefore(now)) { return 'Terminé'; } else if (trip.startDate.isBefore(now) && trip.endDate.isAfter(now)) { return 'En cours'; } else { return 'À venir'; } } }