import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import '../../../../shared/theme/app_theme.dart'; class MemberCard extends StatefulWidget { final Map member; final VoidCallback? onTap; final VoidCallback? onEdit; const MemberCard({ super.key, required this.member, this.onTap, this.onEdit, }); @override State createState() => _MemberCardState(); } class _MemberCardState extends State with SingleTickerProviderStateMixin { late AnimationController _animationController; late Animation _scaleAnimation; @override void initState() { super.initState(); _animationController = AnimationController( duration: const Duration(milliseconds: 150), vsync: this, ); _scaleAnimation = Tween( begin: 1.0, end: 0.98, ).animate(CurvedAnimation( parent: _animationController, curve: Curves.easeInOut, )); } @override void dispose() { _animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _scaleAnimation, builder: (context, child) { return Transform.scale( scale: _scaleAnimation.value, child: Material( color: Colors.transparent, child: InkWell( onTap: widget.onTap != null ? _handleTap : null, onTapDown: widget.onTap != null ? (_) => _animationController.forward() : null, onTapUp: widget.onTap != null ? (_) => _animationController.reverse() : null, onTapCancel: widget.onTap != null ? () => _animationController.reverse() : null, borderRadius: BorderRadius.circular(16), child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.08), blurRadius: 15, offset: const Offset(0, 4), ), ], border: Border.all( color: _getStatusColor().withOpacity(0.2), width: 1, ), ), child: Column( children: [ Row( children: [ _buildAvatar(), const SizedBox(width: 16), Expanded( child: _buildMemberInfo(), ), _buildStatusBadge(), ], ), const SizedBox(height: 16), _buildMemberDetails(), const SizedBox(height: 12), _buildActionButtons(), ], ), ), ), ), ); }, ); } Widget _buildAvatar() { return Container( width: 60, height: 60, decoration: BoxDecoration( gradient: LinearGradient( colors: [ _getStatusColor(), _getStatusColor().withOpacity(0.7), ], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(30), boxShadow: [ BoxShadow( color: _getStatusColor().withOpacity(0.3), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: widget.member['avatar'] != null ? ClipRRect( borderRadius: BorderRadius.circular(30), child: Image.network( widget.member['avatar'], fit: BoxFit.cover, ), ) : Center( child: Text( _getInitials(), style: const TextStyle( color: Colors.white, fontSize: 22, fontWeight: FontWeight.bold, ), ), ), ); } Widget _buildMemberInfo() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '${widget.member['firstName']} ${widget.member['lastName']}', style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: AppTheme.textPrimary, ), ), const SizedBox(height: 4), Text( widget.member['role'], style: TextStyle( fontSize: 14, color: _getStatusColor(), fontWeight: FontWeight.w600, ), ), const SizedBox(height: 2), Text( widget.member['email'], style: const TextStyle( fontSize: 13, color: AppTheme.textSecondary, ), overflow: TextOverflow.ellipsis, ), ], ); } Widget _buildStatusBadge() { final isActive = widget.member['status'] == 'Actif'; final color = isActive ? AppTheme.successColor : AppTheme.textHint; return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(20), border: Border.all( color: color.withOpacity(0.3), width: 1, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 8, height: 8, decoration: BoxDecoration( color: color, borderRadius: BorderRadius.circular(4), ), ), const SizedBox(width: 6), Text( widget.member['status'], style: TextStyle( color: color, fontSize: 12, fontWeight: FontWeight.w600, ), ), ], ), ); } Widget _buildMemberDetails() { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: AppTheme.backgroundLight, borderRadius: BorderRadius.circular(12), ), child: Column( children: [ _buildDetailRow( icon: Icons.phone, label: 'Téléphone', value: widget.member['phone'], color: AppTheme.infoColor, ), const SizedBox(height: 8), _buildDetailRow( icon: Icons.calendar_today, label: 'Adhésion', value: _formatDate(widget.member['joinDate']), color: AppTheme.primaryColor, ), const SizedBox(height: 8), _buildDetailRow( icon: Icons.payment, label: 'Cotisation', value: widget.member['cotisationStatus'], color: _getCotisationColor(), ), ], ), ); } Widget _buildDetailRow({ required IconData icon, required String label, required String value, required Color color, }) { return Row( children: [ Container( width: 32, height: 32, decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(16), ), child: Icon( icon, size: 16, color: color, ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: const TextStyle( fontSize: 12, color: AppTheme.textSecondary, ), ), Text( value, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: label == 'Cotisation' ? color : AppTheme.textPrimary, ), ), ], ), ), ], ); } Widget _buildActionButtons() { return Row( children: [ Expanded( child: OutlinedButton.icon( onPressed: _callMember, icon: const Icon(Icons.phone, size: 16), label: const Text('Appeler'), style: OutlinedButton.styleFrom( foregroundColor: AppTheme.infoColor, side: BorderSide(color: AppTheme.infoColor.withOpacity(0.5)), padding: const EdgeInsets.symmetric(vertical: 8), ), ), ), const SizedBox(width: 12), Expanded( child: OutlinedButton.icon( onPressed: _emailMember, icon: const Icon(Icons.email, size: 16), label: const Text('Email'), style: OutlinedButton.styleFrom( foregroundColor: AppTheme.primaryColor, side: BorderSide(color: AppTheme.primaryColor.withOpacity(0.5)), padding: const EdgeInsets.symmetric(vertical: 8), ), ), ), const SizedBox(width: 12), Container( decoration: BoxDecoration( color: AppTheme.secondaryColor.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: IconButton( onPressed: widget.onEdit, icon: const Icon(Icons.edit, size: 18), color: AppTheme.secondaryColor, padding: const EdgeInsets.all(8), constraints: const BoxConstraints( minWidth: 40, minHeight: 40, ), ), ), ], ); } String _getInitials() { final firstName = widget.member['firstName'] as String; final lastName = widget.member['lastName'] as String; return '${firstName.isNotEmpty ? firstName[0] : ''}${lastName.isNotEmpty ? lastName[0] : ''}'.toUpperCase(); } Color _getStatusColor() { switch (widget.member['role']) { case 'Président': return AppTheme.primaryColor; case 'Secrétaire': return AppTheme.secondaryColor; case 'Trésorier': return AppTheme.accentColor; case 'Responsable événements': return AppTheme.warningColor; default: return AppTheme.infoColor; } } Color _getCotisationColor() { switch (widget.member['cotisationStatus']) { case 'À jour': return AppTheme.successColor; case 'En retard': return AppTheme.errorColor; case 'Exempt': return AppTheme.infoColor; default: return AppTheme.textSecondary; } } String _formatDate(String dateString) { try { final date = DateTime.parse(dateString); final months = [ 'Jan', 'Fév', 'Mar', 'Avr', 'Mai', 'Jun', 'Jul', 'Aoû', 'Sep', 'Oct', 'Nov', 'Déc' ]; return '${date.day} ${months[date.month - 1]} ${date.year}'; } catch (e) { return dateString; } } void _handleTap() { HapticFeedback.selectionClick(); widget.onTap?.call(); } void _callMember() { HapticFeedback.lightImpact(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Appel vers ${widget.member['phone']} - En développement'), backgroundColor: AppTheme.infoColor, behavior: SnackBarBehavior.floating, ), ); } void _emailMember() { HapticFeedback.lightImpact(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Email vers ${widget.member['email']} - En développement'), backgroundColor: AppTheme.primaryColor, behavior: SnackBarBehavior.floating, ), ); } }