MIF_E31222656/lib/screens/admin/admin_dashboard.dart

1450 lines
44 KiB
Dart

import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:tugas_akhir_supabase/core/theme/app_colors.dart';
import 'package:tugas_akhir_supabase/screens/admin/user_management.dart';
import 'package:tugas_akhir_supabase/screens/admin/guide_management.dart';
import 'package:tugas_akhir_supabase/screens/admin/crop_management.dart';
import 'package:tugas_akhir_supabase/screens/admin/community_management.dart';
import 'package:tugas_akhir_supabase/screens/admin/news_management.dart';
import 'package:tugas_akhir_supabase/services/auth_services.dart';
import 'package:tugas_akhir_supabase/services/user_presence_service.dart';
import 'package:get_it/get_it.dart';
import 'package:intl/intl.dart';
import 'dart:ui' as ui;
class AdminDashboard extends StatefulWidget {
const AdminDashboard({super.key});
@override
State<AdminDashboard> createState() => _AdminDashboardState();
}
class _AdminDashboardState extends State<AdminDashboard> {
final _authServices = GetIt.instance<AuthServices>();
UserPresenceService? _presenceService;
bool _isLoading = true;
bool _isAdmin = false;
int _currentIndex = 0;
// Statistik dashboard
int _totalUsers = 0;
int _activeUsers = 0;
int _totalPosts = 0;
int _totalGuides = 0;
int _totalNews = 0;
final int _totalCrops = 0;
// Statistik tambahan
final int _newUsersToday = 0;
final int _newPostsToday = 0;
final List<Map<String, dynamic>> _recentActivities = [];
// Real-time active users
int _realtimeActiveUsers = 0;
final Map<String, Map<String, dynamic>> _onlineUsers = {};
// Status sistem
final bool _systemStatus = true;
String _lastUpdated = '';
// Professional Color Scheme
static const Color primaryGreen = Color(0xFF0F6848);
static const Color lightGreen = Color(0xFF4CAF50);
static const Color accentGreen = Color(0xFF66BB6A);
static const Color surfaceGreen = Color(0xFFEDF7ED);
static const Color bgWhite = Color(0xFFF8F9FA);
static const Color cardWhite = Colors.white;
static const Color textPrimary = Color(0xFF1E293B);
static const Color textSecondary = Color(0xFF64748B);
static const Color dividerColor = Color(0xFFE2E8F0);
static const Color accentBlue = Color(0xFF2563EB);
static const Color accentOrange = Color(0xFFEA580C);
static const Color chartGreen = Color(0xFF10B981);
static const Color chartBlue = Color(0xFF3B82F6);
static const Color chartOrange = Color(0xFFF97316);
static const Color chartPurple = Color(0xFF8B5CF6);
@override
void initState() {
super.initState();
// Safely initialize the presence service
try {
if (GetIt.instance.isRegistered<UserPresenceService>()) {
_presenceService = GetIt.instance<UserPresenceService>();
// Listen to online users count updates
_presenceService?.onlineUsersStream.listen((count) {
if (mounted) {
setState(() {
// Update the active users count
_activeUsers = count;
_realtimeActiveUsers = count;
});
}
});
} else {
debugPrint('UserPresenceService is not registered in GetIt');
// Register the service if needed
final currentUser = Supabase.instance.client.auth.currentUser;
if (currentUser != null) {
debugPrint('Registering UserPresenceService for admin dashboard');
GetIt.instance.registerSingleton<UserPresenceService>(
UserPresenceService(),
);
_presenceService = GetIt.instance<UserPresenceService>();
_presenceService?.initialize().then((_) {
_presenceService?.onlineUsersStream.listen((count) {
if (mounted) {
setState(() {
_activeUsers = count;
_realtimeActiveUsers = count;
});
}
});
});
}
}
} catch (e) {
debugPrint('Error initializing UserPresenceService: $e');
}
_checkAdminAccess();
_loadDashboardStats();
// Tambahkan refresh otomatis setelah beberapa detik
// untuk memastikan data terambil dengan benar
Future.delayed(const Duration(seconds: 2), () {
if (mounted) {
_refreshDashboardData();
}
});
}
@override
void dispose() {
super.dispose();
}
// Load dashboard statistics from Supabase
Future<void> _loadDashboardStats() async {
if (!mounted) return;
if (mounted) {
setState(() => _isLoading = true);
}
try {
debugPrint('🔍 Starting to load dashboard stats...');
final now = DateTime.now();
final formatter = DateFormat('dd MMM yyyy HH:mm');
final client = Supabase.instance.client;
// Fetch user count using the same approach as UserManagement
try {
debugPrint('🔍 Attempting to fetch users with get_all_users RPC...');
// Try to execute the get_all_users function
final response = await client.rpc('get_all_users');
debugPrint('🔍 get_all_users response type: ${response.runtimeType}');
debugPrint('🔍 get_all_users response length: ${response.length}');
debugPrint('🔍 get_all_users response: $response');
// Handle different response types
int userCount = 0;
if (response is List) {
userCount = response.length;
debugPrint('✅ Parsed ${userCount} users from List response');
} else if (response is Map) {
userCount = 1;
debugPrint('✅ Parsed 1 user from Map response');
} else {
debugPrint('⚠️ Unknown response type, trying to convert...');
try {
final users = List<Map<String, dynamic>>.from(response);
userCount = users.length;
debugPrint('✅ Converted to ${userCount} users');
} catch (e) {
debugPrint('❌ Failed to convert response: $e');
// Try to get length directly
try {
final length = response.length;
userCount = length ?? 0;
debugPrint('✅ Got length directly: $userCount');
} catch (e2) {
debugPrint('❌ Failed to get length directly: $e2');
userCount = 0;
}
}
}
if (mounted) {
setState(() {
_totalUsers = userCount;
debugPrint('✅ Set _totalUsers to $userCount');
debugPrint(
'🔍 User count verification: userCount=$userCount, _totalUsers=$_totalUsers',
);
});
debugPrint('✅ setState called for userCount: $userCount');
// Verify the value was set
debugPrint(
'✅ Verification: _totalUsers after setState = $_totalUsers',
);
} else {
debugPrint('❌ Widget not mounted, cannot setState');
}
} catch (e) {
debugPrint('❌ Error fetching users with get_all_users: $e');
// Fallback to manual query if the RPC fails
try {
debugPrint('🔍 Falling back to manual profiles query...');
// Directly fetch all users from profiles table
final profilesResponse = await client
.from('profiles')
.select('*')
.order('created_at', ascending: false);
debugPrint('🔍 Profiles loaded: ${profilesResponse.length}');
// Convert to List<Map>
final profiles = List<Map<String, dynamic>>.from(profilesResponse);
if (mounted) {
setState(() {
_totalUsers = profiles.length;
debugPrint(
'✅ Fetched ${profiles.length} users from profiles table',
);
});
}
} catch (fallbackError) {
debugPrint('❌ Even fallback query failed: $fallbackError');
// Set a default value to prevent issues
if (mounted) {
setState(() {
_totalUsers = 0;
});
}
}
}
debugPrint(
'📊 Current stats after user fetch: Users: $_totalUsers, Active: $_activeUsers',
);
// Fetch guides count
try {
debugPrint('🔍 Fetching guides...');
final guidesResponse = await client.from('farming_guides').select();
if (mounted) {
setState(() {
_totalGuides = guidesResponse.length;
debugPrint('✅ Fetched ${guidesResponse.length} guides');
});
}
} catch (e) {
debugPrint('❌ Error fetching guides count: $e');
if (mounted) {
setState(() {
_totalGuides = 0;
});
}
}
debugPrint(
'📊 Current stats: Users: $_totalUsers, Guides: $_totalGuides',
);
// Fetch news count
try {
final newsResponse = await client.from('saved_news').select();
if (mounted) {
setState(() {
_totalNews = newsResponse.length;
debugPrint('Fetched ${newsResponse.length} news articles');
});
}
} catch (e) {
debugPrint('Error fetching news count: $e');
if (mounted) {
setState(() {
_totalNews = 0;
});
}
}
// Fetch community messages count
try {
final postsResponse = await client.from('community_messages').select();
if (mounted) {
setState(() {
_totalPosts = postsResponse.length;
debugPrint('Fetched ${postsResponse.length} community posts');
});
}
} catch (e) {
debugPrint('Error fetching community posts count: $e');
if (mounted) {
setState(() {
_totalPosts = 0;
});
}
}
if (mounted) {
setState(() {
_isLoading = false;
_lastUpdated = formatter.format(now);
debugPrint('🕒 Last updated: $_lastUpdated');
debugPrint(
'📊 FINAL STATS: Users: $_totalUsers, Active: $_activeUsers, Guides: $_totalGuides, News: $_totalNews, Posts: $_totalPosts',
);
});
}
} catch (e) {
debugPrint('❌ Error loading dashboard stats: $e');
if (mounted) {
setState(() => _isLoading = false);
}
}
}
Future<void> _checkAdminAccess() async {
debugPrint('DEBUG: Mulai cek akses admin...');
if (mounted) {
setState(() => _isLoading = true);
}
try {
final isAdmin = await _authServices.isAdmin();
debugPrint('DEBUG: Hasil cek isAdmin: $isAdmin');
if (mounted) {
setState(() {
_isAdmin = isAdmin;
_isLoading = false;
});
}
// Only kick out if definitely not admin and we're sure about it
if (isAdmin == false) {
debugPrint('DEBUG: User bukan admin, akan pop dan tampilkan snackbar');
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
Navigator.of(context).pop();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text(
'Access denied. Admin privileges required.',
),
backgroundColor: Colors.red.shade400,
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
);
}
});
} else {
debugPrint('DEBUG: User adalah admin, melanjutkan...');
}
} catch (e) {
debugPrint('DEBUG: Error checking admin access: $e');
// Don't kick out on error, just show error message
if (mounted) {
setState(() => _isLoading = false);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error checking admin access: ${e.toString()}'),
backgroundColor: Colors.orange.shade400,
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
);
}
}
debugPrint('DEBUG: Selesai cek akses admin.');
}
Future<void> _refreshDashboardData() async {
if (mounted) {
debugPrint('🔄 Refreshing dashboard data...');
await _loadDashboardStats();
// Update active users count
try {
if (_presenceService != null) {
debugPrint('🔄 Fetching online users from presence service...');
final onlineUsers = await _presenceService!.getOnlineUsers();
if (mounted) {
setState(() {
_activeUsers = onlineUsers.length;
_realtimeActiveUsers = onlineUsers.length;
debugPrint('🔄 Updated active users count: $_activeUsers');
});
}
} else {
// Try to get the service if it was registered after initialization
try {
if (GetIt.instance.isRegistered<UserPresenceService>()) {
debugPrint('🔄 Getting UserPresenceService from GetIt...');
_presenceService = GetIt.instance<UserPresenceService>();
final onlineUsers = await _presenceService!.getOnlineUsers();
if (mounted) {
setState(() {
_activeUsers = onlineUsers.length;
_realtimeActiveUsers = onlineUsers.length;
debugPrint('🔄 Updated active users count: $_activeUsers');
});
}
} else {
debugPrint('⚠️ UserPresenceService not registered in GetIt');
}
} catch (e) {
debugPrint('❌ Error getting UserPresenceService: $e');
}
}
} catch (e) {
debugPrint('❌ Error getting online users: $e');
}
debugPrint(
'🔄 Refresh complete. Users: $_totalUsers, Active: $_activeUsers',
);
}
}
Widget _buildStatsGrid() {
return GridView.count(
crossAxisCount: 2,
childAspectRatio: 1.2,
padding: const EdgeInsets.all(4),
mainAxisSpacing: 12,
crossAxisSpacing: 12,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
children: [
_buildStatCard(
title: 'Total Pengguna',
value: '$_totalUsers',
subtitle: '+$_newUsersToday today',
icon: Icons.people,
color: chartBlue,
),
_buildActiveUsersCard(),
_buildStatCard(
title: 'News',
value: '$_totalNews',
subtitle: 'Saved articles',
icon: Icons.newspaper,
color: chartPurple,
),
_buildStatCard(
title: 'Panduan',
value: '$_totalGuides',
subtitle: 'Published guides',
icon: Icons.book,
color: chartOrange,
),
],
);
}
Widget _buildActiveUsersCard() {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.04),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: chartGreen.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(Icons.people, color: chartGreen, size: 16),
),
const SizedBox(height: 8),
Text(
'$_realtimeActiveUsers',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: textPrimary,
),
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 2),
Text(
'Pengguna Aktif',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: textSecondary,
),
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 2),
Text(
'Online saat ini',
style: TextStyle(
fontSize: 10,
color: chartGreen.withOpacity(0.8),
fontWeight: FontWeight.w500,
),
overflow: TextOverflow.ellipsis,
),
],
),
);
}
Widget _buildStatCard({
required String title,
required String value,
required String subtitle,
required IconData icon,
required Color color,
}) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.04),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(icon, color: color, size: 16),
),
const SizedBox(height: 8),
Text(
value,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: textPrimary,
),
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 2),
Text(
title,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: textSecondary,
),
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 2),
Text(
subtitle,
style: TextStyle(
fontSize: 10,
color: color.withOpacity(0.8),
fontWeight: FontWeight.w500,
),
overflow: TextOverflow.ellipsis,
),
],
),
);
}
@override
Widget build(BuildContext context) {
try {
debugPrint('DEBUG: build() AdminDashboard dipanggil');
// Define page content based on selected index
Widget pageContent;
switch (_currentIndex) {
case 0:
pageContent = _buildOverviewTab();
break;
case 1:
pageContent = const UserManagement();
break;
case 2:
pageContent = const GuideManagement();
break;
// case 3: // Removing Crops tab from admin
// pageContent = const CropManagement();
// break;
case 3: // Updated index after removing Crops
pageContent = const CommunityManagement();
break;
case 4: // Updated index after removing Crops
pageContent = const NewsManagement();
break;
default:
pageContent = _buildOverviewTab();
}
return Scaffold(
backgroundColor: const Color(0xFFF5F7FA),
appBar: AppBar(
title: const Text(
'Admin Dashboard',
style: TextStyle(fontWeight: FontWeight.w600),
),
centerTitle: true,
backgroundColor: AppColors.primary,
foregroundColor: Colors.white,
elevation: 2,
actions: [
IconButton(
icon: const Icon(Icons.exit_to_app),
onPressed: () {
Navigator.pop(context);
},
),
],
),
body:
_isLoading
? const Center(child: CircularProgressIndicator())
: !_isAdmin
? const Center(
child: Text('Access denied. Admin privileges required.'),
)
: pageContent,
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.white,
selectedItemColor: AppColors.primary,
unselectedItemColor: Colors.grey,
selectedFontSize: 12,
unselectedFontSize: 12,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.dashboard_outlined),
activeIcon: Icon(Icons.dashboard),
label: 'Overview',
),
BottomNavigationBarItem(
icon: Icon(Icons.people_outline),
activeIcon: Icon(Icons.people),
label: 'Users',
),
BottomNavigationBarItem(
icon: Icon(Icons.book_outlined),
activeIcon: Icon(Icons.book),
label: 'Guides',
),
// BottomNavigationBarItem(
// icon: Icon(Icons.agriculture_outlined),
// activeIcon: Icon(Icons.agriculture),
// label: 'Crops',
// ),
BottomNavigationBarItem(
icon: Icon(Icons.forum_outlined),
activeIcon: Icon(Icons.forum),
label: 'Community',
),
BottomNavigationBarItem(
icon: Icon(Icons.newspaper_outlined),
activeIcon: Icon(Icons.newspaper),
label: 'News',
),
],
),
);
} catch (e, stack) {
debugPrint('DEBUG: Exception di build AdminDashboard: $e\n$stack');
return Center(child: Text('Error: $e'));
}
}
Widget _buildOverviewTab() {
return RefreshIndicator(
onRefresh: _refreshDashboardData,
color: primaryGreen,
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header Section
_buildHeaderSection(),
const SizedBox(height: 20),
// Main Statistics Grid
_buildMainStatsGrid(),
const SizedBox(height: 24),
// Quick Actions Section
_buildQuickActionsSection(),
const SizedBox(height: 24),
],
),
),
);
}
Widget _buildHeaderSection() {
final now = DateTime.now();
final greeting =
now.hour < 12
? 'Good Morning'
: now.hour < 17
? 'Good Afternoon'
: 'Good Evening';
return Container(
margin: const EdgeInsets.only(bottom: 16),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
const Color(0xFF0F172A), // Slate-900
const Color(0xFF1E293B), // Slate-800
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: const Color(0xFF0F172A).withOpacity(0.2),
blurRadius: 12,
offset: const Offset(0, 4),
spreadRadius: -2,
),
],
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Top row with timestamp and refresh button
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [_buildTimeStamp(), _buildActionButton()],
),
const SizedBox(height: 16),
// System health card (wider)
_buildSystemMetricsWide(),
const SizedBox(height: 16),
// Greeting text
Text(
greeting,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0xFF10B981),
letterSpacing: 0.5,
),
),
const SizedBox(height: 4),
// Welcome text with glow
Container(
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: const Color(0xFF10B981).withOpacity(0.2),
blurRadius: 10,
spreadRadius: 1,
),
],
),
child: const Text(
'Welcome to TaniSMART',
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.w700,
color: Colors.white,
height: 1.1,
letterSpacing: -0.5,
),
),
),
const SizedBox(height: 16),
// Quick stats row
_buildQuickStats(),
],
),
),
);
}
Widget _buildTimeStamp() {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.08),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: Text(
'Last updated: $_lastUpdated',
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w500,
color: Colors.white.withOpacity(0.7),
letterSpacing: 0.2,
),
),
);
}
Widget _buildQuickStats() {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
_buildStatPill(
label: 'Users',
value: '$_totalUsers',
icon: Icons.people_alt_outlined,
color: chartBlue,
),
const SizedBox(width: 8),
_buildStatPill(
label: 'Online',
value: '$_realtimeActiveUsers',
icon: Icons.online_prediction_outlined,
color: chartGreen,
),
const SizedBox(width: 8),
_buildStatPill(
label: 'News',
value: '$_totalNews',
icon: Icons.newspaper_outlined,
color: chartPurple,
),
const SizedBox(width: 8),
_buildStatPill(
label: 'Guides',
value: '$_totalGuides',
icon: Icons.menu_book_outlined,
color: chartOrange,
),
],
),
);
}
Widget _buildStatPill({
required String label,
required String value,
required IconData icon,
required Color color,
}) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.08),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, size: 12, color: color),
const SizedBox(width: 6),
Text(
'$label: ',
style: TextStyle(
fontSize: 11,
color: Colors.white.withOpacity(0.7),
fontWeight: FontWeight.w400,
),
),
Text(
value,
style: const TextStyle(
fontSize: 11,
color: Colors.white,
fontWeight: FontWeight.w600,
),
),
],
),
);
}
Widget _buildSystemMetricsWide() {
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.white.withOpacity(0.1)),
),
child: Row(
children: [
Container(
width: 32,
height: 32,
decoration: BoxDecoration(
color:
_systemStatus
? const Color(0xFF10B981).withOpacity(0.2)
: const Color(0xFFEF4444).withOpacity(0.2),
shape: BoxShape.circle,
border: Border.all(
color:
_systemStatus
? const Color(0xFF10B981).withOpacity(0.4)
: const Color(0xFFEF4444).withOpacity(0.4),
width: 1,
),
),
child: Icon(
_systemStatus ? Icons.eco_rounded : Icons.warning_rounded,
color:
_systemStatus
? const Color(0xFF10B981)
: const Color(0xFFEF4444),
size: 16,
),
),
const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
_systemStatus ? '99.8%' : '87.3%',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
color: Colors.white,
letterSpacing: -0.5,
),
),
Text(
'System Health',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w500,
color: Colors.white.withOpacity(0.7),
letterSpacing: 0.3,
),
),
],
),
const Spacer(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color:
_systemStatus
? const Color(0xFF10B981).withOpacity(0.2)
: const Color(0xFFEF4444).withOpacity(0.2),
borderRadius: BorderRadius.circular(8),
),
child: Text(
_systemStatus ? 'Optimal' : 'Attention',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color:
_systemStatus
? const Color(0xFF10B981)
: const Color(0xFFEF4444),
letterSpacing: 0.5,
),
),
),
],
),
);
}
Widget _buildActionButton() {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
gradient: const LinearGradient(
colors: [Color(0xFF10B981), Color(0xFF059669)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
boxShadow: [
BoxShadow(
color: const Color(0xFF10B981).withOpacity(0.3),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: _refreshDashboardData,
borderRadius: BorderRadius.circular(12),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.refresh_rounded,
color: Colors.white,
size: 16,
),
const SizedBox(width: 3),
const Text(
'Refresh Data',
style: TextStyle(
fontSize: 8,
fontWeight: FontWeight.w600,
color: Colors.white,
letterSpacing: 0.3,
),
),
],
),
),
),
),
);
}
Widget _buildMainStatsGrid() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Platform Overview',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: textPrimary,
letterSpacing: 0.3,
),
),
TextButton.icon(
onPressed: _refreshDashboardData,
icon: Icon(
Icons.analytics_outlined,
size: 14,
color: accentBlue,
),
label: Text(
'View Analytics',
style: TextStyle(
fontSize: 12,
color: accentBlue,
fontWeight: FontWeight.w500,
),
),
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
),
),
],
),
),
_buildStatsGrid(),
],
);
}
Widget _buildQuickActionsSection() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Quick Actions',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: textPrimary,
letterSpacing: 0.3,
),
),
],
),
),
Container(
decoration: BoxDecoration(
color: cardWhite,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.04),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
border: Border.all(color: Colors.grey.shade100),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildQuickActionItem(
title: 'Manage Users',
subtitle: '$_totalUsers total users, $_activeUsers active',
icon: Icons.people_alt_rounded,
iconColor: chartBlue,
onTap: () => setState(() => _currentIndex = 1),
),
Divider(height: 1, color: dividerColor),
_buildQuickActionItem(
title: 'Manage Guides',
subtitle: '$_totalGuides farming guides',
icon: Icons.menu_book_rounded,
iconColor: chartOrange,
onTap: () => setState(() => _currentIndex = 2),
),
Divider(height: 1, color: dividerColor),
_buildQuickActionItem(
title: 'News Management',
subtitle: '$_totalNews saved news articles',
icon: Icons.newspaper_rounded,
iconColor: chartPurple,
onTap: () => setState(() => _currentIndex = 4),
),
Divider(height: 1, color: dividerColor),
_buildQuickActionItem(
title: 'Community Management',
subtitle: '$_totalPosts posts, +$_newPostsToday today',
icon: Icons.forum_rounded,
iconColor: chartGreen,
onTap: () => setState(() => _currentIndex = 3),
),
],
),
),
],
);
}
Widget _buildQuickActionItem({
required String title,
required String subtitle,
required IconData icon,
required Color iconColor,
required VoidCallback onTap,
}) {
return InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: iconColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(icon, size: 18, color: iconColor),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: textPrimary,
),
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 2),
Text(
subtitle,
style: TextStyle(fontSize: 12, color: textSecondary),
overflow: TextOverflow.ellipsis,
),
],
),
),
Icon(Icons.chevron_right, color: Colors.grey.shade400, size: 18),
],
),
),
);
}
Widget _buildRecentActivitiesSection() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Recent Activities',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: textPrimary,
letterSpacing: 0.3,
),
),
TextButton(
onPressed: _refreshDashboardData,
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Text(
'View All',
style: TextStyle(
fontSize: 13,
color: accentBlue,
fontWeight: FontWeight.w500,
),
),
),
],
),
),
_buildRecentActivitiesList(),
],
);
}
Widget _buildRecentActivitiesList() {
if (_recentActivities.isEmpty) {
return Container(
padding: const EdgeInsets.all(32),
decoration: BoxDecoration(
color: cardWhite,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.04),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
border: Border.all(color: Colors.grey.shade100),
),
child: Center(
child: Column(
children: [
Icon(
Icons.inbox_rounded,
size: 48,
color: textSecondary.withOpacity(0.7),
),
const SizedBox(height: 16),
Text(
'No recent activities',
style: TextStyle(
color: textSecondary,
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 8),
ElevatedButton(
onPressed: _refreshDashboardData,
style: ElevatedButton.styleFrom(
backgroundColor: primaryGreen,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: const Text('Refresh Data'),
),
],
),
),
);
}
return Container(
decoration: BoxDecoration(
color: cardWhite,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.04),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
border: Border.all(color: Colors.grey.shade100),
),
child: ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: _recentActivities.length,
separatorBuilder:
(context, index) => Divider(height: 1, color: dividerColor),
itemBuilder: (context, index) {
final activity = _recentActivities[index];
final username =
activity['profiles']?['username'] ?? 'Anonymous User';
final content = activity['content'] ?? '';
final createdAt =
activity['created_at'] != null
? DateFormat(
'MMM dd, HH:mm',
).format(DateTime.parse(activity['created_at']))
: '';
// Get avatar URL if available
final avatarUrl = activity['profiles']?['avatar_url'];
return ListTile(
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 12,
),
leading: Container(
width: 44,
height: 44,
decoration: BoxDecoration(
color: chartBlue.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
border: Border.all(color: Colors.grey.shade100),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child:
avatarUrl != null && avatarUrl.toString().isNotEmpty
? Image.network(
avatarUrl,
fit: BoxFit.cover,
errorBuilder:
(context, error, stackTrace) => Icon(
Icons.person_rounded,
color: chartBlue,
size: 22,
),
)
: Icon(
Icons.person_rounded,
color: chartBlue,
size: 22,
),
),
),
title: Row(
children: [
Expanded(
child: Text(
username,
style: TextStyle(
fontWeight: FontWeight.w600,
color: textPrimary,
fontSize: 15,
),
),
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 2,
),
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(12),
),
child: Text(
createdAt,
style: TextStyle(
fontSize: 11,
color: textSecondary,
fontWeight: FontWeight.w500,
),
),
),
],
),
subtitle: Padding(
padding: const EdgeInsets.only(top: 6),
child: Text(
content.length > 100
? '${content.substring(0, 100)}...'
: content,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: textSecondary, fontSize: 13),
),
),
);
},
),
);
}
}