diff --git a/lib/presentation/pages/poin/pages/product_redeem/product_redeem_page.dart b/lib/presentation/pages/poin/pages/product_redeem/product_redeem_page.dart index 7e3c08e..635c410 100644 --- a/lib/presentation/pages/poin/pages/product_redeem/product_redeem_page.dart +++ b/lib/presentation/pages/poin/pages/product_redeem/product_redeem_page.dart @@ -7,13 +7,11 @@ import '../../poin_page.dart'; @RoutePage() class ProductRedeemPage extends StatefulWidget { final Product product; - final Merchant merchant; final PointCard pointCard; const ProductRedeemPage({ super.key, required this.product, - required this.merchant, required this.pointCard, }); @@ -171,31 +169,6 @@ class _ProductRedeemPageState extends State child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // Merchant Info - Container( - padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), - decoration: BoxDecoration( - color: AppColor.primary.withOpacity(0.1), - borderRadius: BorderRadius.circular(20), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text(widget.merchant.logo, style: TextStyle(fontSize: 16)), - SizedBox(width: 6), - Text( - widget.merchant.name, - style: AppStyle.sm.copyWith( - color: AppColor.primary, - fontWeight: FontWeight.w600, - ), - ), - ], - ), - ), - - SizedBox(height: 16), - // Product Name & Popular Badge Row( children: [ @@ -591,13 +564,6 @@ class _ProductRedeemPageState extends State ), ), SizedBox(height: 12), - Text( - "Show this code at ${widget.merchant.name}", - style: AppStyle.sm.copyWith( - color: AppColor.textSecondary, - ), - textAlign: TextAlign.center, - ), ], ), ), diff --git a/lib/presentation/pages/poin/poin_page.dart b/lib/presentation/pages/poin/poin_page.dart index 8a1c45b..5df1beb 100644 --- a/lib/presentation/pages/poin/poin_page.dart +++ b/lib/presentation/pages/poin/poin_page.dart @@ -19,20 +19,6 @@ class PointCard { int get availablePoints => totalPoints - usedPoints; } -class Merchant { - final String id; - final String name; - final String logo; - final List categories; - - Merchant({ - required this.id, - required this.name, - required this.logo, - required this.categories, - }); -} - class Category { final String id; final String name; @@ -82,142 +68,144 @@ class PoinPage extends StatefulWidget { class _PoinPageState extends State { final ScrollController _scrollController = ScrollController(); - // Sample data + // Sample data - Indonesian content final PointCard pointCard = PointCard( totalPoints: 15000, usedPoints: 3500, - membershipLevel: "Gold Member", + membershipLevel: "Member Emas", ); - final List merchants = [ - Merchant( - id: "1", - name: "Starbucks", - logo: "☕", - categories: [ - Category( - id: "c1", - name: "Beverages", - icon: "🥤", - products: [ - Product( - id: "p1", - name: "Americano", - image: "☕", - pointsRequired: 2500, - description: "Classic black coffee", - isPopular: true, - ), - Product( - id: "p2", - name: "Cappuccino", - image: "☕", - pointsRequired: 3000, - description: "Espresso with steamed milk", - ), - Product( - id: "p3", - name: "Frappuccino", - image: "🥤", - pointsRequired: 4000, - description: "Iced blended coffee", - ), - ], + final List categories = [ + Category( + id: "c1", + name: "Minuman", + icon: "🥤", + products: [ + Product( + id: "p1", + name: "Es Teh Manis", + image: "🧊", + pointsRequired: 1500, + description: "Teh manis dingin segar", + isPopular: true, ), - Category( - id: "c2", - name: "Food", - icon: "🍰", - products: [ - Product( - id: "p4", - name: "Croissant", - image: "🥐", - pointsRequired: 1500, - description: "Buttery pastry", - ), - Product( - id: "p5", - name: "Sandwich", - image: "🥪", - pointsRequired: 3500, - description: "Fresh deli sandwich", - ), - ], + Product( + id: "p2", + name: "Kopi Susu", + image: "☕", + pointsRequired: 2000, + description: "Kopi dengan susu creamy", + ), + Product( + id: "p3", + name: "Jus Jeruk", + image: "🍊", + pointsRequired: 2500, + description: "Jus jeruk segar alami", ), ], ), - Merchant( - id: "2", - name: "McDonald's", - logo: "🍔", - categories: [ - Category( - id: "c3", - name: "Burgers", - icon: "🍔", - products: [ - Product( - id: "p6", - name: "Big Mac", - image: "🍔", - pointsRequired: 5000, - description: "Iconic double burger", - isPopular: true, - ), - Product( - id: "p7", - name: "Quarter Pounder", - image: "🍔", - pointsRequired: 4500, - description: "Fresh beef quarter pound", - ), - ], + Category( + id: "c2", + name: "Makanan", + icon: "🍽️", + products: [ + Product( + id: "p4", + name: "Nasi Gudeg", + image: "🍛", + pointsRequired: 4000, + description: "Gudeg Jogja autentik", + isPopular: true, ), - Category( - id: "c4", - name: "Drinks", - icon: "🥤", - products: [ - Product( - id: "p8", - name: "Coca Cola", - image: "🥤", - pointsRequired: 1000, - description: "Ice cold soda", - ), - Product( - id: "p9", - name: "McCafe Coffee", - image: "☕", - pointsRequired: - 20000, // High points untuk demonstrasi insufficient - description: "Premium coffee blend", - ), - ], + Product( + id: "p5", + name: "Gado-gado", + image: "🥗", + pointsRequired: 3500, + description: "Sayuran dengan bumbu kacang", + ), + Product( + id: "p6", + name: "Bakso", + image: "🍲", + pointsRequired: 3000, + description: "Bakso sapi dengan mie", + ), + ], + ), + Category( + id: "c3", + name: "Cemilan", + icon: "🍪", + products: [ + Product( + id: "p7", + name: "Keripik Singkong", + image: "🥔", + pointsRequired: 1000, + description: "Keripik singkong renyah", + ), + Product( + id: "p8", + name: "Onde-onde", + image: "🍡", + pointsRequired: 1500, + description: "Onde-onde isi kacang hijau", + ), + Product( + id: "p9", + name: "Pisang Goreng", + image: "🍌", + pointsRequired: 1200, + description: "Pisang goreng krispy", + ), + ], + ), + Category( + id: "c4", + name: "Voucher", + icon: "🎟️", + products: [ + Product( + id: "p10", + name: "Diskon 50%", + image: "🏷️", + pointsRequired: 5000, + description: "Potongan harga 50% untuk semua menu", + isPopular: true, + ), + Product( + id: "p11", + name: "Gratis Ongkir", + image: "🚚", + pointsRequired: 2000, + description: "Bebas ongkos kirim untuk pesanan apapun", + ), + Product( + id: "p12", + name: "Buy 1 Get 1", + image: "🎁", + pointsRequired: 25000, // High points untuk demonstrasi insufficient + description: "Beli 1 gratis 1 untuk minuman", ), ], ), ]; - Merchant? selectedMerchant; Map categoryKeys = {}; - String? selectedMerchantId; // Track merchant ID untuk detect changes String? activeCategoryId; // Track active category @override void initState() { super.initState(); - selectedMerchant = merchants.first; - selectedMerchantId = selectedMerchant?.id; - activeCategoryId = - selectedMerchant?.categories.first.id; // Set first category as active + activeCategoryId = categories.first.id; // Set first category as active _initializeCategoryKeys(); } void _initializeCategoryKeys() { categoryKeys.clear(); - for (var category in selectedMerchant?.categories ?? []) { + for (var category in categories) { categoryKeys[category.id] = GlobalKey(); } } @@ -264,30 +252,6 @@ class _PoinPageState extends State { }); } - void _onMerchantChanged(Merchant merchant) { - if (selectedMerchantId == merchant.id) - return; // Prevent unnecessary rebuilds - - setState(() { - selectedMerchant = merchant; - selectedMerchantId = merchant.id; - // Reset active category to first category of new merchant - activeCategoryId = merchant.categories.first.id; - }); - - // Reinitialize category keys for new merchant - immediate call - _initializeCategoryKeys(); - - // Auto scroll to top when merchant changes - if (_scrollController.hasClients) { - _scrollController.animateTo( - 0, - duration: Duration(milliseconds: 300), - curve: Curves.easeOut, - ); - } - } - @override Widget build(BuildContext context) { return Scaffold( @@ -309,34 +273,26 @@ class _PoinPageState extends State { // Point Card Section SliverToBoxAdapter(child: _buildPointCard()), - // Merchant Selection - SliverToBoxAdapter(child: _buildMerchantSelection()), - // Sticky Category Tabs SliverPersistentHeader( pinned: true, - key: ValueKey( - '${selectedMerchant?.id}_$activeCategoryId', - ), // Force rebuild with key + key: ValueKey(activeCategoryId), // Simplified key delegate: _StickyHeaderDelegate( child: _buildCategoryTabs(), height: 66, - merchantId: selectedMerchant?.id ?? '', // Pass merchant ID activeCategoryId: activeCategoryId, // Pass active category ID ), ), ]; }, - body: selectedMerchant == null - ? SizedBox.shrink() - : ListView.builder( - padding: EdgeInsets.only(top: 16), - itemCount: selectedMerchant!.categories.length, - itemBuilder: (context, index) { - final category = selectedMerchant!.categories[index]; - return _buildCategorySection(category); - }, - ), + body: ListView.builder( + padding: EdgeInsets.only(top: 16), + itemCount: categories.length, + itemBuilder: (context, index) { + final category = categories[index]; + return _buildCategorySection(category); + }, + ), ), ); } @@ -385,7 +341,7 @@ class _PoinPageState extends State { ), ), Text( - "Available Points", + "Poin Tersedia", style: AppStyle.sm.copyWith( color: AppColor.textWhite.withOpacity(0.9), ), @@ -431,7 +387,7 @@ class _PoinPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - "Used: ${pointCard.usedPoints}", + "Terpakai: ${pointCard.usedPoints}", style: AppStyle.xs.copyWith( color: AppColor.textWhite.withOpacity(0.8), ), @@ -449,74 +405,7 @@ class _PoinPageState extends State { ); } - Widget _buildMerchantSelection() { - return Container( - margin: EdgeInsets.symmetric(horizontal: 16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Select Merchant", - style: AppStyle.lg.copyWith( - fontWeight: FontWeight.w600, - color: AppColor.textPrimary, - ), - ), - SizedBox(height: 12), - Container( - height: 80, - child: ListView.builder( - scrollDirection: Axis.horizontal, - itemCount: merchants.length, - itemBuilder: (context, index) { - final merchant = merchants[index]; - final isSelected = selectedMerchant?.id == merchant.id; - - return GestureDetector( - onTap: () => _onMerchantChanged(merchant), - child: Container( - width: 80, - margin: EdgeInsets.only(right: 12), - decoration: BoxDecoration( - color: isSelected ? AppColor.primary : AppColor.white, - borderRadius: BorderRadius.circular(12), - border: Border.all( - color: isSelected ? AppColor.primary : AppColor.border, - width: 2, - ), - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(merchant.logo, style: TextStyle(fontSize: 24)), - SizedBox(height: 4), - Text( - merchant.name, - style: AppStyle.xs.copyWith( - color: isSelected - ? AppColor.textWhite - : AppColor.textPrimary, - fontWeight: FontWeight.w500, - ), - textAlign: TextAlign.center, - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - ], - ), - ), - ); - }, - ), - ), - ], - ), - ); - } - Widget _buildCategoryTabs() { - if (selectedMerchant == null) return SizedBox.shrink(); - return Container( color: AppColor.background, // Background untuk sticky header padding: EdgeInsets.symmetric(vertical: 8), @@ -525,9 +414,9 @@ class _PoinPageState extends State { child: ListView.builder( scrollDirection: Axis.horizontal, padding: EdgeInsets.symmetric(horizontal: 16), - itemCount: selectedMerchant!.categories.length, + itemCount: categories.length, itemBuilder: (context, index) { - final category = selectedMerchant!.categories[index]; + final category = categories[index]; final isActive = activeCategoryId == category.id; // Check if this category is active @@ -607,7 +496,7 @@ class _PoinPageState extends State { SizedBox(height: 12), // Fixed height yang lebih besar untuk menghindari overflow Container( - height: 240, // Increased from 200 to 240 + height: 240, child: ListView.builder( scrollDirection: Axis.horizontal, padding: EdgeInsets.symmetric(horizontal: 16), @@ -651,7 +540,7 @@ class _PoinPageState extends State { children: [ // Product Image Container( - height: 90, // Reduced from 100 to give more space below + height: 90, width: double.infinity, decoration: BoxDecoration( color: AppColor.backgroundLight, @@ -664,16 +553,16 @@ class _PoinPageState extends State { Center( child: Text( product.image, - style: TextStyle(fontSize: 36), // Reduced from 40 + style: TextStyle(fontSize: 36), ), ), if (product.isPopular) Positioned( - top: 6, // Adjusted position + top: 6, right: 6, child: Container( padding: EdgeInsets.symmetric( - horizontal: 6, // Reduced padding + horizontal: 6, vertical: 3, ), decoration: BoxDecoration( @@ -681,11 +570,11 @@ class _PoinPageState extends State { borderRadius: BorderRadius.circular(10), ), child: Text( - "Popular", + "Populer", style: AppStyle.xs.copyWith( color: AppColor.white, fontWeight: FontWeight.w600, - fontSize: 10, // Smaller font + fontSize: 10, ), ), ), @@ -694,7 +583,7 @@ class _PoinPageState extends State { ), ), - // Product Info - Made more flexible + // Product Info Expanded( child: Padding( padding: EdgeInsets.all(12), @@ -730,7 +619,7 @@ class _PoinPageState extends State { children: [ Icon( Icons.stars, - size: 14, // Reduced size + size: 14, color: canRedeem ? AppColor.warning : AppColor.textLight, @@ -753,7 +642,7 @@ class _PoinPageState extends State { SizedBox(height: 8), SizedBox( width: double.infinity, - height: 32, // Reduced from 36 + height: 32, child: ElevatedButton( onPressed: canRedeem ? () => _redeemProduct(product) @@ -770,9 +659,8 @@ class _PoinPageState extends State { ), child: FittedBox( child: Text( - canRedeem ? "Redeem" : "Insufficient", + canRedeem ? "Tukar" : "Poin Kurang", style: AppStyle.xs.copyWith( - // Changed to xs fontWeight: FontWeight.w600, color: AppColor.white, ), @@ -797,10 +685,7 @@ class _PoinPageState extends State { ), child: Center( child: Container( - padding: EdgeInsets.symmetric( - horizontal: 12, - vertical: 10, - ), // Reduced padding + padding: EdgeInsets.symmetric(horizontal: 12, vertical: 10), decoration: BoxDecoration( color: AppColor.white, borderRadius: BorderRadius.circular(12), @@ -811,23 +696,23 @@ class _PoinPageState extends State { Icon( Icons.lock_outline, color: AppColor.textSecondary, - size: 20, // Reduced size + size: 20, ), SizedBox(height: 4), Text( - "Need ${pointsShortage}", + "Butuh ${pointsShortage}", style: AppStyle.xs.copyWith( fontWeight: FontWeight.w600, color: AppColor.textSecondary, - fontSize: 10, // Smaller font + fontSize: 10, ), textAlign: TextAlign.center, ), Text( - "more points", + "poin lagi", style: AppStyle.xs.copyWith( color: AppColor.textSecondary, - fontSize: 10, // Smaller font + fontSize: 10, ), textAlign: TextAlign.center, ), @@ -843,11 +728,7 @@ class _PoinPageState extends State { void _redeemProduct(Product product) { context.router.push( - ProductRedeemRoute( - product: product, - merchant: selectedMerchant!, - pointCard: pointCard, - ), + ProductRedeemRoute(product: product, pointCard: pointCard), ); } @@ -862,13 +743,11 @@ class _PoinPageState extends State { class _StickyHeaderDelegate extends SliverPersistentHeaderDelegate { final Widget child; final double height; - final String merchantId; // Add merchant ID to track changes - final String? activeCategoryId; // Add active category to track changes + final String? activeCategoryId; // Track active category _StickyHeaderDelegate({ required this.child, required this.height, - required this.merchantId, // Track merchant changes required this.activeCategoryId, // Track category changes }); @@ -889,22 +768,16 @@ class _StickyHeaderDelegate extends SliverPersistentHeaderDelegate { @override bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) { - // Always rebuild when merchant OR active category changes + // Rebuild when active category changes if (oldDelegate is _StickyHeaderDelegate) { - bool merchantChanged = oldDelegate.merchantId != merchantId; bool categoryChanged = oldDelegate.activeCategoryId != activeCategoryId; - print( - "shouldRebuild - Merchant changed: $merchantChanged, Category changed: $categoryChanged", - ); - print( - "Old merchant: ${oldDelegate.merchantId}, New merchant: $merchantId", - ); + print("shouldRebuild - Category changed: $categoryChanged"); print( "Old category: ${oldDelegate.activeCategoryId}, New category: $activeCategoryId", ); - return merchantChanged || categoryChanged; + return categoryChanged; } return true; } diff --git a/lib/presentation/router/app_router.gr.dart b/lib/presentation/router/app_router.gr.dart index 95cbfe3..7f9c5f9 100644 --- a/lib/presentation/router/app_router.gr.dart +++ b/lib/presentation/router/app_router.gr.dart @@ -403,7 +403,6 @@ class ProductRedeemRoute extends _i23.PageRouteInfo { ProductRedeemRoute({ _i24.Key? key, required _i16.Product product, - required _i16.Merchant merchant, required _i16.PointCard pointCard, List<_i23.PageRouteInfo>? children, }) : super( @@ -411,7 +410,6 @@ class ProductRedeemRoute extends _i23.PageRouteInfo { args: ProductRedeemRouteArgs( key: key, product: product, - merchant: merchant, pointCard: pointCard, ), initialChildren: children, @@ -426,7 +424,6 @@ class ProductRedeemRoute extends _i23.PageRouteInfo { return _i17.ProductRedeemPage( key: args.key, product: args.product, - merchant: args.merchant, pointCard: args.pointCard, ); }, @@ -437,7 +434,6 @@ class ProductRedeemRouteArgs { const ProductRedeemRouteArgs({ this.key, required this.product, - required this.merchant, required this.pointCard, }); @@ -445,13 +441,11 @@ class ProductRedeemRouteArgs { final _i16.Product product; - final _i16.Merchant merchant; - final _i16.PointCard pointCard; @override String toString() { - return 'ProductRedeemRouteArgs{key: $key, product: $product, merchant: $merchant, pointCard: $pointCard}'; + return 'ProductRedeemRouteArgs{key: $key, product: $product, pointCard: $pointCard}'; } }