From b25f9e151a60d7796ef4929df1cdc57baaae6dcc Mon Sep 17 00:00:00 2001 From: Suherdy Yacob Date: Tue, 16 Jun 2026 07:16:32 +0700 Subject: [PATCH] feat: integrate subscription cards and support program-specific reward point validation --- lib/screens/rewards_screen.dart | 95 +++++++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 16 deletions(-) diff --git a/lib/screens/rewards_screen.dart b/lib/screens/rewards_screen.dart index 7894b0a..5e87c59 100644 --- a/lib/screens/rewards_screen.dart +++ b/lib/screens/rewards_screen.dart @@ -11,7 +11,8 @@ class RewardsScreen extends StatefulWidget { } class _RewardsScreenState extends State { - double _userPoints = 0.0; + double _userPoints = 0.0; // Main loyalty points balance shown in the top banner + Map _programPoints = {}; // Maps programId to card points List _rewards = []; bool _isLoading = true; @@ -26,30 +27,64 @@ class _RewardsScreenState extends State { setState(() => _isLoading = true); try { - final cards = await OdooService().getLoyaltyCards(widget.partnerId); + // Fetch both loyalty cards and subscription cards in parallel + final results = await Future.wait([ + OdooService().getLoyaltyCards(widget.partnerId), + OdooService().getSubscriptionCards(widget.partnerId), + ]); - double totalPoints = 0.0; - List rawRewards = []; + final loyaltyCards = results[0]; + final subscriptionCards = results[1]; - if (cards.isNotEmpty) { - totalPoints = safeDouble(cards.first['points']); - final prog = cards.first['program_id']; - int? programId; + double loyaltyPoints = 0.0; + final Map pointsMap = {}; + final List programIds = []; + + // 1. Process main loyalty card + if (loyaltyCards.isNotEmpty) { + loyaltyPoints = safeDouble(loyaltyCards.first['points']); + final prog = loyaltyCards.first['program_id']; + int? progId; if (prog is List && prog.isNotEmpty) { - programId = prog[0] as int?; + progId = prog[0] as int?; } else if (prog is int) { - programId = prog; + progId = prog; } + if (progId != null) { + pointsMap[progId] = loyaltyPoints; + programIds.add(progId); + } + } - if (programId != null) { - rawRewards = await OdooService().getLoyaltyRewards(programId); + // 2. Process active subscription cards + for (final card in subscriptionCards) { + final pts = safeDouble(card['points']); + final prog = card['program_id']; + int? progId; + if (prog is List && prog.isNotEmpty) { + progId = prog[0] as int?; + } else if (prog is int) { + progId = prog; } + if (progId != null) { + pointsMap[progId] = pts; + if (!programIds.contains(progId)) { + programIds.add(progId); + } + } + } + + // 3. Fetch rewards for all resolved program IDs + List fetchedRewards = []; + if (programIds.isNotEmpty) { + fetchedRewards = await OdooService().getLoyaltyRewards(programIds); } if (mounted) { setState(() { - _userPoints = totalPoints; - _rewards = rawRewards; + _userPoints = loyaltyPoints; + _programPoints = pointsMap; + _rewards = fetchedRewards; _isLoading = false; }); } @@ -195,10 +230,28 @@ class _RewardsScreenState extends State { itemBuilder: (context, index) { final reward = _rewards[index]; final reqPoints = safeDouble(reward['required_points']); - final isAvailable = _userPoints >= reqPoints; final String desc = safeString(reward['description']) ?? 'Loyalty Reward'; final String type = safeString(reward['reward_type']) ?? 'product'; + // Resolve reward's program ID and name + final progVal = reward['program_id']; + int? rewardProgramId; + String programName = 'Reward'; + if (progVal is List && progVal.isNotEmpty) { + rewardProgramId = progVal[0] as int?; + if (progVal.length > 1) { + programName = progVal[1] as String; + } + } else if (progVal is int) { + rewardProgramId = progVal; + } + + // Determine point balance and availability based on the specific program + final currentCardPoints = rewardProgramId != null + ? (_programPoints[rewardProgramId] ?? 0.0) + : 0.0; + final isAvailable = currentCardPoints >= reqPoints; + // Decide icon based on reward type IconData iconData = Icons.local_offer_rounded; if (type == 'product') { @@ -279,6 +332,16 @@ class _RewardsScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ + // Program Source Name + Text( + programName.toUpperCase(), + style: theme.textTheme.labelSmall?.copyWith( + color: colorScheme.primary.withValues(alpha: 0.65), + fontWeight: FontWeight.bold, + letterSpacing: 1.0, + ), + ), + const SizedBox(height: 4), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -357,7 +420,7 @@ class _RewardsScreenState extends State { ), const SizedBox(width: 4), Text( - 'Need ${(reqPoints - _userPoints).toStringAsFixed(0)} more pts', + 'Need ${(reqPoints - currentCardPoints).toStringAsFixed(0)} more pts', style: theme.textTheme.bodySmall?.copyWith( color: colorScheme.error, fontWeight: FontWeight.bold,