diff --git a/lib/screens/rewards_screen.dart b/lib/screens/rewards_screen.dart index 5e87c59..ae9fcdb 100644 --- a/lib/screens/rewards_screen.dart +++ b/lib/screens/rewards_screen.dart @@ -13,6 +13,7 @@ class RewardsScreen extends StatefulWidget { class _RewardsScreenState extends State { double _userPoints = 0.0; // Main loyalty points balance shown in the top banner Map _programPoints = {}; // Maps programId to card points + Set _subscriptionProgramIds = {}; // Record program IDs that are subscription cards List _rewards = []; bool _isLoading = true; @@ -56,6 +57,8 @@ class _RewardsScreenState extends State { } } + final Set subProgIds = {}; + // 2. Process active subscription cards for (final card in subscriptionCards) { final pts = safeDouble(card['points']); @@ -68,6 +71,7 @@ class _RewardsScreenState extends State { } if (progId != null) { pointsMap[progId] = pts; + subProgIds.add(progId); if (!programIds.contains(progId)) { programIds.add(progId); } @@ -84,6 +88,7 @@ class _RewardsScreenState extends State { setState(() { _userPoints = loyaltyPoints; _programPoints = pointsMap; + _subscriptionProgramIds = subProgIds; _rewards = fetchedRewards; _isLoading = false; }); @@ -104,6 +109,25 @@ class _RewardsScreenState extends State { } } + String _formatPoints(double points) { + final int val = points.round(); + if (val >= 1000) { + final double k = val / 1000; + if (k % 1 == 0) { + return '${k.toInt()}K'; + } else { + return '${k.toStringAsFixed(1)}K'; + } + } + return val.toString(); + } + + String _formatWithCommas(double value) { + final int val = value.round(); + final RegExp reg = RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'); + return val.toString().replaceAllMapped(reg, (Match match) => '${match[1]},'); + } + @override Widget build(BuildContext context) { final theme = Theme.of(context); @@ -160,7 +184,7 @@ class _RewardsScreenState extends State { ), const SizedBox(height: 4), Text( - '${_userPoints.toStringAsFixed(0)} Points', + '${_formatWithCommas(_userPoints)} Points', style: theme.textTheme.headlineMedium?.copyWith( color: colorScheme.onPrimary, fontWeight: FontWeight.bold, @@ -246,11 +270,19 @@ class _RewardsScreenState extends State { rewardProgramId = progVal; } + // Determine if it is a subscription reward + final isSubscription = rewardProgramId != null && + _subscriptionProgramIds.contains(rewardProgramId); + + // If it's a subscription reward and required points <= 1, treat it as FREE. + // For regular loyalty program, only treat <= 0 as FREE. + final bool isFree = reqPoints <= 0 || (isSubscription && reqPoints <= 1); + // Determine point balance and availability based on the specific program final currentCardPoints = rewardProgramId != null ? (_programPoints[rewardProgramId] ?? 0.0) : 0.0; - final isAvailable = currentCardPoints >= reqPoints; + final isAvailable = isFree ? true : (currentCardPoints >= reqPoints); // Decide icon based on reward type IconData iconData = Icons.local_offer_rounded; @@ -292,28 +324,51 @@ class _RewardsScreenState extends State { ), ), padding: const EdgeInsets.symmetric(horizontal: 10), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - reqPoints.toStringAsFixed(0), - style: theme.textTheme.titleLarge?.copyWith( - color: colorScheme.primary, - fontWeight: FontWeight.bold, - fontFamily: 'monospace', + child: isFree + ? Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.card_giftcard_rounded, + color: colorScheme.primary, + size: 28, + ), + const SizedBox(height: 4), + Text( + 'FREE', + style: theme.textTheme.labelSmall?.copyWith( + color: colorScheme.primary, + fontWeight: FontWeight.bold, + letterSpacing: 1.0, + ), + ), + ], + ) + : Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FittedBox( + fit: BoxFit.scaleDown, + child: Text( + _formatPoints(reqPoints), + style: theme.textTheme.titleLarge?.copyWith( + color: colorScheme.primary, + fontWeight: FontWeight.bold, + fontFamily: 'monospace', + ), + ), + ), + const SizedBox(height: 2), + Text( + 'PTS', + style: theme.textTheme.labelSmall?.copyWith( + color: colorScheme.primary.withValues(alpha: 0.8), + fontWeight: FontWeight.w900, + letterSpacing: 1.0, + ), + ), + ], ), - ), - const SizedBox(height: 2), - Text( - 'PTS', - style: theme.textTheme.labelSmall?.copyWith( - color: colorScheme.primary.withValues(alpha: 0.8), - fontWeight: FontWeight.w900, - letterSpacing: 1.0, - ), - ), - ], - ), ), // Custom Dashed Divider (Simulating Tear-off Voucher) @@ -420,7 +475,7 @@ class _RewardsScreenState extends State { ), const SizedBox(width: 4), Text( - 'Need ${(reqPoints - currentCardPoints).toStringAsFixed(0)} more pts', + 'Need ${_formatWithCommas(reqPoints - currentCardPoints)} more pts', style: theme.textTheme.bodySmall?.copyWith( color: colorScheme.error, fontWeight: FontWeight.bold,