import 'package:flutter/material.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:travel_mate/models/trip.dart'; import 'package:travel_mate/services/place_image_service.dart'; import 'package:travel_mate/repositories/trip_repository.dart'; class TripCard extends StatefulWidget { final Trip trip; final VoidCallback? onTap; const TripCard({ super.key, required this.trip, this.onTap, }); @override State createState() => _TripCardState(); } class _TripCardState extends State { final PlaceImageService _placeImageService = PlaceImageService(); final TripRepository _tripRepository = TripRepository(); String? _currentImageUrl; bool _isLoadingImage = false; bool _hasTriedLoading = false; @override void initState() { super.initState(); _currentImageUrl = widget.trip.imageUrl; // Si aucune image n'est disponible, essayer de la charger if (_currentImageUrl == null || _currentImageUrl!.isEmpty) { _loadImageForTrip(); } } Future _loadImageForTrip() async { if (_hasTriedLoading || _isLoadingImage) return; setState(() { _isLoadingImage = true; _hasTriedLoading = true; }); try { print('TripCard: Tentative de chargement d\'image pour ${widget.trip.location}'); // D'abord vérifier si une image existe déjà dans le Storage String? imageUrl = await _placeImageService.getExistingImageUrl(widget.trip.location); // Si aucune image n'existe, en télécharger une nouvelle if (imageUrl == null) { print('TripCard: Aucune image existante, téléchargement via Google Places...'); imageUrl = await _placeImageService.getPlaceImageUrl(widget.trip.location); } if (mounted && imageUrl != null) { setState(() { _currentImageUrl = imageUrl; _isLoadingImage = false; }); // Mettre à jour le voyage dans la base de données avec l'imageUrl _updateTripWithImage(imageUrl); print('TripCard: Image chargée avec succès: $imageUrl'); } else { setState(() { _isLoadingImage = false; }); } } catch (e) { print('TripCard: Erreur lors du chargement de l\'image: $e'); if (mounted) { setState(() { _isLoadingImage = false; }); } } } Future _updateTripWithImage(String imageUrl) async { try { if (widget.trip.id != null) { // Créer une copie du voyage avec la nouvelle imageUrl final updatedTrip = widget.trip.copyWith( imageUrl: imageUrl, updatedAt: DateTime.now(), ); // Mettre à jour dans la base de données await _tripRepository.updateTrip(widget.trip.id!, updatedTrip); print('TripCard: Voyage mis à jour avec la nouvelle image dans la base de données'); } } catch (e) { print('TripCard: Erreur lors de la mise à jour du voyage: $e'); // En cas d'erreur, on continue sans échec - l'image reste affichée localement } } Widget _buildImageWidget() { final isDarkMode = Theme.of(context).brightness == Brightness.dark; if (_isLoadingImage) { return Container( color: isDarkMode ? Colors.grey[700] : Colors.grey[200], child: const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator(), SizedBox(height: 8), Text('Chargement de l\'image...'), ], ), ), ); } if (_currentImageUrl != null && _currentImageUrl!.isNotEmpty) { return CachedNetworkImage( imageUrl: _currentImageUrl!, fit: BoxFit.cover, placeholder: (context, url) => Container( color: Colors.grey[200], child: const Center( child: CircularProgressIndicator(), ), ), errorWidget: (context, url, error) => _buildPlaceholderImage(isDarkMode), ); } return _buildPlaceholderImage(isDarkMode); } @override Widget build(BuildContext context) { final isDarkMode = Theme.of(context).brightness == Brightness.dark; final cardColor = isDarkMode ? Colors.grey[800] : Colors.white; final textColor = isDarkMode ? Colors.white : Colors.black; final secondaryTextColor = isDarkMode ? Colors.white70 : Colors.grey[600]; return Card( elevation: 4, margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), color: cardColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Material( color: Colors.transparent, child: InkWell( onTap: widget.onTap, borderRadius: BorderRadius.circular(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Image du voyage ClipRRect( borderRadius: const BorderRadius.vertical(top: Radius.circular(12)), child: SizedBox( height: 200, 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, ), 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, ), ), ), ], ], ), ), ], ), ), ), ); } Widget _buildPlaceholderImage(bool isDarkMode) { return Container( color: isDarkMode ? Colors.grey[700] : Colors.grey[200], child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.location_city, size: 48, color: isDarkMode ? Colors.grey[500] : Colors.grey[400], ), const SizedBox(height: 8), Text( 'Aucune image', style: TextStyle( color: isDarkMode ? Colors.grey[400] : Colors.grey[600], fontSize: 12, ), ), ], ), ), ); } }