loyalty
This commit is contained in:
269
lib/features/loyalty/presentation/REWARDS_INTEGRATION.md
Normal file
269
lib/features/loyalty/presentation/REWARDS_INTEGRATION.md
Normal file
@@ -0,0 +1,269 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user