601 lines
18 KiB
Dart
601 lines
18 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
import '../../core/services/auth_service.dart';
|
|
import '../../core/services/chit_group_service.dart';
|
|
import '../../core/services/notification_service.dart';
|
|
import '../../core/utils/snackbar_util.dart';
|
|
import '../../shared/widgets/skeleton_loader.dart';
|
|
import '../../shared/widgets/empty_state_widget.dart';
|
|
import '../../shared/widgets/interactive_card.dart';
|
|
import '../../shared/widgets/notification_badge.dart';
|
|
import '../../features/settings/settings_page.dart';
|
|
import '../../features/notifications/notification_center_page.dart';
|
|
import 'chit_groups_page.dart';
|
|
import 'create_group_page.dart';
|
|
import '../../test_animated_draw.dart';
|
|
import '../../features/recordings/recordings_page.dart';
|
|
|
|
class ManagerDashboard extends StatelessWidget {
|
|
const ManagerDashboard({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
// Initialize services
|
|
Get.put(ChitGroupService());
|
|
if (!Get.isRegistered<NotificationService>()) {
|
|
Get.put(NotificationService());
|
|
}
|
|
|
|
// Load data on first build
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
ChitGroupService.to.loadManagerChitGroups();
|
|
NotificationService.to.loadUnreadCount();
|
|
});
|
|
|
|
return Scaffold(
|
|
backgroundColor: Colors.grey.shade50,
|
|
appBar: AppBar(
|
|
title: Text(
|
|
'Manager Dashboard',
|
|
style: TextStyle(
|
|
fontSize: 18.sp,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
backgroundColor: Colors.green.shade600,
|
|
foregroundColor: Colors.white,
|
|
elevation: 2,
|
|
actions: [
|
|
// Notifications with badge
|
|
Obx(() {
|
|
final unreadCount = NotificationService.to.unreadCount.value;
|
|
return IconButton(
|
|
icon: NotificationBadge(
|
|
count: unreadCount,
|
|
child: Icon(Icons.notifications_rounded, size: 24.w),
|
|
),
|
|
onPressed: () => Get.to(() => const NotificationCenterPage()),
|
|
tooltip: 'Notifications',
|
|
);
|
|
}),
|
|
IconButton(
|
|
icon: Icon(Icons.videocam, size: 24.w),
|
|
onPressed: () => Get.to(() => const RecordingsPage()),
|
|
tooltip: 'View Draw Recordings',
|
|
),
|
|
IconButton(
|
|
icon: Icon(Icons.logout, size: 24.w),
|
|
onPressed: () => _showLogoutDialog(context),
|
|
),
|
|
],
|
|
),
|
|
drawer: MediaQuery.of(context).size.width < 800 ? _buildDrawer() : null,
|
|
body: LayoutBuilder(
|
|
builder: (context, constraints) {
|
|
if (constraints.maxWidth < 800) {
|
|
// Mobile Layout
|
|
return _buildMobileLayout();
|
|
} else {
|
|
// Desktop Layout
|
|
return _buildDesktopLayout();
|
|
}
|
|
},
|
|
),
|
|
floatingActionButton: FloatingActionButton(
|
|
onPressed: () {
|
|
Get.to(() => const TestAnimatedDraw());
|
|
},
|
|
backgroundColor: Colors.purple.shade600,
|
|
foregroundColor: Colors.white,
|
|
child: const Icon(Icons.casino),
|
|
tooltip: 'Test Animated Draw',
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildMobileLayout() {
|
|
return Obx(() {
|
|
final service = ChitGroupService.to;
|
|
|
|
// Show skeleton loader while loading
|
|
if (service.isLoading.value) {
|
|
return const SkeletonDashboard();
|
|
}
|
|
|
|
// Show empty state if no groups
|
|
if (service.chitGroups.isEmpty) {
|
|
return EmptyStateWidget(
|
|
type: EmptyStateType.noGroups,
|
|
actionLabel: 'Create Your First Group',
|
|
onActionPressed: () => _showCreateGroupDialog(Get.context!),
|
|
);
|
|
}
|
|
|
|
// Show dashboard content
|
|
return RefreshIndicator(
|
|
onRefresh: () async {
|
|
await service.loadManagerChitGroups();
|
|
},
|
|
child: SingleChildScrollView(
|
|
padding: EdgeInsets.all(16.w),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Welcome Section
|
|
_buildWelcomeSection(),
|
|
SizedBox(height: 24.h),
|
|
|
|
// Statistics Cards - Mobile Grid
|
|
_buildMobileStatsGrid(),
|
|
SizedBox(height: 24.h),
|
|
|
|
// Quick Actions
|
|
_buildQuickActionsSection(),
|
|
SizedBox(height: 24.h),
|
|
|
|
// Recent Activities
|
|
_buildRecentActivitiesSection(),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
});
|
|
}
|
|
|
|
Widget _buildDesktopLayout() {
|
|
return Row(
|
|
children: [
|
|
// Sidebar - Fixed width that scales better
|
|
Container(
|
|
width: 250.w,
|
|
decoration: BoxDecoration(
|
|
color: Colors.grey.shade100,
|
|
border: Border(
|
|
right: BorderSide(color: Colors.grey.shade300, width: 1),
|
|
),
|
|
),
|
|
child: _buildSidebar(),
|
|
),
|
|
|
|
// Main Content
|
|
Expanded(
|
|
child: SingleChildScrollView(
|
|
padding: EdgeInsets.all(20.w),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_buildWelcomeSection(),
|
|
SizedBox(height: 24.h),
|
|
_buildDesktopStatsGrid(),
|
|
SizedBox(height: 24.h),
|
|
_buildDesktopActivitiesAndActions(),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildDrawer() {
|
|
return Drawer(
|
|
child: _buildSidebar(),
|
|
);
|
|
}
|
|
|
|
Widget _buildSidebar() {
|
|
return Column(
|
|
children: [
|
|
// User Info
|
|
Container(
|
|
padding: EdgeInsets.all(20.w),
|
|
child: Column(
|
|
children: [
|
|
CircleAvatar(
|
|
radius: 32.r,
|
|
backgroundColor: Colors.green.shade600,
|
|
child: Icon(
|
|
Icons.person,
|
|
size: 32.w,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
SizedBox(height: 12.h),
|
|
Obx(() => Text(
|
|
AuthService.to.currentUser.value?.fullName ?? 'Manager',
|
|
style: TextStyle(
|
|
fontSize: 16.sp,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
textAlign: TextAlign.center,
|
|
overflow: TextOverflow.ellipsis,
|
|
maxLines: 2,
|
|
)),
|
|
SizedBox(height: 4.h),
|
|
Text(
|
|
'Chit Fund Manager',
|
|
style: TextStyle(
|
|
fontSize: 14.sp,
|
|
color: Colors.grey.shade600,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Divider(height: 1.h),
|
|
|
|
// Navigation Menu
|
|
Expanded(
|
|
child: ListView(
|
|
padding: EdgeInsets.zero,
|
|
children: [
|
|
_buildMenuItem(
|
|
icon: Icons.dashboard,
|
|
title: 'Dashboard',
|
|
isSelected: true,
|
|
onTap: () {},
|
|
),
|
|
_buildMenuItem(
|
|
icon: Icons.group,
|
|
title: 'My Chitfunds',
|
|
onTap: () => Get.to(() => const ChitGroupsPage()),
|
|
),
|
|
_buildMenuItem(
|
|
icon: Icons.people,
|
|
title: 'Members',
|
|
onTap: () {},
|
|
),
|
|
_buildMenuItem(
|
|
icon: Icons.payment,
|
|
title: 'Payments',
|
|
onTap: () {},
|
|
),
|
|
_buildMenuItem(
|
|
icon: Icons.casino,
|
|
title: 'Lottery Draws',
|
|
onTap: () {},
|
|
),
|
|
_buildMenuItem(
|
|
icon: Icons.analytics,
|
|
title: 'Reports',
|
|
onTap: () {},
|
|
),
|
|
_buildMenuItem(
|
|
icon: Icons.settings,
|
|
title: 'Settings',
|
|
onTap: () => Get.to(() => const SettingsPage()),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildMenuItem({
|
|
required IconData icon,
|
|
required String title,
|
|
bool isSelected = false,
|
|
required VoidCallback onTap,
|
|
}) {
|
|
return ListTile(
|
|
leading: SizedBox(
|
|
width: 24.w,
|
|
child: Icon(
|
|
icon,
|
|
color: isSelected ? Colors.green.shade600 : Colors.grey.shade600,
|
|
),
|
|
),
|
|
title: Text(
|
|
title,
|
|
style: TextStyle(
|
|
color: isSelected ? Colors.green.shade600 : Colors.grey.shade800,
|
|
fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
|
|
fontSize: 14.sp,
|
|
),
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
selected: isSelected,
|
|
selectedTileColor: Colors.green.shade50,
|
|
onTap: onTap,
|
|
);
|
|
}
|
|
|
|
Widget _buildWelcomeSection() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'Welcome back!',
|
|
style: TextStyle(
|
|
fontSize: 20.sp,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.grey.shade800,
|
|
),
|
|
),
|
|
SizedBox(height: 8.h),
|
|
Text(
|
|
'Here\'s what\'s happening with your chit funds today.',
|
|
style: TextStyle(
|
|
fontSize: 14.sp,
|
|
color: Colors.grey.shade600,
|
|
),
|
|
maxLines: 2,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildMobileStatsGrid() {
|
|
return Obx(() {
|
|
final groups = ChitGroupService.to.chitGroups;
|
|
final activeGroups = groups.where((g) => g.isActive).length;
|
|
final formingGroups = groups.where((g) => g.isForming).length;
|
|
final totalMembers = groups.fold<int>(0, (sum, group) => sum + (group.members?.length ?? 0));
|
|
|
|
return Column(
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Expanded(child: InteractiveStatsCard(
|
|
title: 'Active Chitfunds',
|
|
value: activeGroups.toString(),
|
|
icon: Icons.group,
|
|
color: Colors.blue.shade600,
|
|
onTap: () => _navigateToGroups(),
|
|
)),
|
|
SizedBox(width: 12.w),
|
|
Expanded(child: InteractiveStatsCard(
|
|
title: 'Forming Chitfunds',
|
|
value: formingGroups.toString(),
|
|
icon: Icons.group_add,
|
|
color: Colors.orange.shade600,
|
|
onTap: () => _navigateToGroups(),
|
|
)),
|
|
],
|
|
),
|
|
SizedBox(height: 12.h),
|
|
Row(
|
|
children: [
|
|
Expanded(child: InteractiveStatsCard(
|
|
title: 'Total Members',
|
|
value: totalMembers.toString(),
|
|
icon: Icons.people,
|
|
color: Colors.green.shade600,
|
|
onTap: () => _navigateToMembers(),
|
|
)),
|
|
SizedBox(width: 12.w),
|
|
Expanded(child: InteractiveStatsCard(
|
|
title: 'Total Chitfunds',
|
|
value: groups.length.toString(),
|
|
icon: Icons.dashboard,
|
|
color: Colors.purple.shade600,
|
|
onTap: () => _navigateToGroups(),
|
|
)),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
});
|
|
}
|
|
|
|
Widget _buildDesktopStatsGrid() {
|
|
return Obx(() {
|
|
final groups = ChitGroupService.to.chitGroups;
|
|
final activeGroups = groups.where((g) => g.isActive).length;
|
|
final formingGroups = groups.where((g) => g.isForming).length;
|
|
final totalMembers = groups.fold<int>(0, (sum, group) => sum + (group.members?.length ?? 0));
|
|
|
|
return Row(
|
|
children: [
|
|
Expanded(child: InteractiveStatsCard(
|
|
title: 'Active Groups',
|
|
value: activeGroups.toString(),
|
|
icon: Icons.group,
|
|
color: Colors.blue.shade600,
|
|
onTap: () => _navigateToGroups(),
|
|
)),
|
|
SizedBox(width: 16.w),
|
|
Expanded(child: InteractiveStatsCard(
|
|
title: 'Forming Groups',
|
|
value: formingGroups.toString(),
|
|
icon: Icons.group_add,
|
|
color: Colors.orange.shade600,
|
|
onTap: () => _navigateToGroups(),
|
|
)),
|
|
SizedBox(width: 16.w),
|
|
Expanded(child: InteractiveStatsCard(
|
|
title: 'Total Members',
|
|
value: totalMembers.toString(),
|
|
icon: Icons.people,
|
|
color: Colors.green.shade600,
|
|
onTap: () => _navigateToMembers(),
|
|
)),
|
|
SizedBox(width: 16.w),
|
|
Expanded(child: InteractiveStatsCard(
|
|
title: 'Total Groups',
|
|
value: groups.length.toString(),
|
|
icon: Icons.dashboard,
|
|
color: Colors.purple.shade600,
|
|
onTap: () => _navigateToGroups(),
|
|
)),
|
|
],
|
|
);
|
|
});
|
|
}
|
|
|
|
Widget _buildQuickActionsSection() {
|
|
return Card(
|
|
child: Padding(
|
|
padding: EdgeInsets.all(20.w),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'Quick Actions',
|
|
style: TextStyle(
|
|
fontSize: 18.sp,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
SizedBox(height: 16.h),
|
|
Column(
|
|
children: [
|
|
QuickActionCard(
|
|
title: 'Create New Chitfund',
|
|
subtitle: 'Start a new chit fund group',
|
|
icon: Icons.group_add,
|
|
color: Colors.green.shade600,
|
|
onTap: () => _showCreateGroupDialog(Get.context!),
|
|
),
|
|
QuickActionCard(
|
|
title: 'View All Chitfunds',
|
|
subtitle: 'Manage your existing groups',
|
|
icon: Icons.list,
|
|
color: Colors.blue.shade600,
|
|
onTap: () => _navigateToGroups(),
|
|
),
|
|
QuickActionCard(
|
|
title: 'Manage Members',
|
|
subtitle: 'Add or remove members',
|
|
icon: Icons.people,
|
|
color: Colors.orange.shade600,
|
|
onTap: () => _navigateToMembers(),
|
|
),
|
|
QuickActionCard(
|
|
title: 'Payment Records',
|
|
subtitle: 'Track all transactions',
|
|
icon: Icons.payment,
|
|
color: Colors.purple.shade600,
|
|
onTap: () => _navigateToPayments(),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildRecentActivitiesSection() {
|
|
return Card(
|
|
child: Padding(
|
|
padding: EdgeInsets.all(20.w),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'Recent Activities',
|
|
style: TextStyle(
|
|
fontSize: 18.sp,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
SizedBox(height: 12.h),
|
|
ActivityCard(
|
|
title: 'New Member Joined',
|
|
description: 'A new member joined Group A',
|
|
time: '2h ago',
|
|
icon: Icons.person_add,
|
|
color: Colors.green.shade600,
|
|
),
|
|
ActivityCard(
|
|
title: 'Payment Received',
|
|
description: 'Payment received from John Doe',
|
|
time: '4h ago',
|
|
icon: Icons.payment,
|
|
color: Colors.blue.shade600,
|
|
),
|
|
ActivityCard(
|
|
title: 'Lottery Draw Completed',
|
|
description: 'Draw completed for Group B',
|
|
time: '1d ago',
|
|
icon: Icons.casino,
|
|
color: Colors.orange.shade600,
|
|
),
|
|
ActivityCard(
|
|
title: 'Group Created',
|
|
description: 'New group "Group C" created',
|
|
time: '2d ago',
|
|
icon: Icons.group_add,
|
|
color: Colors.purple.shade600,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildDesktopActivitiesAndActions() {
|
|
return Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Recent Activities
|
|
Expanded(
|
|
flex: 2,
|
|
child: _buildRecentActivitiesSection(),
|
|
),
|
|
SizedBox(width: 16.w),
|
|
|
|
// Quick Actions
|
|
Expanded(
|
|
flex: 1,
|
|
child: _buildQuickActionsSection(),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
|
|
void _showLogoutDialog(BuildContext context) {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: const Text('Logout'),
|
|
content: const Text('Are you sure you want to logout?'),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
child: const Text('Cancel'),
|
|
),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
Navigator.of(context).pop();
|
|
AuthService.to.logout();
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Colors.red,
|
|
foregroundColor: Colors.white,
|
|
),
|
|
child: const Text('Logout'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
void _showCreateGroupDialog(BuildContext context) {
|
|
Get.to(() => const CreateGroupPage());
|
|
}
|
|
|
|
void _navigateToGroups() {
|
|
Get.to(() => const ChitGroupsPage());
|
|
}
|
|
|
|
void _navigateToMembers() {
|
|
SnackbarUtil.showInfo(
|
|
'Members page will be implemented next',
|
|
title: 'Coming Soon',
|
|
);
|
|
}
|
|
|
|
void _navigateToPayments() {
|
|
SnackbarUtil.showInfo(
|
|
'Payments page will be implemented next',
|
|
title: 'Coming Soon',
|
|
);
|
|
}
|
|
}
|