import 'package:flutter/material.dart'; import '../../models/activity.dart'; /// Widget représentant une carte d'activité avec système de vote class ActivityCard extends StatelessWidget { final Activity activity; final String currentUserId; final Function(int) onVote; final VoidCallback? onAddToTrip; const ActivityCard({ Key? key, required this.activity, required this.currentUserId, required this.onVote, this.onAddToTrip, }) : super(key: key); @override Widget build(BuildContext context) { final theme = Theme.of(context); final isDarkMode = theme.brightness == Brightness.dark; final userVote = activity.getUserVote(currentUserId); return Container( decoration: BoxDecoration( color: theme.cardColor, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(isDarkMode ? 0.3 : 0.1), blurRadius: 10, offset: const Offset(0, 5), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Image de l'activité if (activity.imageUrl != null) ...[ ClipRRect( borderRadius: const BorderRadius.vertical(top: Radius.circular(16)), child: Stack( children: [ Image.network( activity.imageUrl!, height: 200, width: double.infinity, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return Container( height: 200, width: double.infinity, decoration: BoxDecoration( color: theme.colorScheme.surfaceVariant, borderRadius: const BorderRadius.vertical(top: Radius.circular(16)), ), child: Icon( Icons.image_not_supported, size: 48, color: theme.colorScheme.onSurfaceVariant, ), ); }, ), // Badge catégorie if (activity.category.isNotEmpty) Positioned( top: 12, right: 12, child: Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: _getCategoryColor(activity.category), borderRadius: BorderRadius.circular(12), ), child: Text( activity.category, style: theme.textTheme.bodySmall?.copyWith( color: Colors.white, fontWeight: FontWeight.w500, ), ), ), ), ], ), ), ], // Contenu de la carte Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Titre et rating Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Text( activity.name, style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, color: theme.colorScheme.onSurface, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ), if (activity.rating != null) ...[ const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( color: Colors.amber.withOpacity(0.2), borderRadius: BorderRadius.circular(8), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.star, size: 14, color: Colors.amber), const SizedBox(width: 2), Text( activity.rating!.toStringAsFixed(1), style: theme.textTheme.bodySmall?.copyWith( fontWeight: FontWeight.w500, color: Colors.amber[800], ), ), ], ), ), ], ], ), const SizedBox(height: 8), // Description Text( activity.description, style: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.onSurface.withOpacity(0.7), ), maxLines: 3, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 12), // Informations supplémentaires if (activity.priceLevel != null || activity.address != null) ...[ Row( children: [ if (activity.priceLevel != null) ...[ Icon( Icons.euro, size: 16, color: theme.colorScheme.onSurface.withOpacity(0.5), ), const SizedBox(width: 4), Text( activity.priceLevel!, style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurface.withOpacity(0.7), ), ), if (activity.address != null) ...[ const SizedBox(width: 16), Icon( Icons.location_on, size: 16, color: theme.colorScheme.onSurface.withOpacity(0.5), ), const SizedBox(width: 4), Expanded( child: Text( activity.address!, style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurface.withOpacity(0.7), ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], ] else if (activity.address != null) ...[ Icon( Icons.location_on, size: 16, color: theme.colorScheme.onSurface.withOpacity(0.5), ), const SizedBox(width: 4), Expanded( child: Text( activity.address!, style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurface.withOpacity(0.7), ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], ], ), const SizedBox(height: 12), ], // Section vote et actions Row( children: [ // Boutons de vote Container( decoration: BoxDecoration( color: theme.colorScheme.surfaceVariant.withOpacity(0.5), borderRadius: BorderRadius.circular(20), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ // Vote positif _buildVoteButton( icon: Icons.thumb_up, count: activity.positiveVotes, isActive: userVote == 1, onTap: () => onVote(userVote == 1 ? 0 : 1), activeColor: Colors.blue, ), Container( width: 1, height: 24, color: theme.colorScheme.onSurface.withOpacity(0.2), ), // Vote négatif _buildVoteButton( icon: Icons.thumb_down, count: activity.negativeVotes, isActive: userVote == -1, onTap: () => onVote(userVote == -1 ? 0 : -1), activeColor: Colors.red, ), ], ), ), const Spacer(), // Bouton d'action if (onAddToTrip != null) ElevatedButton( onPressed: onAddToTrip, style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), ), child: const Text('Voter'), ) else // Score total Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: _getScoreColor(activity.totalVotes).withOpacity(0.2), borderRadius: BorderRadius.circular(12), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Text( '${activity.totalVotes > 0 ? '+' : ''}${activity.totalVotes}', style: theme.textTheme.bodySmall?.copyWith( fontWeight: FontWeight.bold, color: _getScoreColor(activity.totalVotes), ), ), const SizedBox(width: 4), Text( 'vote${activity.votes.length > 1 ? 's' : ''}', style: theme.textTheme.bodySmall?.copyWith( color: _getScoreColor(activity.totalVotes), ), ), ], ), ), ], ), ], ), ), ], ), ); } Widget _buildVoteButton({ required IconData icon, required int count, required bool isActive, required VoidCallback onTap, required Color activeColor, }) { return GestureDetector( onTap: onTap, child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( icon, size: 18, color: isActive ? activeColor : Colors.grey, ), if (count > 0) ...[ const SizedBox(width: 4), Text( count.toString(), style: TextStyle( fontSize: 12, fontWeight: FontWeight.w500, color: isActive ? activeColor : Colors.grey, ), ), ], ], ), ), ); } Color _getCategoryColor(String category) { switch (category.toLowerCase()) { case 'musée': return Colors.purple; case 'restaurant': return Colors.orange; case 'attraction': return Colors.blue; case 'divertissement': return Colors.pink; case 'shopping': return Colors.green; case 'nature': return Colors.teal; case 'culture': return Colors.indigo; case 'vie nocturne': return Colors.deepPurple; case 'sports': return Colors.red; case 'détente': return Colors.cyan; default: return Colors.grey; } } Color _getScoreColor(int score) { if (score > 0) return Colors.green; if (score < 0) return Colors.red; return Colors.grey; } }