diff --git a/CLAUDE.md b/CLAUDE.md index d10835a..f20c433 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -81,12 +81,92 @@ You have access to these expert subagents - USE THEM PROACTIVELY: - Use Flutter 3.x features and Material 3 design - Implement clean architecture with Riverpod for state management - Use Hive for local database and offline functionality -- Follow proper dependency injection with Riverpod DI +- Follow proper dependency injection with Riverpod DI - Implement proper error handling and user feedback - Follow iOS and Android platform-specific design guidelines - Support Vietnamese language (primary) and English (secondary) - Mobile-first design optimized for phone screens +### Hive Best Practices +**IMPORTANT: Box Type Management** +When working with Hive boxes, always use `Box` in data sources and apply `.whereType()` for type-safe queries: + +```dart +// ✅ CORRECT - Use Box with type filtering +Box get _box { + return Hive.box(HiveBoxNames.favoriteBox); +} + +Future> getAllFavorites(String userId) async { + try { + final favorites = _box.values + .whereType() // Type-safe filtering + .where((fav) => fav.userId == userId) + .toList(); + return favorites; + } catch (e) { + debugPrint('[DataSource] Error: $e'); + rethrow; + } +} + +// ❌ INCORRECT - Will cause HiveError +Box get _box { + return Hive.box(HiveBoxNames.favoriteBox); +} +``` + +**Reason**: Hive boxes are opened as `Box` in the central HiveService. Re-opening with a specific type causes `HiveError: The box is already open and of type Box`. + +### AppBar Standardization +**ALL AppBars must follow this standard pattern** (reference: `products_page.dart`): + +```dart +AppBar( + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.black), + onPressed: () => context.pop(), + ), + title: const Text('Page Title', style: TextStyle(color: Colors.black)), + elevation: AppBarSpecs.elevation, + backgroundColor: AppColors.white, + foregroundColor: AppColors.grey900, + centerTitle: false, + actions: [ + // Custom actions here + const SizedBox(width: AppSpacing.sm), // Always end with spacing + ], +) +``` + +**Key Requirements**: +- Black back arrow with explicit color +- Black text title with TextStyle +- Left-aligned title (`centerTitle: false`) +- White background (`AppColors.white`) +- Use `AppBarSpecs.elevation` (not hardcoded values) +- Always add `SizedBox(width: AppSpacing.sm)` after actions + +**For SliverAppBar** (in CustomScrollView): +```dart +SliverAppBar( + pinned: true, + backgroundColor: AppColors.white, + foregroundColor: AppColors.grey900, + elevation: AppBarSpecs.elevation, + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.black), + onPressed: () => context.pop(), + ), + title: const Text('Page Title', style: TextStyle(color: Colors.black)), + centerTitle: false, + actions: [ + // Custom actions + const SizedBox(width: AppSpacing.sm), + ], +) +``` + --- ## Worker App Project Structure @@ -593,6 +673,10 @@ final otpTimerProvider = StateNotifierProvider - **Quick Action Grid**: - 4-6 icon buttons for main features - Products, Loyalty, Orders, Projects, Promotions + - **Cart badge**: Dynamic count from `cartItemCountProvider` + - Shows cart item count when > 0 + - Hidden when cart is empty + - Updates in real-time across all pages - **Bottom Navigation**: - 5 tabs: Home, Products, Loyalty, Account, More @@ -657,27 +741,55 @@ final loyaltyPointsProvider = AsyncNotifierProvider filteredGifts(ref) { + // Filters by selected category +} + +final selectedGiftCategoryProvider = StateNotifierProvider... +final hasEnoughPointsProvider = Provider.family... +``` + +**Navigation**: +- Route: `/loyalty/rewards` (RouteNames in app_router.dart) +- Accessible from home quick action "Đổi quà" + +**Design Reference**: `html/loyalty-rewards.html` ✅ --- @@ -1275,6 +1387,46 @@ class FABSpecs { } ``` +#### AppBar (Standardized across all pages) +```dart +class AppBarSpecs { + // From ui_constants.dart + static const double elevation = 0.5; + + // Standard pattern for all pages + static AppBar standard({ + required String title, + required VoidCallback onBack, + List? actions, + }) { + return AppBar( + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.black), + onPressed: onBack, + ), + title: Text(title, style: const TextStyle(color: Colors.black)), + elevation: elevation, + backgroundColor: AppColors.white, + foregroundColor: AppColors.grey900, + centerTitle: false, + actions: [ + ...?actions, + const SizedBox(width: AppSpacing.sm), + ], + ); + } +} +``` + +**Usage Notes**: +- ALL pages use this standard AppBar pattern +- Back arrow is always black with explicit color +- Title is always left-aligned (`centerTitle: false`) +- Title text is always black +- Background is always white +- Actions always end with `SizedBox(width: AppSpacing.sm)` spacing +- For SliverAppBar, add `pinned: true` property + --- ## Technical Architecture @@ -1761,16 +1913,32 @@ end ## Roadmap -### Phase 1 - Complete ✅ -- Authentication (login, OTP, register) -- Home with member cards (3 tiers) -- Complete loyalty system -- Product browsing and cart -- Order management -- Project and quote management -- Chat support -- Account management -- Promotions and notifications +### Phase 1 - Core Features ✅ +- ✅ Authentication (login, OTP, register) +- ✅ Home with member cards (3 tiers) +- ✅ Product browsing with search and filters +- ✅ Shopping cart with dynamic badge +- ✅ Favorites system with Hive persistence +- ✅ Order management +- ✅ Project and quote management +- ✅ Account management +- ✅ Promotions with detail pages + +### Phase 1.5 - Loyalty System ✅ RECENT +- ✅ **Rewards/Gift Redemption Page** (Dec 2024) + - Points balance card with gradient + - Category filtering (Voucher/Product/Service/Discount) + - 2-column gift grid with 6 mock items + - Redemption flow with confirmation dialog + - Points deduction and success feedback +- ✅ **UI Standardization** (Dec 2024) + - All AppBars now follow consistent pattern + - Dynamic cart badge across all pages + - Hive Box best practices documented +- 🔄 Points history page (planned) +- 🔄 Referral program page (planned) +- 🔄 My gifts page (planned) +- 🔄 Chat support (planned) ### Phase 2 - Backend Integration 🔄 - REST API integration @@ -1803,3 +1971,130 @@ When working on this Flutter Worker app: - **Performance Issues** → performance-expert **Think delegation first, implementation second!** + +--- + +## Recent Updates & Implementation Notes (December 2024) + +### ✅ Rewards/Gift Redemption System +**Status**: Fully implemented with mock data + +**Implementation Details**: +1. **Providers** (`lib/features/loyalty/presentation/providers/`): + - `loyalty_points_provider.dart` - Manages user points (9,750 available, 1,200 expiring) + - `gifts_provider.dart` - 6 mock gifts matching HTML design + - `selectedGiftCategoryProvider` - Category filter state + - `filteredGiftsProvider` - Category-based filtering + - `hasEnoughPointsProvider.family` - Points validation per gift + +2. **Widgets** (`lib/features/loyalty/presentation/widgets/`): + - `points_balance_card.dart` - Gradient blue card with points display + - `reward_card.dart` - Gift card with bottom-aligned action button + +3. **Pages** (`lib/features/loyalty/presentation/pages/`): + - `rewards_page.dart` - Main screen with RefreshIndicator + +4. **Route**: `/loyalty/rewards` in `app_router.dart` + +**Key Features**: +- 2-column grid layout with `childAspectRatio: 0.7` +- Category filtering: Tất cả/Voucher/Sản phẩm/Dịch vụ/Ưu đãi đặc biệt +- Bottom-aligned buttons using `Expanded` + `Spacer()` +- Redemption dialog with points calculation +- Success feedback with SnackBar + +### ✅ AppBar Standardization +**Status**: Completed across all pages + +**Standard Pattern**: +```dart +AppBar( + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.black), + onPressed: () => context.pop(), + ), + title: const Text('Title', style: TextStyle(color: Colors.black)), + elevation: AppBarSpecs.elevation, + backgroundColor: AppColors.white, + foregroundColor: AppColors.grey900, + centerTitle: false, + actions: [..., const SizedBox(width: AppSpacing.sm)], +) +``` + +**Updated Pages**: +- `cart_page.dart` - Lines 84-103 +- `favorites_page.dart` - Lines 79-115 +- `rewards_page.dart` - Lines 35-48 +- `promotion_detail_page.dart` - Lines 59-86 (loading/error), 130-161 (SliverAppBar) +- `product_detail_page.dart` - Already correct ✅ + +### ✅ Dynamic Cart Badge +**Status**: Implemented across home and products pages + +**Implementation**: +```dart +// Added provider in cart_provider.dart +@riverpod +int cartItemCount(CartItemCountRef ref) { + final cartState = ref.watch(cartProvider); + return cartState.items.fold(0, (sum, item) => sum + item.quantity); +} + +// Used in home_page.dart and products_page.dart +final cartItemCount = ref.watch(cartItemCountProvider); +QuickAction( + badge: cartItemCount > 0 ? '$cartItemCount' : null, +) +``` + +**Behavior**: +- Shows total quantity across all cart items +- Hidden when cart is empty +- Updates in real-time via Riverpod +- Navigation via `RouteNames.cart` constant + +### ✅ Hive Box Type Management +**Status**: Best practices documented and implemented + +**Problem Solved**: +``` +HiveError: The box "favorite_box" is already open and of type Box +``` + +**Solution Applied**: +- All data sources now use `Box` getters +- Type-safe queries via `.whereType()` +- Applied to `favorites_local_datasource.dart` + +**Pattern**: +```dart +Box get _box => Hive.box(boxName); + +Future> getAllFavorites() async { + return _box.values + .whereType() // Type-safe! + .where((fav) => fav.userId == userId) + .toList(); +} +``` + +### 🔄 Next Steps (Planned) +1. Points history page with transaction list +2. Referral program page with share functionality +3. My gifts page with tabs (Active/Used/Expired) +4. Backend API integration for loyalty endpoints +5. Real redemption flow with gift codes + +### 📝 Code Quality Checklist +All recent implementations follow: +- ✅ Clean architecture (data/domain/presentation) +- ✅ Riverpod state management +- ✅ Hive for local persistence +- ✅ Material 3 design system +- ✅ Vietnamese localization +- ✅ AppBar standardization +- ✅ CachedNetworkImage for all remote images +- ✅ Proper error handling +- ✅ Loading states (CircularProgressIndicator) +- ✅ Empty states with helpful messages