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: [ // Titre du voyage Text( widget.trip.title, style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: textColor, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 8), // 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), ), ), ], ), ], ), ), ], ), ), ), ); } 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, ), ), ], ), ), ); } }