chitfund/luckychit/lib/interfaces/manager/manager_dashboard.dart

737 lines
22 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 '../../core/design_system/app_components/state_view.dart';
import '../../core/design_system/app_text.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 'import_existing_group_dialog.dart';
import 'members_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) {
final scheme = Theme.of(context).colorScheme;
// 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(
appBar: AppBar(
title: Text('Dashboard', style: AppText.title(context)),
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(context) : null,
body: LayoutBuilder(
builder: (layoutContext, constraints) {
if (constraints.maxWidth < 800) {
return _buildMobileLayout(layoutContext);
}
return _buildDesktopLayout(layoutContext);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Get.to(() => const TestAnimatedDraw());
},
backgroundColor: scheme.tertiary,
foregroundColor: scheme.onTertiary,
child: const Icon(Icons.casino),
tooltip: 'Test Animated Draw',
),
);
}
Widget _buildMobileLayout(BuildContext context) {
return Obx(() {
final service = ChitGroupService.to;
return StateView(
isLoading: service.isLoading.value,
isEmpty: service.chitGroups.isEmpty,
emptyType: EmptyStateType.noGroups,
emptyTitle: 'No chit groups yet',
emptyMessage: 'Create your first group to get started.',
onRetry: () => service.loadManagerChitGroups(),
loading: const SkeletonDashboard(),
child: RefreshIndicator(
onRefresh: () async {
await service.loadManagerChitGroups();
},
child: SingleChildScrollView(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildWelcomeSection(context),
SizedBox(height: 24.h),
_buildRecentActivitiesSection(),
SizedBox(height: 24.h),
_buildQuickActionsSection(),
],
),
),
),
);
});
}
Widget _buildDesktopLayout(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
return Row(
children: [
Container(
width: 250.w,
decoration: BoxDecoration(
color: scheme.surfaceContainerHighest,
border: Border(
right: BorderSide(color: scheme.outlineVariant, width: 1),
),
),
child: _buildSidebar(context),
),
Expanded(
child: SingleChildScrollView(
padding: EdgeInsets.all(20.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildWelcomeSection(context),
SizedBox(height: 24.h),
_buildDesktopActivitiesAndActions(),
],
),
),
),
],
);
}
Widget _buildDrawer(BuildContext context) {
return Drawer(
child: _buildSidebar(context),
);
}
Widget _buildSidebar(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
return Column(
children: [
Container(
padding: EdgeInsets.all(20.w),
child: Column(
children: [
CircleAvatar(
radius: 32.r,
backgroundColor: scheme.primary,
child: Icon(
Icons.person,
size: 32.w,
color: scheme.onPrimary,
),
),
SizedBox(height: 12.h),
Obx(() => Text(
AuthService.to.currentUser.value?.fullName ?? 'Manager',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: scheme.onSurface,
),
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 2,
)),
SizedBox(height: 4.h),
Text(
'Chit Fund Manager',
style: TextStyle(
fontSize: 14.sp,
color: scheme.onSurfaceVariant,
),
),
],
),
),
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(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Welcome back!',
style: AppText.headline(context),
),
SizedBox(height: 8.h),
Text(
'Here\'s what\'s happening with your chit funds today.',
style: AppText.bodyMuted(context),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
);
}
Widget _buildMobileStatsGrid() {
// Stats cards removed as per user request
return SizedBox.shrink();
}
Widget _buildDesktopStatsGrid() {
// Stats cards removed as per user request
return SizedBox.shrink();
}
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: 'Import Existing Chitfund',
subtitle: 'Add a group that already started',
icon: Icons.upload,
color: Colors.blue.shade600,
onTap: () => _showImportGroupDialog(Get.context!),
),
QuickActionCard(
title: 'View All Chitfunds',
subtitle: 'Manage your existing groups',
icon: Icons.list,
color: Colors.teal.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 Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'My Chitfunds',
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.w600,
),
),
TextButton.icon(
onPressed: _navigateToGroups,
icon: Icon(Icons.arrow_forward, size: 18.w),
label: Text('View All', style: TextStyle(fontSize: 14.sp)),
),
],
),
SizedBox(height: 12.h),
Obx(() {
final groups = ChitGroupService.to.chitGroups.take(3).toList();
if (groups.isEmpty) {
return Card(
child: Padding(
padding: EdgeInsets.all(32.w),
child: Center(
child: Text(
'No chit funds yet',
style: TextStyle(
fontSize: 14.sp,
color: Colors.grey.shade600,
),
),
),
),
);
}
return Column(
children: groups.map((group) => Padding(
padding: EdgeInsets.only(bottom: 12.h),
child: _buildGroupQuickCard(group),
)).toList(),
);
}),
],
);
}
Widget _buildGroupQuickCard(dynamic group) {
final status = group.status ?? 'forming';
final statusColor = _getStatusColor(status);
return InteractiveCard(
onTap: () => Get.to(() => const ChitGroupsPage()),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
group.name ?? 'Unnamed',
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.w700,
color: Colors.grey.shade800,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
decoration: BoxDecoration(
color: statusColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(16.r),
border: Border.all(color: statusColor, width: 1.5),
),
child: Text(
_getStatusText(status),
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w600,
color: statusColor,
),
),
),
],
),
SizedBox(height: 12.h),
Row(
children: [
_buildQuickInfo(
Icons.people,
'${group.currentMemberCount}/${group.maxMembers}',
Colors.blue.shade600,
),
SizedBox(width: 16.w),
_buildQuickInfo(
Icons.currency_rupee,
'${_formatAmount(group.monthlyInstallment)}',
Colors.green.shade600,
),
SizedBox(width: 16.w),
_buildQuickInfo(
Icons.calendar_month,
'${group.durationMonths}m',
Colors.orange.shade600,
),
],
),
// Quick action buttons
if (status.toLowerCase() == 'active') ...[
SizedBox(height: 16.h),
Row(
children: [
Expanded(
child: ElevatedButton.icon(
onPressed: () {
// Record payment - simplified flow
Get.to(() => const ChitGroupsPage());
},
icon: Icon(Icons.payment, size: 18.w),
label: Text('Record', style: TextStyle(fontSize: 13.sp)),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green.shade600,
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 10.h),
),
),
),
SizedBox(width: 8.w),
Expanded(
child: ElevatedButton.icon(
onPressed: () {
// Conduct draw
Get.to(() => const ChitGroupsPage());
},
icon: Icon(Icons.casino, size: 18.w),
label: Text('Draw', style: TextStyle(fontSize: 13.sp)),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.purple.shade600,
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 10.h),
),
),
),
SizedBox(width: 8.w),
Expanded(
child: OutlinedButton(
onPressed: () {
Get.to(() => const ChitGroupsPage());
},
style: OutlinedButton.styleFrom(
padding: EdgeInsets.symmetric(vertical: 10.h),
side: BorderSide(color: Colors.grey.shade400),
),
child: Text('View', style: TextStyle(fontSize: 13.sp)),
),
),
],
),
] else ...[
SizedBox(height: 16.h),
SizedBox(
width: double.infinity,
child: OutlinedButton.icon(
onPressed: () {
Get.to(() => const ChitGroupsPage());
},
icon: Icon(Icons.arrow_forward, size: 18.w),
label: Text('Manage Group', style: TextStyle(fontSize: 14.sp)),
style: OutlinedButton.styleFrom(
padding: EdgeInsets.symmetric(vertical: 12.h),
),
),
),
],
],
),
);
}
Widget _buildQuickInfo(IconData icon, String text, Color color) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, size: 16.w, color: color),
SizedBox(width: 4.w),
Text(
text,
style: TextStyle(
fontSize: 13.sp,
fontWeight: FontWeight.w600,
color: Colors.grey.shade700,
),
),
],
);
}
Color _getStatusColor(String status) {
switch (status.toLowerCase()) {
case 'active':
return Colors.green.shade600;
case 'forming':
return Colors.orange.shade600;
case 'completed':
return Colors.blue.shade600;
default:
return Colors.grey.shade600;
}
}
String _getStatusText(String status) {
switch (status.toLowerCase()) {
case 'active':
return 'Active';
case 'forming':
return 'Forming';
case 'completed':
return 'Completed';
default:
return status;
}
}
String _formatAmount(dynamic amount) {
if (amount == null) return '0';
final value = double.tryParse(amount.toString()) ?? 0.0;
if (value >= 100000) {
return '${(value / 100000).toStringAsFixed(1)}L';
} else if (value >= 1000) {
return '${(value / 1000).toStringAsFixed(0)}K';
}
return value.toStringAsFixed(0);
}
Widget _buildOldRecentActivitiesSection() {
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 Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// My Chitfunds (full width on desktop)
_buildRecentActivitiesSection(),
SizedBox(height: 24.h),
// Quick Actions (below chitfunds)
_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 _showImportGroupDialog(BuildContext context) {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => const ImportExistingGroupDialog(),
).then((result) {
if (result == true) {
// Reload groups after successful import
ChitGroupService.to.loadManagerChitGroups();
SnackbarUtil.showSuccess(
'Group imported! Now add members and backfill past data.',
title: 'Success',
);
}
});
}
void _navigateToGroups() {
Get.to(() => const ChitGroupsPage());
}
void _navigateToMembers() {
Get.to(() => const MembersPage());
}
void _navigateToPayments() {
SnackbarUtil.showInfo(
'Payments page will be implemented next',
title: 'Coming Soon',
);
}
}