diff --git a/lib/core/router/app_router.dart b/lib/core/router/app_router.dart index 1ab4d87..c8a9126 100644 --- a/lib/core/router/app_router.dart +++ b/lib/core/router/app_router.dart @@ -8,6 +8,7 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:worker/features/cart/presentation/pages/cart_page.dart'; import 'package:worker/features/favorites/presentation/pages/favorites_page.dart'; +import 'package:worker/features/loyalty/presentation/pages/loyalty_page.dart'; import 'package:worker/features/loyalty/presentation/pages/rewards_page.dart'; import 'package:worker/features/main/presentation/pages/main_scaffold.dart'; import 'package:worker/features/orders/presentation/pages/order_detail_page.dart'; @@ -101,6 +102,16 @@ class AppRouter { ), ), + // Loyalty Route + GoRoute( + path: RouteNames.loyalty, + name: RouteNames.loyalty, + pageBuilder: (context, state) => MaterialPage( + key: state.pageKey, + child: const LoyaltyPage(), + ), + ), + // Loyalty Rewards Route GoRoute( path: '/loyalty/rewards', diff --git a/lib/features/loyalty/presentation/pages/loyalty_page.dart b/lib/features/loyalty/presentation/pages/loyalty_page.dart new file mode 100644 index 0000000..1a1f985 --- /dev/null +++ b/lib/features/loyalty/presentation/pages/loyalty_page.dart @@ -0,0 +1,473 @@ +/// Page: Loyalty Page +/// +/// Main loyalty program page displaying member card, progress, and features. +library; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:qr_flutter/qr_flutter.dart'; +import 'package:worker/core/constants/ui_constants.dart'; +import 'package:worker/core/theme/colors.dart'; +import 'package:worker/features/loyalty/presentation/providers/loyalty_points_provider.dart'; + +/// Loyalty Page +/// +/// Features: +/// - Diamond member card with QR code +/// - Progress bar to next tier +/// - Quick action menu items +/// - Current tier benefits +class LoyaltyPage extends ConsumerWidget { + const LoyaltyPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final loyaltyPoints = ref.watch(loyaltyPointsProvider); + + return Scaffold( + backgroundColor: const Color(0xFFF4F6F8), + appBar: AppBar( + title: const Text( + 'Hội viên thân thiết', + style: TextStyle(color: Colors.black), + ), + elevation: AppBarSpecs.elevation, + backgroundColor: AppColors.white, + foregroundColor: AppColors.grey900, + centerTitle: false, + automaticallyImplyLeading: false, + ), + body: SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Member Card + _buildMemberCard(loyaltyPoints), + + const SizedBox(height: 16), + + // Progress Card + _buildProgressCard(), + + const SizedBox(height: 16), + + // Loyalty Features Menu + ..._buildLoyaltyMenu(context), + + const SizedBox(height: 16), + + // Current Benefits Card + _buildBenefitsCard(), + ], + ), + ), + ); + } + + /// Build Diamond Member Card + Widget _buildMemberCard(LoyaltyPointsState loyaltyPoints) { + return Container( + height: 200, + decoration: BoxDecoration( + gradient: const LinearGradient( + colors: [Color(0xFF4A00E0), Color(0xFF8E2DE2)], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: const Color(0xFF4A00E0).withValues(alpha: 0.3), + blurRadius: 20, + offset: const Offset(0, 10), + ), + ], + ), + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Top Row: Brand and Valid Through + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'EUROTILE', + style: TextStyle( + color: Colors.white, + fontSize: 24, + fontWeight: FontWeight.w700, + letterSpacing: 1.2, + ), + ), + const SizedBox(height: 4), + Text( + 'ARCHITECT MEMBERSHIP', + style: TextStyle( + color: Colors.white.withValues(alpha: 0.9), + fontSize: 11, + letterSpacing: 0.5, + ), + ), + ], + ), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + 'Valid through', + style: TextStyle( + color: Colors.white.withValues(alpha: 0.8), + fontSize: 11, + ), + ), + const SizedBox(height: 2), + const Text( + '31/12/2025', + style: TextStyle( + color: Colors.white, + fontSize: 14, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ], + ), + + const Spacer(), + + // Bottom Row: User Info and QR Code + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'La Nguyen Quynh', + style: TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 4), + Text( + 'CLASS: DIAMOND', + style: TextStyle( + color: Colors.white.withValues(alpha: 0.9), + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), + Text( + 'Points: ${loyaltyPoints.availablePoints}', + style: TextStyle( + color: Colors.white.withValues(alpha: 0.9), + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + child: QrImageView( + data: '0983441099', + version: QrVersions.auto, + size: 60, + backgroundColor: Colors.white, + ), + ), + ], + ), + ], + ), + ); + } + + /// Build Progress Card + Widget _buildProgressCard() { + return Card( + elevation: 5, + margin: EdgeInsets.zero, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Tiến trình lên hạng', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: AppColors.grey900, + ), + ), + const SizedBox(height: 16), + + // Current and Next Tier + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Hạng hiện tại: DIAMOND', + style: TextStyle( + fontSize: 13, + color: AppColors.grey500, + ), + ), + Text( + 'Hạng kế tiếp: PLATINUM', + style: TextStyle( + fontSize: 13, + color: AppColors.grey500, + ), + ), + ], + ), + + const SizedBox(height: 12), + + // Progress Bar + ClipRRect( + borderRadius: BorderRadius.circular(4), + child: LinearProgressIndicator( + value: 0.65, + minHeight: 8, + backgroundColor: AppColors.grey100, + valueColor: const AlwaysStoppedAnimation( + Color(0xFF4A00E0), + ), + ), + ), + + const SizedBox(height: 12), + + // Points to Next Tier + Center( + child: RichText( + textAlign: TextAlign.center, + text: const TextSpan( + style: TextStyle( + fontSize: 13, + color: AppColors.grey500, + ), + children: [ + TextSpan(text: 'Còn '), + TextSpan( + text: '2,250 điểm', + style: TextStyle( + fontWeight: FontWeight.w600, + color: AppColors.grey900, + ), + ), + TextSpan(text: ' nữa để lên hạng Platinum'), + ], + ), + ), + ), + ], + ), + ), + ); + } + + /// Build Loyalty Menu Items + List _buildLoyaltyMenu(BuildContext context) { + final menuItems = [ + { + 'icon': Icons.card_giftcard, + 'title': 'Đổi quà tặng', + 'subtitle': 'Sử dụng điểm để đổi quà hấp dẫn', + 'route': '/loyalty/rewards', + }, + { + 'icon': Icons.add_circle_outline, + 'title': 'Ghi nhận điểm', + 'subtitle': 'Gửi hóa đơn để nhận điểm thưởng', + 'route': null, + }, + { + 'icon': Icons.history, + 'title': 'Lịch sử điểm', + 'subtitle': 'Xem chi tiết cộng/trừ điểm', + 'route': null, + }, + { + 'icon': Icons.person_add, + 'title': 'Giới thiệu bạn bè', + 'subtitle': 'Nhận thưởng khi giới thiệu thành công', + 'route': null, + }, + { + 'icon': Icons.inventory_2_outlined, + 'title': 'Quà của tôi', + 'subtitle': 'Xem voucher và quà tặng đã đổi', + 'route': null, + }, + { + 'icon': Icons.diamond_outlined, + 'title': 'Quyền lợi hội viên', + 'subtitle': 'Xem các ưu đãi dành cho hạng của bạn', + 'route': null, + }, + ]; + + return menuItems.map((item) { + return Card( + margin: const EdgeInsets.only(bottom: 12), + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + side: const BorderSide(color: AppColors.grey100), + ), + child: InkWell( + onTap: () { + if (item['route'] != null) { + context.push(item['route'] as String); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('${item['title']} - Đang phát triển')), + ); + } + }, + borderRadius: BorderRadius.circular(12), + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + // Icon + Container( + width: 48, + height: 48, + decoration: BoxDecoration( + color: AppColors.primaryBlue.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(12), + ), + child: Icon( + item['icon'] as IconData, + color: AppColors.primaryBlue, + size: 24, + ), + ), + + const SizedBox(width: 16), + + // Title and Subtitle + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item['title'] as String, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + color: AppColors.grey900, + ), + ), + const SizedBox(height: 2), + Text( + item['subtitle'] as String, + style: const TextStyle( + fontSize: 13, + color: AppColors.grey500, + ), + ), + ], + ), + ), + + // Arrow + const Icon( + Icons.chevron_right, + color: AppColors.grey500, + size: 20, + ), + ], + ), + ), + ), + ); + }).toList(); + } + + /// Build Benefits Card + Widget _buildBenefitsCard() { + return Card( + elevation: 1, + margin: EdgeInsets.zero, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Quyền lợi hạng Diamond', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: AppColors.grey900, + ), + ), + const SizedBox(height: 16), + _buildBenefitItem('Chiết khấu 15% cho tất cả sản phẩm'), + _buildBenefitItem('Giao hàng miễn phí cho đơn từ 5 triệu'), + _buildBenefitItem('Ưu tiên xử lý đơn hàng'), + _buildBenefitItem('Tặng 500 điểm vào ngày sinh nhật'), + _buildBenefitItem('Tư vấn thiết kế miễn phí'), + _buildBenefitItem('Mời tham gia sự kiện VIP độc quyền', isLast: true), + ], + ), + ), + ); + } + + /// Build Benefit Item + Widget _buildBenefitItem(String text, {bool isLast = false}) { + return Padding( + padding: EdgeInsets.only(bottom: isLast ? 0 : 12), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Icon( + Icons.check_circle, + size: 20, + color: Color(0xFF4A00E0), + ), + const SizedBox(width: 12), + Expanded( + child: Text( + text, + style: const TextStyle( + fontSize: 14, + color: AppColors.grey900, + height: 1.4, + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/features/main/presentation/pages/main_scaffold.dart b/lib/features/main/presentation/pages/main_scaffold.dart index c5695dd..c0cf474 100644 --- a/lib/features/main/presentation/pages/main_scaffold.dart +++ b/lib/features/main/presentation/pages/main_scaffold.dart @@ -8,6 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:worker/core/theme/colors.dart'; import 'package:worker/features/home/presentation/pages/home_page.dart'; +import 'package:worker/features/loyalty/presentation/pages/loyalty_page.dart'; import 'package:worker/features/main/presentation/providers/current_page_provider.dart'; import 'package:worker/features/promotions/presentation/pages/promotions_page.dart'; @@ -29,7 +30,7 @@ class MainScaffold extends ConsumerWidget { // Define pages final pages = [ const HomePage(), - _buildComingSoonPage('Hội viên'), // Loyalty + const LoyaltyPage(), // Loyalty const PromotionsPage(), _buildComingSoonPage('Thông báo'), // Notifications _buildComingSoonPage('Cài đặt'), // Account