Files
worker/lib/features/home/presentation/pages/home_page.dart
Phuoc Nguyen fc9b5e967f update perf
2025-12-02 17:32:20 +07:00

332 lines
12 KiB
Dart

/// Page: Home Page
///
/// Main dashboard screen of the Worker app.
/// Displays member card, promotions, and quick action sections.
library;
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:go_router/go_router.dart';
import 'package:shimmer/shimmer.dart';
import 'package:worker/core/router/app_router.dart';
import 'package:worker/core/utils/extensions.dart';
import 'package:worker/features/cart/presentation/providers/cart_provider.dart';
import 'package:worker/features/home/presentation/providers/member_card_provider.dart';
import 'package:worker/features/home/presentation/providers/promotions_provider.dart';
import 'package:worker/features/home/presentation/widgets/member_card_widget.dart';
import 'package:worker/features/home/presentation/widgets/promotion_slider.dart';
import 'package:worker/features/home/presentation/widgets/quick_action_section.dart';
import 'package:worker/generated/l10n/app_localizations.dart';
/// Home Page
///
/// Main entry point of the app after login.
/// Shows:
/// - Member card (Diamond/Platinum/Gold)
/// - Promotional banners
/// - Quick action sections
/// - Bottom navigation
/// - Floating action button (Chat)
///
/// Initializes cart on mount to load items from API.
class HomePage extends ConsumerStatefulWidget {
const HomePage({super.key});
@override
ConsumerState<HomePage> createState() => _HomePageState();
}
class _HomePageState extends ConsumerState<HomePage> {
@override
void initState() {
super.initState();
// Initialize cart from API on app startup
WidgetsBinding.instance.addPostFrameCallback((_) {
ref.read(cartProvider.notifier).initialize();
});
}
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context);
// Watch member card state
final memberCardAsync = ref.watch(memberCardProvider);
// Watch promotions state
final promotionsAsync = ref.watch(promotionsProvider);
// Watch cart item count
final cartItemCount = ref.watch(cartItemCountProvider);
final colorScheme = context.colorScheme;
return Scaffold(
backgroundColor: colorScheme.surfaceContainerLowest,
body: CustomScrollView(
slivers: [
// Add top padding for status bar
SliverPadding(
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
),
// Member Card Section
SliverToBoxAdapter(
child: memberCardAsync.when(
data: (memberCard) => MemberCardWidget(memberCard: memberCard),
loading: () => Container(
margin: const EdgeInsets.all(16),
height: 200,
decoration: BoxDecoration(
color: colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(16),
),
child: const Center(child: CircularProgressIndicator()),
),
error: (error, stack) => Container(
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: colorScheme.errorContainer,
borderRadius: BorderRadius.circular(16),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
FaIcon(
FontAwesomeIcons.circleExclamation,
color: colorScheme.error,
size: 48,
),
const SizedBox(height: 8),
Text(
l10n.error,
style: TextStyle(
color: colorScheme.error,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 4),
Text(
error.toString(),
style: TextStyle(
color: colorScheme.onSurfaceVariant,
fontSize: 12,
),
textAlign: TextAlign.center,
),
],
),
),
),
),
// Promotions Section
SliverToBoxAdapter(
child: promotionsAsync.when(
data: (promotions) => promotions.isNotEmpty
? PromotionSlider(
promotions: promotions,
onPromotionTap: (promotion) {
// Navigate to promotion details
context.push('/promotions/${promotion.id}');
},
)
: const SizedBox.shrink(),
loading: () => _buildPromotionsShimmer(colorScheme),
error: (error, stack) => const SizedBox.shrink(),
),
),
// Quick Action Sections
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
children: [
// Products & Cart Section
QuickActionSection(
title: 'Sản phẩm & Giỏ hàng',
actions: [
QuickAction(
icon: FontAwesomeIcons.grip,
label: 'Sản phẩm',
onTap: () => context.pushNamed(RouteNames.products),
),
QuickAction(
icon: FontAwesomeIcons.cartShopping,
label: 'Giỏ hàng',
badge: cartItemCount > 0 ? '$cartItemCount' : null,
onTap: () => context.push(RouteNames.cart),
),
QuickAction(
icon: FontAwesomeIcons.heart,
label: 'Yêu thích',
onTap: () => context.push(RouteNames.favorites),
),
],
),
// Orders & Payments Section
QuickActionSection(
title: 'Đơn hàng & thanh toán',
actions: [
QuickAction(
icon: FontAwesomeIcons.fileLines,
label: 'Chính sách giá',
onTap: () => context.push(RouteNames.pricePolicy),
),
QuickAction(
icon: FontAwesomeIcons.boxesStacked,
label: 'Đơn hàng',
onTap: () => context.push(RouteNames.orders),
),
QuickAction(
icon: FontAwesomeIcons.receipt,
label: 'Thanh toán',
onTap: () => context.push(RouteNames.payments),
),
],
),
// Loyalty Section
QuickActionSection(
title: 'Khách hàng thân thiết',
actions: [
QuickAction(
icon: FontAwesomeIcons.circlePlus,
label: 'Ghi nhận điểm',
onTap: () => context.push(RouteNames.pointsRecords),
),
QuickAction(
icon: FontAwesomeIcons.gift,
label: 'Đổi quà',
onTap: () => context.push('/loyalty/rewards'),
),
QuickAction(
icon: FontAwesomeIcons.clockRotateLeft,
label: 'Lịch sử điểm',
onTap: () => context.push(RouteNames.pointsHistory),
),
],
),
// Sample Houses & News Section
QuickActionSection(
title: 'Nhà mẫu, dự án & tin tức',
actions: [
QuickAction(
icon: FontAwesomeIcons.houseCircleCheck,
label: 'Nhà mẫu',
onTap: () => context.push(RouteNames.modelHouses),
),
QuickAction(
icon: FontAwesomeIcons.building,
label: 'Đăng ký dự án',
onTap: () => context.push(RouteNames.submissions),
),
],
),
// Bottom Padding (for bottom nav clearance)
const SizedBox(height: 40),
],
),
),
),
],
),
);
}
/// Build shimmer loading for promotions section
Widget _buildPromotionsShimmer(ColorScheme colorScheme) {
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Title shimmer
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
'Chương trình ưu đãi',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: colorScheme.onSurface,
),
),
),
const SizedBox(height: 12),
// Cards shimmer
SizedBox(
height: 210,
child: ListView.builder(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 16),
itemCount: 3,
itemBuilder: (context, index) {
return Shimmer.fromColors(
baseColor: colorScheme.surfaceContainerHighest,
highlightColor: colorScheme.surface,
child: Container(
width: 280,
margin: const EdgeInsets.only(right: 12),
decoration: BoxDecoration(
color: colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Image placeholder
Container(
height: 140,
decoration: BoxDecoration(
color: colorScheme.surfaceContainerHighest,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(12),
),
),
),
// Text placeholders
Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 200,
height: 16,
decoration: BoxDecoration(
color: colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(4),
),
),
const SizedBox(height: 8),
Container(
width: 140,
height: 12,
decoration: BoxDecoration(
color: colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(4),
),
),
],
),
),
],
),
),
);
},
),
),
],
),
);
}
}