From cb253831a029b87a4f0240dde36fff48ce2f3c16 Mon Sep 17 00:00:00 2001 From: Dayron Date: Mon, 3 Nov 2025 15:26:52 +0100 Subject: [PATCH] Enhance trip details view with map options and improved layout --- .../home/show_trip_details_content.dart | 790 +++++++++++++----- lib/components/home/trip_card.dart | 157 ++-- 2 files changed, 628 insertions(+), 319 deletions(-) diff --git a/lib/components/home/show_trip_details_content.dart b/lib/components/home/show_trip_details_content.dart index a03e014..427f892 100644 --- a/lib/components/home/show_trip_details_content.dart +++ b/lib/components/home/show_trip_details_content.dart @@ -4,9 +4,9 @@ import 'package:travel_mate/blocs/trip/trip_bloc.dart'; import 'package:travel_mate/blocs/trip/trip_event.dart'; import 'package:travel_mate/components/home/create_trip_content.dart'; import 'package:travel_mate/models/trip.dart'; +import 'package:travel_mate/components/map/map_content.dart'; import 'package:travel_mate/services/error_service.dart'; -import 'package:url_launcher/url_launcher.dart'; // Ajouter cet import -import 'package:travel_mate/components/map/map_content.dart'; // Ajouter cet import si la page carte existe +import 'package:url_launcher/url_launcher.dart'; class ShowTripDetailsContent extends StatefulWidget { final Trip trip; @@ -19,6 +19,14 @@ class ShowTripDetailsContent extends StatefulWidget { class _ShowTripDetailsContentState extends State { final ErrorService _errorService = ErrorService(); + // Calculer les jours restants avant le voyage + int get daysUntilTrip { + final now = DateTime.now(); + final tripStart = widget.trip.startDate; + final difference = tripStart.difference(now).inDays; + return difference > 0 ? difference : 0; + } + // Méthode pour ouvrir la carte interne void _openInternalMap() { Navigator.push( @@ -30,255 +38,387 @@ class _ShowTripDetailsContentState extends State { ); } + // Méthode pour afficher le dialogue de sélection de carte + void _showMapOptions() { + final theme = Theme.of(context); + + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + backgroundColor: theme.dialogBackgroundColor, + title: Text( + 'Ouvrir la carte', + style: theme.textTheme.titleLarge?.copyWith( + color: theme.colorScheme.onSurface, + ), + ), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Choisissez comment vous souhaitez ouvrir la carte :', + style: theme.textTheme.bodyMedium?.copyWith( + color: theme.colorScheme.onSurface, + ), + ), + const SizedBox(height: 20), + // Options centrées verticalement + Column( + children: [ + // Carte de l'application + SizedBox( + width: double.infinity, + child: ElevatedButton.icon( + onPressed: () { + Navigator.pop(context); + _openInternalMap(); + }, + icon: const Icon(Icons.map), + label: const Text('Carte de l\'app'), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 12), + ), + ), + ), + const SizedBox(height: 12), + // Google Maps + SizedBox( + width: double.infinity, + child: ElevatedButton.icon( + onPressed: () { + Navigator.pop(context); + _openGoogleMaps(); + }, + icon: const Icon(Icons.directions), + label: const Text('Google Maps'), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.green, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 12), + ), + ), + ), + const SizedBox(height: 12), + // Waze + SizedBox( + width: double.infinity, + child: ElevatedButton.icon( + onPressed: () { + Navigator.pop(context); + _openWaze(); + }, + icon: const Icon(Icons.navigation), + label: const Text('Waze'), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.orange, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 12), + ), + ), + ), + ], + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: Text( + 'Annuler', + style: TextStyle(color: theme.colorScheme.primary), + ), + ), + ], + ); + }, + ); + } + // Méthode pour ouvrir Google Maps Future _openGoogleMaps() async { final location = Uri.encodeComponent(widget.trip.location); - final url = 'https://www.google.com/maps/search/?api=1&query=$location'; - + try { - final uri = Uri.parse(url); - if (await canLaunchUrl(uri)) { - await launchUrl(uri, mode: LaunchMode.externalApplication); - } else { - // Fallback: essayer l'URL scheme pour l'app mobile - final appUrl = 'comgooglemaps://?q=$location'; - final appUri = Uri.parse(appUrl); - if (await canLaunchUrl(appUri)) { - await launchUrl(appUri); - } else { - // Si rien ne marche, afficher un message d'erreur - if (mounted) { - _errorService.showError( - message: - 'Impossible d\'ouvrir Google Maps, veuillez vérifier que l\'application est installée.', - ); - } - } + // Essayer d'abord l'URL scheme pour l'app mobile + final appUrl = 'comgooglemaps://?q=$location'; + final appUri = Uri.parse(appUrl); + if (await canLaunchUrl(appUri)) { + await launchUrl(appUri); + return; } + + // Fallback vers l'URL web + final webUrl = 'https://www.google.com/maps/search/?api=1&query=$location'; + final webUri = Uri.parse(webUrl); + if (await canLaunchUrl(webUri)) { + await launchUrl(webUri, mode: LaunchMode.externalApplication); + return; + } + + _errorService.showError( + message: 'Impossible d\'ouvrir Google Maps. Vérifiez que l\'application est installée.', + ); } catch (e) { - if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Erreur lors de l\'ouverture de Google Maps: $e'), - backgroundColor: Colors.red, - ), - ); + _errorService.showError( + message: 'Erreur lors de l\'ouverture de Google Maps', + ); + } + } + + // Méthode pour ouvrir Waze + Future _openWaze() async { + final location = Uri.encodeComponent(widget.trip.location); + + try { + // Essayer d'abord l'URL scheme pour l'app mobile + final appUrl = 'waze://?q=$location'; + final appUri = Uri.parse(appUrl); + if (await canLaunchUrl(appUri)) { + await launchUrl(appUri); + return; } + + // Fallback vers l'URL web + final webUrl = 'https://waze.com/ul?q=$location'; + final webUri = Uri.parse(webUrl); + if (await canLaunchUrl(webUri)) { + await launchUrl(webUri, mode: LaunchMode.externalApplication); + return; + } + + _errorService.showError( + message: 'Impossible d\'ouvrir Waze. Vérifiez que l\'application est installée.', + ); + } catch (e) { + _errorService.showError( + message: 'Erreur lors de l\'ouverture de Waze', + ); } } @override Widget build(BuildContext context) { - final isDarkMode = Theme.of(context).brightness == Brightness.dark; - final textColor = isDarkMode ? Colors.white : Colors.black; - final secondaryTextColor = isDarkMode ? Colors.white70 : Colors.grey[600]; - + final theme = Theme.of(context); + final isDarkMode = theme.brightness == Brightness.dark; + return Scaffold( - appBar: AppBar(title: Text(widget.trip.title)), - body: Padding( - padding: const EdgeInsets.all(16.0), + backgroundColor: isDarkMode ? theme.scaffoldBackgroundColor : Colors.grey[50], + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + leading: IconButton( + icon: Icon(Icons.arrow_back, color: theme.colorScheme.onSurface), + onPressed: () => Navigator.pop(context), + ), + title: Text( + widget.trip.title, + style: theme.textTheme.titleLarge?.copyWith( + color: theme.colorScheme.onSurface, + fontWeight: FontWeight.w600, + ), + ), + actions: [ + IconButton( + icon: Icon(Icons.more_vert, color: theme.colorScheme.onSurface), + onPressed: () => _showOptionsMenu(), + ), + ], + ), + body: SingleChildScrollView( child: Column( - crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - widget.trip.title, - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: textColor, - ), - ), - SizedBox(height: 8), - Text(widget.trip.description, style: TextStyle(color: textColor)), - SizedBox(height: 16), - Row( - children: [ - Icon(Icons.location_on, size: 16, color: secondaryTextColor), - SizedBox(width: 8), - Text( - widget.trip.location, - style: TextStyle(fontSize: 14, color: secondaryTextColor), - ), - ], - ), - SizedBox(height: 8), - Row( - children: [ - Icon(Icons.calendar_today, size: 16, color: secondaryTextColor), - SizedBox(width: 8), - Text( - '${widget.trip.startDate.day}/${widget.trip.startDate.month}/${widget.trip.startDate.year} - ${widget.trip.endDate.day}/${widget.trip.endDate.month}/${widget.trip.endDate.year}', - style: TextStyle(fontSize: 14, color: secondaryTextColor), - ), - ], - ), - SizedBox(height: 8), - Row( - children: [ - Icon(Icons.group, size: 16, color: secondaryTextColor), - SizedBox(width: 8), - Text( - '${widget.trip.participants.length} participant${widget.trip.participants.length > 1 ? 's' : ''}', - style: TextStyle(fontSize: 14, color: secondaryTextColor), - ), - ], - ), - SizedBox(height: 16), - Text( - 'Budget: ${widget.trip.budget ?? 'N/A'}€', - style: TextStyle(color: textColor), - ), - SizedBox(height: 24), - - // Section des boutons de carte - Text( - 'Explorer la destination', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: textColor, - ), - ), - SizedBox(height: 16), - - // Boutons en ligne - Row( - children: [ - // Bouton carte interne - Expanded( - child: ElevatedButton.icon( - onPressed: _openInternalMap, - icon: Icon(Icons.map, color: Colors.white), - label: Text( - 'Voir sur la carte', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.w500, - ), - ), - style: ElevatedButton.styleFrom( - backgroundColor: const Color.fromARGB(255, 102, 102, 102), - padding: EdgeInsets.symmetric(vertical: 12), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - ), - ), - SizedBox(width: 12), - - // Bouton Google Maps - Expanded( - child: ElevatedButton.icon( - onPressed: _openGoogleMaps, - icon: Icon(Icons.directions, color: Colors.white), - label: Text( - 'Google Maps', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.w500, - ), - ), - style: ElevatedButton.styleFrom( - backgroundColor: const Color.fromARGB(255, 102, 102, 102), - padding: EdgeInsets.symmetric(vertical: 12), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - ), - ), - ], - ), - - SizedBox(height: 24), - - // Boutons existants - SizedBox( + // Image du voyage + Container( + height: 250, width: double.infinity, - height: 50, - child: ElevatedButton( - onPressed: () async { - final result = await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - CreateTripContent(tripToEdit: widget.trip), - ), - ); - - if (result == true && mounted) { - Navigator.pop(context, true); // Retour avec flag - } - }, - style: ElevatedButton.styleFrom( - backgroundColor: Color.fromARGB(255, 0, 123, 255), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), + margin: const EdgeInsets.all(16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha:0.1), + blurRadius: 10, + offset: const Offset(0, 5), ), - ), - child: Text( - 'Modifier les informations du voyage', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(16), + child: widget.trip.imageUrl != null && widget.trip.imageUrl!.isNotEmpty + ? Image.network( + widget.trip.imageUrl!, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) => _buildPlaceholderImage(), + ) + : _buildPlaceholderImage(), ), ), - SizedBox(height: 16), - SizedBox( - width: double.infinity, - height: 50, - child: ElevatedButton( - onPressed: () { - showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text('Confirmer la suppression'), - content: Text( - 'Êtes-vous sûr de vouloir supprimer ce voyage ? Cette action est irréversible.', + + // Contenu principal + Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Section "Départ dans X jours" + Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + decoration: BoxDecoration( + color: theme.cardColor, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: isDarkMode + ? Colors.white.withValues(alpha:0.1) + : Colors.black.withValues(alpha:0.1), + width: 1, ), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: Text('Annuler'), - ), - TextButton( - onPressed: () { - context.read().add( - TripDeleteRequested(tripId: widget.trip.id!), - ); - Navigator.pop(context); // Fermer le dialogue - Navigator.pop( - context, - true, - ); // Retourner à l'écran précédent - }, - child: Text( - 'Supprimer', - style: TextStyle(color: Colors.red), - ), + boxShadow: [ + BoxShadow( + color: isDarkMode + ? Colors.black.withValues(alpha:0.3) + : Colors.black.withValues(alpha:0.1), + blurRadius: isDarkMode ? 8 : 5, + offset: const Offset(0, 2), + ), + ], + ), + child: Row( + children: [ + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: Colors.teal.withValues(alpha:0.1), + borderRadius: BorderRadius.circular(8), + ), + child: Icon( + Icons.flight_takeoff, + color: Colors.teal, + size: 20, + ), + ), + const SizedBox(width: 12), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Départ dans', + style: theme.textTheme.bodySmall?.copyWith( + color: theme.colorScheme.onSurface.withValues(alpha:0.6), + ), + ), + Text( + daysUntilTrip > 0 ? '$daysUntilTrip Jours' : 'Voyage terminé', + style: theme.textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + color: theme.colorScheme.onSurface, + ), + ), + Text( + widget.trip.formattedDates, + style: theme.textTheme.bodySmall?.copyWith( + color: theme.colorScheme.onSurface.withValues(alpha:0.6), + ), + ), + ], ), ], ), - ); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.red, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), ), - ), - child: Text( - 'Supprimer le voyage', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 16, + + const SizedBox(height: 24), + + // Section Participants + Text( + 'Participants', + style: theme.textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + color: theme.colorScheme.onSurface, + ), ), - ), + const SizedBox(height: 12), + + Row( + children: [ + // Avatars des participants (limité à 4 + bouton add) + ...List.generate( + widget.trip.totalParticipants > 4 ? 4 : widget.trip.totalParticipants, + (index) => Container( + margin: const EdgeInsets.only(right: 8), + child: CircleAvatar( + radius: 25, + backgroundColor: Colors.blue[100], + child: Icon( + Icons.person, + color: Colors.blue[600], + size: 24, + ), + ), + ), + ), + + // Bouton d'ajout si moins de 4 participants affichés + if (widget.trip.totalParticipants <= 4) + Container( + width: 50, + height: 50, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Colors.grey[300]!, + width: 2, + style: BorderStyle.solid, + ), + ), + child: Icon( + Icons.add, + color: Colors.grey[400], + size: 24, + ), + ), + ], + ), + + const SizedBox(height: 32), + + // Grille d'actions + GridView.count( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + crossAxisCount: 2, + childAspectRatio: 1.5, + crossAxisSpacing: 16, + mainAxisSpacing: 16, + children: [ + _buildActionButton( + icon: Icons.calendar_today, + title: 'Calendrier', + color: Colors.blue, + onTap: () => _showComingSoon('Calendrier'), + ), + _buildActionButton( + icon: Icons.local_activity, + title: 'Activités', + color: Colors.green, + onTap: () => _showComingSoon('Activités'), + ), + _buildActionButton( + icon: Icons.account_balance_wallet, + title: 'Dépenses', + color: Colors.orange, + onTap: () => _showComingSoon('Dépenses'), + ), + _buildActionButton( + icon: Icons.map, + title: 'Ouvrir la carte', + color: Colors.purple, + onTap: _showMapOptions, + ), + ], + ), + ], ), ), ], @@ -286,4 +426,202 @@ class _ShowTripDetailsContentState extends State { ), ); } + + Widget _buildPlaceholderImage() { + return Container( + color: Colors.grey[200], + child: const Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.location_city, + size: 48, + color: Colors.grey, + ), + SizedBox(height: 8), + Text( + 'Aucune image', + style: TextStyle( + color: Colors.grey, + fontSize: 14, + ), + ), + ], + ), + ), + ); + } + + Widget _buildActionButton({ + required IconData icon, + required String title, + required Color color, + required VoidCallback onTap, + }) { + final theme = Theme.of(context); + final isDarkMode = theme.brightness == Brightness.dark; + + return InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(12), + child: Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: theme.cardColor, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: isDarkMode + ? Colors.white.withValues(alpha:0.1) + : Colors.black.withValues(alpha:0.1), + width: 1, + ), + boxShadow: [ + BoxShadow( + color: isDarkMode + ? Colors.black.withValues(alpha:0.3) + : Colors.black.withValues(alpha:0.1), + blurRadius: isDarkMode ? 8 : 5, + offset: const Offset(0, 2), + ), + ], + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: color.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(8), + ), + child: Icon( + icon, + color: color, + size: 24, + ), + ), + const SizedBox(height: 8), + Text( + title, + style: theme.textTheme.bodySmall?.copyWith( + fontWeight: FontWeight.w600, + color: theme.colorScheme.onSurface, + ), + textAlign: TextAlign.center, + ), + ], + ), + ), + ); + } + + void _showComingSoon(String feature) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('$feature - Fonctionnalité à venir'), + backgroundColor: Colors.blue, + ), + ); + } + + void _showOptionsMenu() { + final theme = Theme.of(context); + + showModalBottomSheet( + context: context, + backgroundColor: theme.bottomSheetTheme.backgroundColor, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + builder: (context) => Container( + padding: const EdgeInsets.all(20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + leading: Icon(Icons.edit, color: theme.colorScheme.primary), + title: Text( + 'Modifier le voyage', + style: theme.textTheme.bodyLarge?.copyWith( + color: theme.colorScheme.onSurface, + ), + ), + onTap: () async { + Navigator.pop(context); + final result = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + CreateTripContent(tripToEdit: widget.trip), + ), + ); + if (result == true && mounted) { + Navigator.pop(context, true); + } + }, + ), + ListTile( + leading: const Icon(Icons.delete, color: Colors.red), + title: Text( + 'Supprimer le voyage', + style: theme.textTheme.bodyLarge?.copyWith( + color: theme.colorScheme.onSurface, + ), + ), + onTap: () { + Navigator.pop(context); + _showDeleteConfirmation(); + }, + ), + ], + ), + ), + ); + } + + void _showDeleteConfirmation() { + final theme = Theme.of(context); + + showDialog( + context: context, + builder: (context) => AlertDialog( + backgroundColor: theme.dialogBackgroundColor, + title: Text( + 'Confirmer la suppression', + style: theme.textTheme.titleLarge?.copyWith( + color: theme.colorScheme.onSurface, + ), + ), + content: Text( + 'Êtes-vous sûr de vouloir supprimer ce voyage ? Cette action est irréversible.', + style: theme.textTheme.bodyMedium?.copyWith( + color: theme.colorScheme.onSurface, + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: Text( + 'Annuler', + style: TextStyle(color: theme.colorScheme.primary), + ), + ), + TextButton( + onPressed: () { + context.read().add( + TripDeleteRequested(tripId: widget.trip.id!), + ); + Navigator.pop(context); + Navigator.pop(context, true); + }, + child: const Text( + 'Supprimer', + style: TextStyle(color: Colors.red), + ), + ), + ], + ), + ); + } } diff --git a/lib/components/home/trip_card.dart b/lib/components/home/trip_card.dart index e511f96..efd6c75 100644 --- a/lib/components/home/trip_card.dart +++ b/lib/components/home/trip_card.dart @@ -165,105 +165,76 @@ class _TripCardState extends State { width: double.infinity, child: _buildImageWidget(), ), - ), // Informations du voyage - Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - widget.trip.title, - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: textColor, + ), + // Informations du voyage + Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Titre du voyage + Text( + widget.trip.title, + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: textColor, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - const SizedBox(height: 4), - Row( - children: [ - Icon(Icons.location_on, size: 16, color: secondaryTextColor), - const SizedBox(width: 4), - Expanded( - child: Text( - widget.trip.location, - style: TextStyle(color: secondaryTextColor), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - const SizedBox(height: 8), - Text( - widget.trip.description, - style: TextStyle(color: secondaryTextColor, fontSize: 12), - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - const SizedBox(height: 12), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Dates', - style: TextStyle( - color: secondaryTextColor, - fontSize: 10, - fontWeight: FontWeight.w500, - ), - ), - Text( - widget.trip.formattedDates, - style: TextStyle(color: textColor, fontSize: 12), - ), - ], - ), - Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - 'Participants', - style: TextStyle( - color: secondaryTextColor, - fontSize: 10, - fontWeight: FontWeight.w500, - ), - ), - Text( - '${widget.trip.totalParticipants - 1} personne${widget.trip.totalParticipants > 1 ? 's' : ''}', - style: TextStyle(color: textColor, fontSize: 12), - ), - ], - ), - ], - ), - if (widget.trip.budget != null && widget.trip.budget! > 0) ...[ const SizedBox(height: 8), - Container( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), - decoration: BoxDecoration( - color: Colors.green.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(6), - ), - child: Text( - 'Budget: ${widget.trip.budget!.toStringAsFixed(0)}€', - style: TextStyle( - color: Colors.green[700], - fontSize: 11, - fontWeight: FontWeight.w500, + + // Dates, participants et bouton voir + Row( + children: [ + // Colonne avec dates et participants + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Dates + Text( + widget.trip.formattedDates, + style: TextStyle( + color: secondaryTextColor, + fontSize: 14, + ), + ), + const SizedBox(height: 4), + // Participants + Text( + '${widget.trip.totalParticipants - 1} participant${widget.trip.totalParticipants > 1 ? 's' : ''}', + style: TextStyle( + color: secondaryTextColor, + fontSize: 14, + ), + ), + ], + ), ), - ), + + // Bouton Voir + ElevatedButton( + onPressed: widget.onTap, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + ), + child: const Text( + 'Voir', + style: TextStyle(fontSize: 14), + ), + ), + ], ), ], - ], + ), ), - ), ], ), ),