/// Notifications Page /// /// Displays user notifications with category tabs (General/Orders). /// Features: /// - Tab navigation for filtering notifications /// - Unread notification indicators (blue left border) /// - Pull-to-refresh /// - Empty state when no notifications library; import 'package:flutter/material.dart' hide Notification; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:worker/core/constants/ui_constants.dart'; import 'package:worker/core/theme/colors.dart'; import 'package:worker/features/notifications/domain/entities/notification.dart'; import 'package:worker/features/notifications/presentation/providers/notifications_provider.dart'; import 'package:worker/features/notifications/presentation/widgets/notification_card.dart'; /// Notifications Page /// /// Main notifications screen accessible from bottom navigation. class NotificationsPage extends HookConsumerWidget { const NotificationsPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { // Use Flutter hooks for local state management final selectedCategory = useState('general'); final notificationsAsync = ref.watch( filteredNotificationsProvider(selectedCategory.value), ); return Scaffold( backgroundColor: const Color(0xFFF4F6F8), body: SafeArea( child: Column( children: [ // Header _buildHeader(), // Tabs _buildTabs(context, selectedCategory), // Notifications List Expanded( child: notificationsAsync.when( data: (notifications) => _buildNotificationsList( context, ref, notifications, selectedCategory, ), loading: () => const Center(child: CircularProgressIndicator()), error: (error, stack) => _buildErrorState(ref, selectedCategory), ), ), ], ), ), ); } /// Build header Widget _buildHeader() { return Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), decoration: BoxDecoration( color: Colors.white, boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.05), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: const Text( 'Thông báo', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Color(0xFF212121), ), ), ); } /// Build category tabs Widget _buildTabs( BuildContext context, ValueNotifier selectedCategory, ) { return Container( padding: const EdgeInsets.all(AppSpacing.md), child: Row( children: [ Expanded( child: _buildTabButton( context, selectedCategory, label: 'Chung', category: 'general', isSelected: selectedCategory.value == 'general', ), ), const SizedBox(width: AppSpacing.sm), Expanded( child: _buildTabButton( context, selectedCategory, label: 'Đơn hàng', category: 'order', isSelected: selectedCategory.value == 'order', ), ), ], ), ); } /// Build tab button Widget _buildTabButton( BuildContext context, ValueNotifier selectedCategory, { required String label, required String category, required bool isSelected, }) { return ElevatedButton( onPressed: () { selectedCategory.value = category; }, style: ElevatedButton.styleFrom( backgroundColor: isSelected ? AppColors.primaryBlue : Colors.white, foregroundColor: isSelected ? Colors.white : const Color(0xFF64748B), elevation: 0, padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppRadius.button), side: BorderSide( color: isSelected ? AppColors.primaryBlue : const Color(0xFFE2E8F0), width: 1, ), ), ), child: Text( label, style: TextStyle( fontSize: 14, fontWeight: isSelected ? FontWeight.w600 : FontWeight.w500, ), ), ); } /// Build notifications list Widget _buildNotificationsList( BuildContext context, WidgetRef ref, List notifications, ValueNotifier selectedCategory, ) { if (notifications.isEmpty) { return _buildEmptyState(); } return RefreshIndicator( onRefresh: () async { ref.invalidate(filteredNotificationsProvider(selectedCategory.value)); }, child: ListView.builder( padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md), itemCount: notifications.length, itemBuilder: (context, index) { final notification = notifications[index]; return NotificationCard( notification: notification, onTap: () { // TODO: Handle notification tap (mark as read, navigate to related entity) _handleNotificationTap(context, ref, notification); }, ); }, ), ); } /// Build empty state Widget _buildEmptyState() { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ FaIcon( FontAwesomeIcons.bell, size: 64, color: AppColors.grey500.withValues(alpha: 0.5), ), const SizedBox(height: 16), const Text( 'Không có thông báo', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: AppColors.grey500, ), ), const SizedBox(height: 8), const Text( 'Bạn chưa có thông báo nào', style: TextStyle(fontSize: 14, color: AppColors.grey500), ), ], ), ); } /// Build error state Widget _buildErrorState( WidgetRef ref, ValueNotifier selectedCategory, ) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ FaIcon( FontAwesomeIcons.circleExclamation, size: 64, color: AppColors.danger.withValues(alpha: 0.5), ), const SizedBox(height: 16), const Text( 'Không thể tải thông báo', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: AppColors.grey500, ), ), const SizedBox(height: 16), ElevatedButton.icon( onPressed: () { ref.invalidate( filteredNotificationsProvider(selectedCategory.value), ); }, icon: const FaIcon(FontAwesomeIcons.arrowsRotate, size: 18), label: const Text('Thử lại'), style: ElevatedButton.styleFrom( backgroundColor: AppColors.primaryBlue, foregroundColor: Colors.white, ), ), ], ), ); } /// Handle notification tap void _handleNotificationTap( BuildContext context, WidgetRef ref, Notification notification, ) { // Mark as read if unread if (notification.isUnread) { // TODO: Implement mark as read ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Đã đánh dấu đã đọc'), duration: Duration(seconds: 1), ), ); } // Navigate to related entity if exists // TODO: Implement navigation based on notification type } }