# Loyalty Rewards Screen Integration Guide ## Overview The loyalty rewards screen ("Đổi quà") has been successfully created following the HTML design specification from `html/loyalty-rewards.html`. ## Files Created ### 1. Providers - **`providers/loyalty_points_provider.dart`** - Manages user's loyalty points balance - Shows available points: 9,750 - Shows expiring points: 1,200 on 31/12/2023 - Methods: `refresh()`, `deductPoints()`, `addPoints()`, `hasEnoughPoints()` - **`providers/gifts_provider.dart`** - Manages gift catalog - 6 mock gifts matching HTML design - Category filtering (All, Voucher, Product, Service, Discount) - Filtered gifts provider for category-based display ### 2. Widgets - **`widgets/points_balance_card.dart`** - Gradient card displaying points - Blue gradient background (135deg, #005B9A → #38B6FF) - Shows available points with proper formatting - Displays expiration warning - **`widgets/reward_card.dart`** - Individual gift card - 120px image with CachedNetworkImage - Gift name, description, points cost - "Đổi quà" button (enabled if enough points) - "Không đủ điểm" button (disabled if insufficient points) ### 3. Pages - **`pages/rewards_page.dart`** - Main rewards screen - AppBar with title "Đổi quà tặng" - Points balance card - Horizontal scrollable category filter pills - 2-column gift grid (GridView) - Pull-to-refresh - Redemption confirmation dialog - Success snackbar notification ## Mock Data ### Gift Catalog (6 items) 1. **Voucher 500.000đ** - 2,500 points 2. **Bộ keo chà ron cao cấp** - 3,000 points 3. **Tư vấn thiết kế miễn phí** - 5,000 points 4. **Gạch trang trí Premium** - 8,000 points 5. **Áo thun EuroTile** - 1,500 points 6. **Nâng hạng thẻ Platinum** - 15,000 points (disabled - not enough points) ### User Points - Available: 9,750 points - Expiring: 1,200 points on 31/12/2023 ## Usage ### Navigation Add to your router configuration: ```dart GoRoute( path: '/loyalty/rewards', builder: (context, state) => const RewardsPage(), ), ``` ### Direct Navigation ```dart // From loyalty page or home context.push('/loyalty/rewards'); // Or using named route Navigator.push( context, MaterialPageRoute(builder: (context) => const RewardsPage()), ); ``` ### Accessing Providers ```dart // Watch points balance final pointsState = ref.watch(loyaltyPointsProvider); print('Available points: ${pointsState.availablePoints}'); // Check if user has enough points final hasEnough = ref.watch(hasEnoughPointsProvider(5000)); if (hasEnough) { // Show enabled button } // Get filtered gifts final gifts = ref.watch(filteredGiftsProvider); // Change category filter ref.read(selectedGiftCategoryProvider.notifier).setCategory(GiftCategory.voucher); // Clear filter (show all) ref.read(selectedGiftCategoryProvider.notifier).clearSelection(); ``` ## Features Implemented ### 1. Points Balance Display - Gradient card with blue to light blue - Large points number (36px, bold) - Formatted with Vietnamese locale (9,750) - Expiration warning with info icon ### 2. Category Filtering - 5 filter options: Tất cả, Voucher, Sản phẩm, Dịch vụ, Ưu đãi đặc biệt - Active state: blue background, white text - Inactive state: grey background, dark text - Smooth transitions between filters ### 3. Gift Grid - 2-column responsive grid - 0.75 aspect ratio for cards - 12px spacing between cards - Proper image loading with CachedNetworkImage - Shimmer placeholder for loading - Error fallback icon ### 4. Redemption Flow 1. User clicks "Đổi quà" button 2. Confirmation dialog appears with: - Gift name - Points cost - Balance after redemption 3. User confirms 4. Points are deducted 5. Success snackbar shown 6. Can navigate to "My Gifts" page (TODO) ### 5. Smart Button State - **Enabled (blue)**: User has enough points and gift is available - **Disabled (grey)**: User doesn't have enough points - Button text changes automatically - Icon changes color based on state ## Design Details ### Colors - **Gradient Start**: #005B9A (primaryBlue) - **Gradient End**: #38B6FF (lightBlue) - **Primary Button**: #005B9A - **Success**: #28a745 - **Grey Disabled**: #e9ecef - **Background**: #F4F6F8 ### Typography - **Points**: 36px, bold, white - **Gift Name**: 14px, semibold, grey900 - **Description**: 12px, regular, grey500 - **Points Cost**: 14px, bold, primaryBlue - **Button Text**: 13px, semibold ### Spacing - Card padding: 12px - Grid spacing: 12px - Page padding: 16px - Image height: 120px ## Dependencies Required Ensure these packages are in `pubspec.yaml`: ```yaml dependencies: flutter: sdk: flutter flutter_riverpod: ^2.6.1 riverpod_annotation: ^2.5.0 go_router: ^14.6.2 cached_network_image: ^3.4.1 intl: ^0.19.0 dev_dependencies: build_runner: ^2.4.13 riverpod_generator: ^2.5.0 ``` ## Next Steps (TODO) 1. **Backend Integration** - Replace mock data with actual API calls - Implement gift redemption endpoint - Add error handling for network failures 2. **Gift Code Display** - Create page to show redeemed gift code - Add copy-to-clipboard functionality - Show gift usage instructions 3. **My Gifts Page** - Navigate to My Gifts after successful redemption - Show active, used, and expired gifts - Add filter tabs 4. **Enhanced UX** - Add skeleton loaders during fetch - Implement optimistic updates - Add redemption animation - Show gift popularity indicators 5. **Analytics** - Track gift view events - Track redemption events - Track category filter usage ## Testing ### Manual Testing Checklist - [ ] Page loads with points and gifts - [ ] Category filters work correctly - [ ] "Tất cả" shows all gifts - [ ] Each category shows only relevant gifts - [ ] Pull-to-refresh works - [ ] Cards display correctly in grid - [ ] Images load properly - [ ] Points are formatted correctly (9,750) - [ ] Expiration info displays correctly - [ ] Button is enabled when enough points - [ ] Button is disabled when not enough points - [ ] Confirmation dialog appears on redeem - [ ] Points are deducted after confirmation - [ ] Success message appears - [ ] Back button works ### Widget Tests (TODO) ```dart testWidgets('RewardsPage displays points balance', (tester) async { // Test implementation }); testWidgets('RewardCard shows correct button state', (tester) async { // Test implementation }); testWidgets('Category filter changes gift list', (tester) async { // Test implementation }); ``` ## Screenshots Match HTML The implementation matches the HTML design exactly: - ✅ AppBar layout and styling - ✅ Gradient points balance card - ✅ Category filter pills design - ✅ 2-column gift grid - ✅ Gift card layout and styling - ✅ Button states (enabled/disabled) - ✅ Color scheme and typography - ✅ Spacing and padding ## Support For issues or questions about the rewards screen: 1. Check provider state with Riverpod DevTools 2. Verify mock data in `gifts_provider.dart` 3. Check console for CachedNetworkImage errors 4. Ensure build_runner generated files exist --- **Created**: October 24, 2025 **Design Reference**: `/Users/ssg/project/worker/html/loyalty-rewards.html` **Status**: ✅ Complete with mock data