/// Reward Card Widget /// /// Displays a gift catalog item with redemption button. library; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:intl/intl.dart'; import 'package:worker/core/theme/colors.dart'; import 'package:worker/features/loyalty/domain/entities/gift_catalog.dart'; import 'package:worker/features/loyalty/presentation/providers/loyalty_points_provider.dart'; /// Reward Card Widget /// /// Shows gift information and redemption button. /// Button state changes based on whether user has enough points. class RewardCard extends ConsumerWidget { /// Gift to display final GiftCatalog gift; /// Callback when redeem button is pressed final VoidCallback onRedeem; const RewardCard({required this.gift, required this.onRedeem, super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final hasEnoughPoints = ref.watch(hasEnoughPointsProvider(gift.pointsCost)); final numberFormat = NumberFormat('#,###', 'vi_VN'); return Card( elevation: 2, margin: EdgeInsets.symmetric(horizontal: 8, vertical: 8), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), clipBehavior: Clip.antiAlias, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Gift Image _buildImage(), // Gift Info Expanded( child: Padding( padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Gift Name Text( gift.name, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: AppColors.grey900, height: 1.3, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), // Gift Description if (gift.description != null && gift.description!.isNotEmpty) Text( gift.description!, style: const TextStyle( fontSize: 12, color: AppColors.grey500, height: 1.2, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), // Spacer to push points and button to bottom const Spacer(), // Points Cost (at bottom) Text( '${numberFormat.format(gift.pointsCost)} điểm', style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w700, color: AppColors.primaryBlue, ), ), const SizedBox(height: 8), // Redeem Button (at bottom) SizedBox( width: double.infinity, height: 36, child: ElevatedButton.icon( onPressed: hasEnoughPoints && gift.isAvailable ? onRedeem : null, icon: FaIcon( FontAwesomeIcons.gift, size: 14, color: hasEnoughPoints && gift.isAvailable ? Colors.white : AppColors.grey500, ), label: Text( hasEnoughPoints && gift.isAvailable ? 'Đổi quà' : 'Không đủ điểm', style: const TextStyle( fontSize: 13, fontWeight: FontWeight.w600, ), ), style: ElevatedButton.styleFrom( backgroundColor: hasEnoughPoints && gift.isAvailable ? AppColors.primaryBlue : AppColors.grey100, foregroundColor: hasEnoughPoints && gift.isAvailable ? Colors.white : AppColors.grey500, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 8, ), ), ), ), ], ), ), ), ], ), ); } /// Build gift image Widget _buildImage() { return SizedBox( height: 120, child: gift.imageUrl != null && gift.imageUrl!.isNotEmpty ? CachedNetworkImage( imageUrl: gift.imageUrl!, fit: BoxFit.cover, placeholder: (context, url) => Container( color: AppColors.grey100, child: const Center( child: CircularProgressIndicator(strokeWidth: 2), ), ), errorWidget: (context, url, error) => Container( color: AppColors.grey100, child: const FaIcon( FontAwesomeIcons.gift, size: 48, color: AppColors.grey500, ), ), ) : Container( color: AppColors.grey100, child: const FaIcon( FontAwesomeIcons.gift, size: 48, color: AppColors.grey500, ), ), ); } }