import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import '../../core/services/chit_group_service.dart'; import '../../core/services/payment_service.dart'; import '../../core/services/api_service.dart'; import '../../core/models/chit_group.dart'; import '../../core/models/group_member.dart'; import '../../core/models/payment.dart'; import '../../core/models/monthly_draw.dart'; import '../../core/models/financial_table_entry.dart'; import '../../shared/widgets/financial_table.dart'; import 'member_selection_dialog.dart'; import 'add_user_dialog.dart'; import 'combined_draw_dialog.dart'; import 'add_past_draw_dialog.dart'; import 'add_past_payments_dialog.dart'; import 'record_payment_dialog.dart'; import 'payment_history_page.dart'; import 'edit_draw_dialog.dart'; import 'edit_member_dialog.dart'; import 'edit_group_dialog.dart'; import '../../features/chitfund_schedule/chitfund_schedule_page.dart'; import '../../features/monthly_payments/monthly_payment_status_page.dart'; class GroupDetailsPage extends StatefulWidget { final ChitGroup group; const GroupDetailsPage({ super.key, required this.group, }); @override State createState() => _GroupDetailsPageState(); } class _GroupDetailsPageState extends State with SingleTickerProviderStateMixin { late TabController _tabController; final ChitGroupService _service = ChitGroupService.to; late final PaymentService _paymentService; @override void initState() { super.initState(); _tabController = TabController(length: 5, vsync: this); // Get payment service (already initialized in app.dart) _paymentService = Get.find(); // Load group details, members, payments, draws, and financial data WidgetsBinding.instance.addPostFrameCallback((_) { _service.loadChitGroupDetails(widget.group.id); _service.loadGroupMembers(widget.group.id); _service.loadGroupStats(widget.group.id); _service.loadGroupMonthlyDraws(widget.group.id); _service.loadGroupFinancialData(widget.group.id); // Load payment data with delay to avoid setState during build Future.delayed(const Duration(milliseconds: 300), () { if (mounted) { _paymentService.loadGroupPayments(widget.group.id); _paymentService.loadPendingPayments(widget.group.id); _paymentService.loadPaymentSummary(widget.group.id); } }); }); } @override void dispose() { _tabController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.grey.shade50, appBar: AppBar( leading: IconButton( icon: Icon(Icons.arrow_back, color: Colors.grey.shade800, size: 24.w), onPressed: () => Navigator.pop(context), tooltip: 'Back', ), title: Text( widget.group.name, style: TextStyle( fontSize: 20.sp, fontWeight: FontWeight.w600, color: Colors.grey.shade900, ), overflow: TextOverflow.ellipsis, ), backgroundColor: Colors.white, iconTheme: IconThemeData(color: Colors.grey.shade800), elevation: 0, actions: [ // Actions menu - always visible PopupMenuButton( icon: Icon(Icons.more_vert, size: 24.w, color: Colors.grey.shade700), tooltip: 'More Options', onSelected: (value) { if (value == 'edit_group') { _editGroup(); } else if (value == 'delete_group') { _confirmDeleteGroup(); } else if (value == 'select') { _showMemberSelectionDialog(context); } else if (value == 'add_user') { _showAddUserDialog(context); } else if (value == 'add_past_draw') { _showAddPastDrawDialog(context); } else if (value == 'add_past_payments') { _showAddPastPaymentsDialog(context); } }, itemBuilder: (context) { final currentMemberCount = widget.group.currentMemberCount; final maxMembers = widget.group.maxMembers; final canAddMembers = currentMemberCount < maxMembers; return [ // Edit/Delete group options (only for forming groups) if (widget.group.status == 'forming') ...[ PopupMenuItem( value: 'edit_group', child: Row( children: [ Icon(Icons.edit, color: Colors.green.shade600), SizedBox(width: 12.w), const Text('Edit Group Details'), ], ), ), PopupMenuItem( value: 'delete_group', child: Row( children: [ Icon(Icons.delete, color: Colors.red.shade600), SizedBox(width: 12.w), const Text('Delete Group'), ], ), ), const PopupMenuDivider(), ], // Add members options (if not full) if (canAddMembers) ...[ PopupMenuItem( value: 'select', child: Row( children: [ Icon(Icons.people_alt, color: Colors.blue.shade600), SizedBox(width: 12.w), const Text('Select Members'), ], ), ), PopupMenuItem( value: 'add_user', child: Row( children: [ Icon(Icons.person_add, color: Colors.green.shade600), SizedBox(width: 12.w), const Text('Add New User'), ], ), ), const PopupMenuDivider(), ], // Backfill options (always available) PopupMenuItem( value: 'add_past_draw', child: Row( children: [ Icon(Icons.history, color: Colors.blue.shade600), SizedBox(width: 12.w), const Text('Add Past Draw Result'), ], ), ), PopupMenuItem( value: 'add_past_payments', child: Row( children: [ Icon(Icons.payment_outlined, color: Colors.green.shade600), SizedBox(width: 12.w), const Text('Add Past Payments'), ], ), ), ]; }, ), ], bottom: PreferredSize( preferredSize: Size.fromHeight(64.h), child: Container( height: 64.h, decoration: BoxDecoration( color: Colors.white, border: Border(bottom: BorderSide(color: Colors.grey.shade200, width: 1)), ), child: TabBar( controller: _tabController, indicatorColor: Colors.blue.shade600, labelColor: Colors.blue.shade600, unselectedLabelColor: Colors.grey.shade700, labelStyle: TextStyle(fontSize: 10.sp, fontWeight: FontWeight.w600), unselectedLabelStyle: TextStyle(fontSize: 10.sp, fontWeight: FontWeight.w500), isScrollable: false, tabAlignment: TabAlignment.fill, tabs: const [ Tab(icon: Icon(Icons.dashboard, size: 20), text: 'Overview'), Tab(icon: Icon(Icons.people, size: 20), text: 'Members'), Tab(icon: Icon(Icons.payment, size: 20), text: 'Pay'), Tab(icon: Icon(Icons.casino, size: 20), text: 'Draws'), Tab(icon: Icon(Icons.table_chart, size: 20), text: 'Finance'), ], ), ), ), ), body: TabBarView( controller: _tabController, children: [ _buildOverviewTab(), _buildMembersTab(), _buildPaymentsTab(), _buildDrawsTab(), _buildFinancialTab(), ], ), ); } Widget _buildOverviewTab() { return Obx(() { final group = _service.selectedGroup.value ?? widget.group; final stats = _service.groupStats.value; return SingleChildScrollView( padding: EdgeInsets.all(16.w), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Welcome Section Row( children: [ CircleAvatar( radius: 24.r, backgroundColor: Colors.blue.shade100, child: Icon(Icons.group, size: 24.w, color: Colors.blue.shade600), ), SizedBox(width: 12.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Hello ${group.name}', style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.w600, color: Colors.black87, ), ), Text( 'Welcome Back!', style: TextStyle( fontSize: 14.sp, color: Colors.grey.shade600, ), ), ], ), ), Icon(Icons.notifications_outlined, size: 24.w, color: Colors.grey.shade600), ], ), SizedBox(height: 24.h), // Group Balance Card Container( width: double.infinity, padding: EdgeInsets.all(12.w), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Colors.blue.shade600, Colors.purple.shade600], ), borderRadius: BorderRadius.circular(12.r), boxShadow: [ BoxShadow( color: Colors.blue.shade200.withOpacity(0.3), blurRadius: 8, offset: const Offset(0, 3), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Chitfund Balance', style: TextStyle( fontSize: 14.sp, color: Colors.white.withOpacity(0.9), fontWeight: FontWeight.w500, ), ), Row( children: [ Icon(Icons.visibility_off, size: 16.w, color: Colors.white.withOpacity(0.8)), SizedBox(width: 4.w), Icon(Icons.qr_code, size: 16.w, color: Colors.white.withOpacity(0.8)), ], ), ], ), SizedBox(height: 8.h), Text( _formatIndianCurrency(group.totalValue), style: TextStyle( fontSize: 24.sp, fontWeight: FontWeight.bold, color: Colors.white, ), ), SizedBox(height: 6.h), Row( children: [ _buildStatusChip(group.status), SizedBox(width: 6.w), Text( '${group.maxMembers} members', style: TextStyle( fontSize: 12.sp, color: Colors.white.withOpacity(0.8), ), ), ], ), ], ), ), SizedBox(height: 24.h), // Current Month & Draw Information _buildCurrentMonthSection(group), SizedBox(height: 24.h), // Quick Actions Text( 'Quick Actions', style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.w600, color: Colors.black87, ), ), SizedBox(height: 16.h), // First row - 3 buttons Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _buildQuickActionButton('Members', Icons.people, Colors.green), _buildQuickActionButton('Payments', Icons.payment, Colors.orange), _buildQuickActionButton('Draws', Icons.casino, Colors.purple), ], ), SizedBox(height: 12.h), // Second row - 3 buttons Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _buildQuickActionButton('Schedule', Icons.calendar_view_month, Colors.teal, onTap: () => _navigateToSchedule()), _buildQuickActionButton('Payment Status', Icons.assignment_turned_in, Colors.amber, onTap: () => _navigateToPaymentStatus()), _buildQuickActionButton('History', Icons.history, Colors.indigo), ], ), SizedBox(height: 32.h), // Chitfund Information Text( 'Chitfund Information', style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.w600, color: Colors.black87, ), ), SizedBox(height: 16.h), _buildInfoGrid([ _buildInfoItem('Total Value', _formatIndianCurrency(group.totalValue), Icons.currency_rupee), _buildInfoItem('Monthly Installment', _formatIndianCurrency(group.monthlyInstallment), Icons.payment), _buildInfoItem('Duration', '${group.durationMonths} months', Icons.calendar_today), _buildInfoItem('Max Members', '${group.maxMembers}', Icons.people), _buildInfoItem('Commission', _formatIndianCurrency(group.foremanCommissionAmount), Icons.percent), _buildInfoItem('Draw Date', '${group.drawDate}th', Icons.event), ]), SizedBox(height: 32.h), // Statistics Cards if (stats != null) ...[ Text( 'Statistics', style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.w600, color: Colors.black87, ), ), SizedBox(height: 16.h), _buildStatsGrid(stats), SizedBox(height: 32.h), ], // Recent Activity Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Recent Activity', style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.w600, color: Colors.black87, ), ), TextButton( onPressed: () {}, child: Text( 'See all >', style: TextStyle( fontSize: 14.sp, color: Colors.blue.shade600, fontWeight: FontWeight.w500, ), ), ), ], ), SizedBox(height: 16.h), _buildActivityList(), ], ), ); }); } Widget _buildQuickActionButton(String label, IconData icon, Color color, {VoidCallback? onTap}) { return GestureDetector( onTap: onTap, child: Column( children: [ Container( width: 48.w, height: 48.w, decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(24.r), ), child: Icon(icon, size: 20.w, color: color), ), SizedBox(height: 6.h), Text( label, style: TextStyle( fontSize: 10.sp, fontWeight: FontWeight.w500, color: Colors.black87, ), textAlign: TextAlign.center, maxLines: 1, overflow: TextOverflow.ellipsis, ), ], ), ); } Widget _buildMembersTab() { return Obx(() { final members = _service.groupMembers; if (_service.isLoading.value) { return const Center(child: CircularProgressIndicator()); } if (members.isEmpty) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.people_outline, size: 64.w, color: Colors.grey.shade400, ), SizedBox(height: 16.h), Text( 'No members yet', style: TextStyle( fontSize: 18.sp, color: Colors.grey.shade600, ), ), SizedBox(height: 8.h), Text( 'Add members to get started', style: TextStyle( fontSize: 14.sp, color: Colors.grey.shade500, ), ), SizedBox(height: 24.h), if (widget.group.isForming) Column( children: [ Row( children: [ Expanded( child: ElevatedButton.icon( onPressed: () => _showMemberSelectionDialog(context), icon: const Icon(Icons.people_alt), label: const Text('Select Members'), style: ElevatedButton.styleFrom( backgroundColor: Colors.blue.shade600, foregroundColor: Colors.white, ), ), ), SizedBox(width: 12.w), Expanded( child: ElevatedButton.icon( onPressed: () => _showAddUserDialog(context), icon: const Icon(Icons.person_add), label: const Text('Add New User'), style: ElevatedButton.styleFrom( backgroundColor: Colors.green.shade600, foregroundColor: Colors.white, ), ), ), ], ), SizedBox(height: 12.h), Text( 'Select Members: Choose from existing users\nAdd New User: Create a new user account', textAlign: TextAlign.center, style: TextStyle( fontSize: 12.sp, color: Colors.grey.shade600, ), ), ], ), ], ), ); } return RefreshIndicator( onRefresh: () => _service.loadGroupMembers(widget.group.id), child: ListView.builder( padding: EdgeInsets.all(16.w), itemCount: members.length, itemBuilder: (context, index) { final member = members[index]; return _buildMemberCard(member); }, ), ); }); } Widget _buildPaymentsTab() { return Obx(() { final payments = _paymentService.payments; final pendingPayments = _paymentService.pendingPayments; final paymentSummary = _paymentService.paymentSummary; if (_paymentService.isLoading) { return const Center(child: CircularProgressIndicator()); } return RefreshIndicator( onRefresh: () async { await _paymentService.loadGroupPayments(widget.group.id); await _paymentService.loadPendingPayments(widget.group.id); await _paymentService.loadPaymentSummary(widget.group.id); }, child: SingleChildScrollView( padding: EdgeInsets.all(16.w), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Quick Actions Row( children: [ Expanded( child: ElevatedButton.icon( onPressed: () => _showRecordPaymentDialog(), icon: Icon(Icons.add, size: 20.w), label: Text( 'Record Payment', style: TextStyle(fontSize: 14.sp, fontWeight: FontWeight.w600), ), style: ElevatedButton.styleFrom( backgroundColor: Colors.green.shade600, foregroundColor: Colors.white, padding: EdgeInsets.symmetric(vertical: 12.h), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.r), ), ), ), ), SizedBox(width: 12.w), Expanded( child: OutlinedButton.icon( onPressed: () => _navigateToPaymentHistory(), icon: Icon(Icons.history, size: 20.w), label: Text( 'View History', style: TextStyle(fontSize: 14.sp, fontWeight: FontWeight.w600), ), style: OutlinedButton.styleFrom( padding: EdgeInsets.symmetric(vertical: 12.h), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.r), ), ), ), ), ], ), SizedBox(height: 24.h), // Payment Summary Cards if (paymentSummary.isNotEmpty) ...[ Text( 'Payment Summary', style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.w600, color: Colors.grey.shade800, ), ), SizedBox(height: 16.h), _buildPaymentSummaryCards(paymentSummary), SizedBox(height: 24.h), ], // Pending Payments if (pendingPayments.isNotEmpty) ...[ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Pending Payments (${pendingPayments.length})', style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.w600, color: Colors.grey.shade800, ), ), TextButton( onPressed: () => _navigateToPaymentHistory(), child: Text( 'View All', style: TextStyle(fontSize: 14.sp), ), ), ], ), SizedBox(height: 16.h), ...pendingPayments.take(3).map((pending) => Padding( padding: EdgeInsets.only(bottom: 12.h), child: _buildPendingPaymentCard(pending), )), SizedBox(height: 24.h), ], // Recent Payments if (payments.isNotEmpty) ...[ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Recent Payments', style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.w600, color: Colors.grey.shade800, ), ), TextButton( onPressed: () => _navigateToPaymentHistory(), child: Text( 'View All', style: TextStyle(fontSize: 14.sp), ), ), ], ), SizedBox(height: 16.h), ...payments.take(5).map((payment) => Padding( padding: EdgeInsets.only(bottom: 12.h), child: _buildRecentPaymentCard(payment), )), ] else ...[ // Empty state Center( child: Column( children: [ SizedBox(height: 40.h), Icon( Icons.payment_outlined, size: 64.w, color: Colors.grey.shade400, ), SizedBox(height: 16.h), Text( 'No payments yet', style: TextStyle( fontSize: 18.sp, color: Colors.grey.shade600, ), ), SizedBox(height: 8.h), Text( 'Start recording member payments', style: TextStyle( fontSize: 14.sp, color: Colors.grey.shade500, ), ), ], ), ), ], ], ), ), ); }); } Widget _buildDrawsTab() { return Obx(() { final draws = _service.monthlyDraws; if (_service.isLoading.value) { return const Center(child: CircularProgressIndicator()); } if (draws.isEmpty) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.casino_outlined, size: 64.w, color: Colors.grey.shade400, ), SizedBox(height: 16.h), Text( 'No draws yet', style: TextStyle( fontSize: 18.sp, color: Colors.grey.shade600, ), ), SizedBox(height: 8.h), Text( 'Draws will appear here once conducted', style: TextStyle( fontSize: 14.sp, color: Colors.grey.shade500, ), ), SizedBox(height: 24.h), if (widget.group.isActive) ElevatedButton.icon( onPressed: () => _conductDraw(context), icon: const Icon(Icons.casino), label: const Text('Conduct Draw'), style: ElevatedButton.styleFrom( backgroundColor: Colors.purple.shade600, foregroundColor: Colors.white, ), ), ], ), ); } return RefreshIndicator( onRefresh: () => _service.loadGroupMonthlyDraws(widget.group.id), child: ListView.builder( padding: EdgeInsets.all(16.w), itemCount: draws.length, itemBuilder: (context, index) { final draw = draws[index]; return _buildDrawCard(draw); }, ), ); }); } Widget _buildFinancialTab() { return Obx(() { final financialData = _service.financialData; if (_service.isLoading.value) { return const Center(child: CircularProgressIndicator()); } // Show empty state if no data from backend if (financialData.isEmpty) { return Center( child: Padding( padding: EdgeInsets.all(32.w), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.table_chart_outlined, size: 64.w, color: Colors.grey.shade400, ), SizedBox(height: 16.h), Text( 'No Financial Data Available', style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.w600, color: Colors.grey.shade700, ), ), SizedBox(height: 8.h), Text( 'Financial data will appear here once\nthe group becomes active', textAlign: TextAlign.center, style: TextStyle( fontSize: 14.sp, color: Colors.grey.shade600, ), ), SizedBox(height: 24.h), ElevatedButton.icon( onPressed: () => _service.loadGroupFinancialData(widget.group.id), icon: const Icon(Icons.refresh), label: const Text('Refresh Data'), style: ElevatedButton.styleFrom( backgroundColor: Colors.blue.shade600, foregroundColor: Colors.white, padding: EdgeInsets.symmetric( horizontal: 24.w, vertical: 12.h, ), ), ), ], ), ), ); } return RefreshIndicator( onRefresh: () => _service.loadGroupFinancialData(widget.group.id), child: SingleChildScrollView( padding: EdgeInsets.all(16.w), child: Column( children: [ // Always use compact table for better mobile experience CompactFinancialTable( entries: financialData, isLoading: _service.isLoading.value, onRefresh: () => _service.loadGroupFinancialData(widget.group.id), ), SizedBox(height: 16.h), // Summary cards _buildFinancialSummaryCards(financialData), ], ), ), ); }); } Widget _buildFinancialSummaryCards(List financialData) { if (financialData.isEmpty) return const SizedBox.shrink(); final totalEntry = financialData.lastWhere( (entry) => entry.monthYear.toLowerCase() == 'total', orElse: () => financialData.last, ); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Summary', style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.w600, color: Colors.black87, ), ), SizedBox(height: 16.h), Column( children: [ Row( children: [ Expanded( child: _buildSummaryCard( 'Total Chit Value', _formatIndianCurrency(totalEntry.chitValue), Icons.currency_rupee, Colors.blue, ), ), SizedBox(width: 12.w), Expanded( child: _buildSummaryCard( 'Total Bid Amount', _formatIndianCurrency(totalEntry.bidAmount), Icons.payment, Colors.green, ), ), ], ), SizedBox(height: 12.h), Row( children: [ Expanded( child: _buildSummaryCard( 'Total Dividend', _formatIndianCurrency(totalEntry.dividendAmount), Icons.trending_up, totalEntry.dividendAmount >= 0 ? Colors.green : Colors.red, ), ), SizedBox(width: 12.w), Expanded( child: _buildSummaryCard( 'Total Commission', _formatIndianCurrency(totalEntry.commissionInstallment), Icons.percent, Colors.orange, ), ), ], ), ], ), ], ); } Widget _buildSummaryCard(String title, String value, IconData icon, Color color) { return Container( padding: EdgeInsets.all(16.w), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12.r), boxShadow: [ BoxShadow( color: Colors.grey.shade200, blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(icon, color: color, size: 20.w), SizedBox(width: 6.w), Expanded( child: Text( title, style: TextStyle( fontSize: 14.sp, color: Colors.grey.shade600, fontWeight: FontWeight.w500, ), overflow: TextOverflow.ellipsis, ), ), ], ), SizedBox(height: 8.h), Text( value, style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.w600, color: color, ), overflow: TextOverflow.ellipsis, ), ], ), ); } Widget _buildDrawCard(MonthlyDraw draw) { return Container( margin: EdgeInsets.only(bottom: 12.h), padding: EdgeInsets.all(16.w), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12.r), boxShadow: [ BoxShadow( color: Colors.grey.shade200, blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: Row( children: [ Container( padding: EdgeInsets.all(12.w), decoration: BoxDecoration( color: _getDrawStatusColor(draw.status).withOpacity(0.1), borderRadius: BorderRadius.circular(12.r), ), child: Icon( Icons.casino, color: _getDrawStatusColor(draw.status), size: 24.w, ), ), SizedBox(width: 16.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '${draw.month}/${draw.year} Draw', style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, color: Colors.black87, ), ), SizedBox(height: 4.h), if (draw.winner != null) Text( 'Winner: ${draw.winner!.fullName}', style: TextStyle( fontSize: 14.sp, color: Colors.grey.shade600, ), ), SizedBox(height: 8.h), Row( children: [ _buildDrawStatusChip(draw.status), SizedBox(width: 6.w), Text( _formatIndianCurrency(draw.prizeAmount), style: TextStyle( fontSize: 14.sp, color: Colors.green.shade600, fontWeight: FontWeight.w600, ), ), ], ), ], ), ), Column( children: [ Text( '${draw.drawDate.day}/${draw.drawDate.month}/${draw.drawDate.year}', style: TextStyle( fontSize: 12.sp, color: Colors.grey.shade600, ), ), SizedBox(height: 8.h), // Action Buttons Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( icon: Icon(Icons.edit, size: 20.w), color: Colors.orange.shade600, tooltip: 'Edit Draw', onPressed: () => _editDraw(draw), padding: EdgeInsets.all(8.w), constraints: BoxConstraints( minWidth: 36.w, minHeight: 36.h, ), ), IconButton( icon: Icon(Icons.delete, size: 20.w), color: Colors.red.shade600, tooltip: 'Delete Draw', onPressed: () => _confirmDeleteDraw(draw), padding: EdgeInsets.all(8.w), constraints: BoxConstraints( minWidth: 36.w, minHeight: 36.h, ), ), ], ), ], ), ], ), ); } Widget _buildDrawStatusChip(String status) { Color color; String label; switch (status) { case 'completed': color = Colors.green; label = 'Completed'; break; case 'pending': color = Colors.orange; label = 'Pending'; break; case 'cancelled': color = Colors.red; label = 'Cancelled'; break; default: color = Colors.grey; label = status; } return Container( padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(12.r), border: Border.all(color: color, width: 0.5), ), child: Text( label, style: TextStyle( color: color, fontSize: 12.sp, fontWeight: FontWeight.w500, ), ), ); } Color _getDrawStatusColor(String status) { switch (status) { case 'completed': return Colors.green; case 'pending': return Colors.orange; case 'cancelled': return Colors.red; default: return Colors.grey; } } void _showDrawDetails(MonthlyDraw draw) { Get.snackbar('Coming Soon', 'Draw details page will be implemented next'); } Widget _buildStatusChip(String status) { Color color; String label; switch (status) { case 'forming': color = Colors.orange; label = 'Forming'; break; case 'active': color = Colors.green; label = 'Active'; break; case 'completed': color = Colors.grey; label = 'Completed'; break; default: color = Colors.grey; label = status; } return Container( padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h), decoration: BoxDecoration( color: color.withOpacity(0.2), borderRadius: BorderRadius.circular(16.r), border: Border.all(color: color), ), child: Text( label, style: TextStyle( color: color, fontSize: 12.sp, fontWeight: FontWeight.w600, ), ), ); } Widget _buildInfoGrid(List items) { return Column( children: [ for (int i = 0; i < items.length; i += 2) Padding( padding: EdgeInsets.only(bottom: 12.h), child: Row( children: [ Expanded(child: items[i]), if (i + 1 < items.length) ...[ SizedBox(width: 12.w), Expanded(child: items[i + 1]), ], ], ), ), ], ); } Widget _buildInfoItem(String label, String value, IconData icon) { return Container( padding: EdgeInsets.all(12.w), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8.r), boxShadow: [ BoxShadow( color: Colors.grey.shade200, blurRadius: 2, offset: const Offset(0, 1), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Row( children: [ Icon(icon, size: 16.w, color: Colors.blue.shade600), SizedBox(width: 6.w), Expanded( child: Text( label, style: TextStyle( fontSize: 12.sp, color: Colors.grey.shade600, fontWeight: FontWeight.w500, ), overflow: TextOverflow.ellipsis, ), ), ], ), SizedBox(height: 6.h), Text( value, style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w600, color: Colors.black87, ), overflow: TextOverflow.ellipsis, maxLines: 2, ), ], ), ); } Widget _buildStatsCard(String title, String value, IconData icon, Color color) { return Container( padding: EdgeInsets.all(16.w), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12.r), boxShadow: [ BoxShadow( color: Colors.grey.shade200, blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(icon, color: color, size: 24.w), const Spacer(), Icon( Icons.trending_up, color: Colors.green.shade600, size: 20.w, ), ], ), SizedBox(height: 12.h), Text( value, style: TextStyle( fontSize: 20.sp, fontWeight: FontWeight.bold, color: Colors.black87, ), ), SizedBox(height: 4.h), Text( title, style: TextStyle( fontSize: 14.sp, color: Colors.grey.shade600, ), overflow: TextOverflow.ellipsis, ), ], ), ); } Widget _buildStatsGrid(Map stats) { return Column( children: [ Row( children: [ Expanded( child: _buildStatsCard( 'Total Collection', _formatIndianCurrency(stats['total_collection']?.toDouble() ?? 0), Icons.account_balance_wallet, Colors.blue, ), ), SizedBox(width: 12.w), Expanded( child: _buildStatsCard( 'Pending Amount', _formatIndianCurrency(stats['pending_amount']?.toDouble() ?? 0), Icons.pending, Colors.orange, ), ), ], ), SizedBox(height: 12.h), Row( children: [ Expanded( child: _buildStatsCard( 'Completed Draws', '${stats['completed_draws'] ?? 0}', Icons.check_circle, Colors.green, ), ), SizedBox(width: 12.w), Expanded( child: _buildStatsCard( 'Remaining Draws', '${stats['remaining_draws'] ?? 0}', Icons.schedule, Colors.purple, ), ), ], ), ], ); } Widget _buildActivityList() { return Column( children: [ _buildActivityItem( 'New member joined', '2 hours ago', Icons.person_add, Colors.green, '+${_formatIndianCurrency(widget.group.monthlyInstallment)}', ), _buildActivityItem( 'Payment received', '4 hours ago', Icons.payment, Colors.blue, '+${_formatIndianCurrency(widget.group.monthlyInstallment)}', ), _buildActivityItem( 'Monthly draw completed', '1 day ago', Icons.casino, Colors.orange, '-${_formatIndianCurrency(widget.group.totalValue / widget.group.maxMembers)}', ), _buildActivityItem( 'Chitfund started', '3 days ago', Icons.play_arrow, Colors.purple, '₹0', ), _buildActivityItem( 'Chitfund created', '1 week ago', Icons.group_add, Colors.indigo, '₹0', ), ], ); } Widget _buildActivityItem(String title, String time, IconData icon, Color color, String amount) { return Container( margin: EdgeInsets.only(bottom: 12.h), padding: EdgeInsets.all(16.w), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12.r), boxShadow: [ BoxShadow( color: Colors.grey.shade200, blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: Row( children: [ Container( padding: EdgeInsets.all(12.w), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(12.r), ), child: Icon(icon, color: color, size: 20.w), ), SizedBox(width: 16.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w500, color: Colors.black87, ), ), SizedBox(height: 4.h), Text( time, style: TextStyle( fontSize: 14.sp, color: Colors.grey.shade600, ), ), ], ), ), Text( amount, style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, color: amount.startsWith('+') ? Colors.green.shade600 : amount.startsWith('-') ? Colors.red.shade600 : Colors.grey.shade600, ), ), ], ), ); } Widget _buildMemberCard(GroupMember member) { return Container( margin: EdgeInsets.only(bottom: 12.h), padding: EdgeInsets.all(16.w), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12.r), boxShadow: [ BoxShadow( color: Colors.grey.shade200, blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: Row( children: [ // Member Number Badge Container( width: 48.w, height: 48.w, decoration: BoxDecoration( color: Colors.purple.shade600, shape: BoxShape.circle, boxShadow: [ BoxShadow( color: Colors.purple.shade200, blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: Center( child: Text( '#${member.memberNumber}', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 18.sp, ), ), ), ), SizedBox(width: 16.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text( member.user?.fullName ?? 'Unknown Member', style: TextStyle( fontWeight: FontWeight.w600, fontSize: 16.sp, color: Colors.black87, ), ), ), Container( padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 2.h), decoration: BoxDecoration( color: Colors.purple.shade50, borderRadius: BorderRadius.circular(8.r), border: Border.all(color: Colors.purple.shade200), ), child: Text( 'Member #${member.memberNumber}', style: TextStyle( fontSize: 11.sp, color: Colors.purple.shade700, fontWeight: FontWeight.w600, ), ), ), ], ), SizedBox(height: 4.h), Text( member.user?.mobileNumber ?? '', style: TextStyle(fontSize: 14.sp, color: Colors.grey.shade600), ), SizedBox(height: 8.h), Row( children: [ _buildMemberStatusChip(member.status), SizedBox(width: 6.w), Text( '${_formatIndianCurrency(member.totalPaid)} paid', style: TextStyle( fontSize: 14.sp, color: Colors.green.shade600, fontWeight: FontWeight.w500, ), ), ], ), ], ), ), PopupMenuButton( icon: Icon(Icons.more_vert, size: 24.w, color: Colors.grey.shade600), onSelected: (value) => _handleMemberAction(value, member), itemBuilder: (context) => [ const PopupMenuItem( value: 'view', child: Row( children: [ Icon(Icons.visibility, size: 18), SizedBox(width: 8), Text('View Details'), ], ), ), const PopupMenuItem( value: 'edit', child: Row( children: [ Icon(Icons.edit, size: 18, color: Colors.blue), SizedBox(width: 8), Text('Edit Member', style: TextStyle(color: Colors.blue)), ], ), ), const PopupMenuDivider(), if (member.status == 'active') const PopupMenuItem( value: 'suspend', child: Row( children: [ Icon(Icons.pause, size: 18), SizedBox(width: 8), Text('Suspend'), ], ), ), if (member.status == 'suspended') const PopupMenuItem( value: 'activate', child: Row( children: [ Icon(Icons.play_arrow, size: 18), SizedBox(width: 8), Text('Activate'), ], ), ), const PopupMenuItem( value: 'remove', child: Row( children: [ Icon(Icons.remove_circle, size: 18, color: Colors.red), SizedBox(width: 8), Text('Remove', style: TextStyle(color: Colors.red)), ], ), ), ], ), ], ), ); } Widget _buildPaymentCard(Payment payment) { return Container( margin: EdgeInsets.only(bottom: 12.h), padding: EdgeInsets.all(16.w), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12.r), boxShadow: [ BoxShadow( color: Colors.grey.shade200, blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: Row( children: [ Container( padding: EdgeInsets.all(12.w), decoration: BoxDecoration( color: _getPaymentStatusColor(payment.status).withOpacity(0.1), borderRadius: BorderRadius.circular(12.r), ), child: Icon( Icons.payment, color: _getPaymentStatusColor(payment.status), size: 20.w, ), ), SizedBox(width: 16.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( payment.user?.fullName ?? 'Unknown Member', style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, color: Colors.black87, ), ), SizedBox(height: 4.h), Text( _formatIndianCurrency(payment.amount), style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, color: Colors.green.shade600, ), ), SizedBox(height: 8.h), Row( children: [ _buildPaymentStatusChip(payment.status), SizedBox(width: 6.w), Text( '${payment.month}/${payment.year}', style: TextStyle(fontSize: 14.sp, color: Colors.grey.shade600), ), ], ), ], ), ), Text( payment.paidAt != null ? '${payment.paidAt!.day}/${payment.paidAt!.month}/${payment.paidAt!.year}' : 'Pending', style: TextStyle( fontSize: 12.sp, color: Colors.grey.shade600, ), ), ], ), ); } Widget _buildMemberStatusChip(String status) { Color color; String label; switch (status) { case 'active': color = Colors.green; label = 'Active'; break; case 'suspended': color = Colors.orange; label = 'Suspended'; break; case 'inactive': color = Colors.grey; label = 'Inactive'; break; default: color = Colors.grey; label = status; } return Container( padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(12.r), border: Border.all(color: color, width: 0.5), ), child: Text( label, style: TextStyle( color: color, fontSize: 12.sp, fontWeight: FontWeight.w500, ), ), ); } Widget _buildPaymentStatusChip(String status) { Color color; String label; switch (status) { case 'paid': color = Colors.green; label = 'Paid'; break; case 'pending': color = Colors.orange; label = 'Pending'; break; case 'overdue': color = Colors.red; label = 'Overdue'; break; default: color = Colors.grey; label = status; } return Container( padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(12.r), border: Border.all(color: color, width: 0.5), ), child: Text( label, style: TextStyle( color: color, fontSize: 12.sp, fontWeight: FontWeight.w500, ), ), ); } void _showMemberSelectionDialog(BuildContext context) { showDialog( context: context, barrierDismissible: false, builder: (context) => MemberSelectionDialog(group: widget.group), ); } void _showAddUserDialog(BuildContext context) { showDialog( context: context, barrierDismissible: false, builder: (context) => const AddUserDialog(), ); } void _showAddPastDrawDialog(BuildContext context) { // Determine which month to add based on existing draws final existingDraws = _service.monthlyDraws; final nextMonth = existingDraws.length + 1; showDialog( context: context, barrierDismissible: false, builder: (context) => AddPastDrawDialog( group: widget.group, monthNumber: nextMonth, ), ).then((result) { if (result == true) { // Reload draws after adding _service.loadGroupMonthlyDraws(widget.group.id); } }); } void _showAddPastPaymentsDialog(BuildContext context) { // Ask which month to add payments for _showMonthSelectionDialog(context); } void _showMonthSelectionDialog(BuildContext context) { final monthController = TextEditingController(text: '1'); showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Select Month'), content: Column( mainAxisSize: MainAxisSize.min, children: [ const Text('Which month do you want to add payments for?'), SizedBox(height: 16.h), TextField( controller: monthController, keyboardType: TextInputType.number, decoration: const InputDecoration( labelText: 'Month Number', hintText: 'e.g., 1, 2, 3', border: OutlineInputBorder(), ), ), ], ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Cancel'), ), ElevatedButton( onPressed: () { final month = int.tryParse(monthController.text); if (month != null && month >= 1) { Navigator.pop(context); _showAddPastPaymentsDialogForMonth(context, month); } }, child: const Text('Continue'), ), ], ), ); } void _showAddPastPaymentsDialogForMonth(BuildContext context, int monthNumber) { showDialog( context: context, barrierDismissible: false, builder: (context) => AddPastPaymentsDialog( group: widget.group, monthNumber: monthNumber, ), ).then((result) { if (result == true) { // Reload payments after adding _paymentService.loadGroupPayments(widget.group.id); } }); } void _conductDraw(BuildContext context) { showDialog( context: context, barrierDismissible: false, builder: (context) => CombinedDrawDialog(group: widget.group), ); } void _editDraw(MonthlyDraw draw) { showDialog( context: context, builder: (context) => EditDrawDialog(draw: draw), ).then((result) { if (result == true) { _service.loadGroupMonthlyDraws(widget.group.id); } }); } void _confirmDeleteDraw(MonthlyDraw draw) { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Delete Draw?'), content: Text( 'Are you sure you want to delete the draw for ${draw.month}/${draw.year}?\n\n' 'Winner: ${draw.winner?.fullName ?? "Unknown"}\n' 'Prize: ${_formatIndianCurrency(draw.prizeAmount)}\n\n' 'This action cannot be undone.', ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Cancel'), ), ElevatedButton( onPressed: () { Navigator.pop(context); _deleteDraw(draw); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.red, foregroundColor: Colors.white, ), child: const Text('Delete'), ), ], ), ); } Future _deleteDraw(MonthlyDraw draw) async { try { final apiService = ApiService(); final response = await apiService.deleteMonthlyDraw(draw.id); if (response['success']) { Get.snackbar( 'Success', 'Draw deleted successfully', backgroundColor: Colors.green, colorText: Colors.white, ); await _service.loadGroupMonthlyDraws(widget.group.id); } else { Get.snackbar( 'Error', response['message'] ?? 'Failed to delete draw', backgroundColor: Colors.red, colorText: Colors.white, ); } } catch (e) { Get.snackbar( 'Error', 'Failed to delete draw: ${e.toString()}', backgroundColor: Colors.red, colorText: Colors.white, ); } } void _editGroup() { if (widget.group.status != 'forming') { Get.snackbar( 'Cannot Edit', 'Group can only be edited while in "Forming" status', backgroundColor: Colors.orange, colorText: Colors.white, ); return; } showDialog( context: context, builder: (context) => EditGroupDialog(group: widget.group), ).then((result) { if (result == true) { _service.loadChitGroupDetails(widget.group.id); _service.loadManagerChitGroups(); } }); } void _confirmDeleteGroup() { if (widget.group.status != 'forming') { Get.snackbar( 'Cannot Delete', 'Group can only be deleted while in "Forming" status', backgroundColor: Colors.orange, colorText: Colors.white, duration: const Duration(seconds: 3), ); return; } // Count only ACTIVE members (not removed or inactive) final activeMemberCount = _service.groupMembers.where((m) => m.status == 'active').length; if (activeMemberCount > 0) { Get.snackbar( 'Cannot Delete', 'Group has $activeMemberCount active member(s). Remove all active members first.', backgroundColor: Colors.orange, colorText: Colors.white, duration: const Duration(seconds: 4), ); return; } // Show removed members count if any final removedMemberCount = _service.groupMembers.where((m) => m.status == 'removed').length; final totalMembers = _service.groupMembers.length; showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Delete Group?'), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Are you sure you want to delete "${widget.group.name}"?', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600), ), SizedBox(height: 16.h), if (removedMemberCount > 0) ...[ Container( padding: EdgeInsets.all(12.w), decoration: BoxDecoration( color: Colors.blue.shade50, borderRadius: BorderRadius.circular(8.r), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Group Status:', style: TextStyle( fontWeight: FontWeight.w600, fontSize: 14.sp, ), ), SizedBox(height: 6.h), Text('• Active members: 0', style: TextStyle(fontSize: 13.sp)), Text('• Removed members: $removedMemberCount', style: TextStyle(fontSize: 13.sp, color: Colors.grey.shade600)), Text('• Total records: $totalMembers', style: TextStyle(fontSize: 13.sp)), ], ), ), SizedBox(height: 12.h), ], Text( 'This action cannot be undone.', style: TextStyle( color: Colors.red.shade700, fontWeight: FontWeight.w600, fontSize: 14.sp, ), ), ], ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Cancel'), ), ElevatedButton( onPressed: () { Navigator.pop(context); _deleteGroup(); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.red, foregroundColor: Colors.white, ), child: const Text('Delete Group'), ), ], ), ); } Future _deleteGroup() async { try { final success = await _service.deleteChitGroup(widget.group.id); if (success) { Get.back(); // Close group details page Get.snackbar( 'Success', 'Group deleted successfully', backgroundColor: Colors.green, colorText: Colors.white, ); } } catch (e) { Get.snackbar( 'Error', 'Failed to delete group: ${e.toString()}', backgroundColor: Colors.red, colorText: Colors.white, ); } } void _startChitfund() async { final success = await _service.startChitGroup(widget.group.id); if (success) { Get.snackbar('Success', 'Chitfund started successfully'); } } void _navigateToSchedule() { Get.to(() => ChitfundSchedulePage(chitfund: widget.group)); } void _navigateToPaymentStatus() { final currentDate = DateTime.now(); Get.to(() => MonthlyPaymentStatusPage( chitfund: widget.group, month: currentDate.month, year: currentDate.year, )); } void _handleMemberAction(String action, GroupMember member) { switch (action) { case 'view': Get.snackbar('Coming Soon', 'Member details page will be implemented next'); break; case 'edit': _editMember(member); break; case 'suspend': _updateMemberStatus(member.id, 'suspended'); break; case 'activate': _updateMemberStatus(member.id, 'active'); break; case 'remove': _showRemoveMemberDialog(member); break; } } void _editMember(GroupMember member) { showDialog( context: context, builder: (context) => EditMemberDialog(member: member), ).then((result) { if (result == true) { _service.loadGroupMembers(widget.group.id); } }); } void _updateMemberStatus(String memberId, String status) async { final success = await _service.updateMemberStatus(widget.group.id, memberId, status); if (success) { Get.snackbar('Success', 'Member status updated successfully'); } } void _showRemoveMemberDialog(GroupMember member) { showDialog( context: context, builder: (context) => AlertDialog( title: Row( children: [ Icon(Icons.remove_circle_outline, color: Colors.orange.shade700), SizedBox(width: 8.w), const Text('Remove Member from Group'), ], ), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Are you sure you want to remove ${member.user?.fullName ?? 'this member'} (Member #${member.memberNumber}) from this group?', style: TextStyle(fontSize: 15.sp), ), SizedBox(height: 16.h), Container( padding: EdgeInsets.all(12.w), decoration: BoxDecoration( color: Colors.blue.shade50, borderRadius: BorderRadius.circular(8.r), border: Border.all(color: Colors.blue.shade200), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.info_outline, size: 16.w, color: Colors.blue.shade700), SizedBox(width: 6.w), Text( 'Important:', style: TextStyle( fontWeight: FontWeight.bold, color: Colors.blue.shade900, fontSize: 13.sp, ), ), ], ), SizedBox(height: 6.h), Text( '• Member is only removed from THIS group\n' '• Their account remains active\n' '• They can still be added to other groups\n' '• Their user data is preserved', style: TextStyle( fontSize: 12.sp, color: Colors.blue.shade900, height: 1.4, ), ), ], ), ), ], ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Cancel'), ), ElevatedButton( onPressed: () async { Navigator.of(context).pop(); final success = await _service.removeMemberFromGroup(widget.group.id, member.userId); if (success) { Get.snackbar( 'Member Removed from Group', '${member.user?.fullName ?? 'Member'} removed from ${widget.group.name}. They can still be added to other groups.', backgroundColor: Colors.green, colorText: Colors.white, duration: const Duration(seconds: 4), ); } }, style: ElevatedButton.styleFrom( backgroundColor: Colors.orange.shade600, foregroundColor: Colors.white, ), child: const Text('Remove from Group'), ), ], ), ); } void _showRecordPaymentDialog() { showDialog( context: context, builder: (context) => RecordPaymentDialog(group: widget.group), ).then((result) { if (result == true) { // Refresh payment data _paymentService.loadGroupPayments(widget.group.id); _paymentService.loadPendingPayments(widget.group.id); _paymentService.loadPaymentSummary(widget.group.id); } }); } void _navigateToPaymentHistory() { Get.to(() => PaymentHistoryPage(group: widget.group)); } Widget _buildPaymentSummaryCards(Map summary) { final stats = summary['payment_stats']; if (stats == null) return const SizedBox.shrink(); return Column( children: [ IntrinsicHeight( child: Row( children: [ Expanded( child: _buildPaymentStatCard( 'Collected', _formatIndianCurrency(stats['total_collection'] is num ? stats['total_collection'].toDouble() : 0), Icons.check_circle, Colors.green, ), ), SizedBox(width: 8.w), Expanded( child: _buildPaymentStatCard( 'Pending', '${stats['pending_payments'] ?? 0}', Icons.schedule, Colors.orange, ), ), ], ), ), SizedBox(height: 8.h), IntrinsicHeight( child: Row( children: [ Expanded( child: _buildPaymentStatCard( 'Success Rate', '${(stats['collection_percentage'] is num ? stats['collection_percentage'].toStringAsFixed(1) : '0.0')}%', Icons.trending_up, Colors.blue, ), ), SizedBox(width: 8.w), Expanded( child: _buildPaymentStatCard( 'Total Payments', '${stats['total_payments'] ?? 0}', Icons.payments, Colors.purple, ), ), ], ), ), ], ); } Widget _buildPaymentStatCard(String title, String value, IconData icon, Color color) { return Card( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.r), ), child: Padding( padding: EdgeInsets.all(4.w), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Row( children: [ Icon(icon, color: color, size: 12.w), SizedBox(width: 2.w), Expanded( child: Text( title, style: TextStyle( fontSize: 8.sp, color: Colors.grey.shade600, fontWeight: FontWeight.w500, ), overflow: TextOverflow.ellipsis, maxLines: 1, ), ), ], ), Text( value, style: TextStyle( fontSize: 10.sp, fontWeight: FontWeight.w600, color: color, ), overflow: TextOverflow.ellipsis, maxLines: 1, ), ], ), ), ); } Widget _buildPendingPaymentCard(Map pending) { final member = pending['member']; final amountDue = pending['amount_due']; return Card( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.r), ), child: Padding( padding: EdgeInsets.all(10.w), child: Row( children: [ Container( padding: EdgeInsets.all(6.w), decoration: BoxDecoration( color: Colors.orange.shade100, borderRadius: BorderRadius.circular(6.r), ), child: Icon( Icons.schedule, color: Colors.orange.shade600, size: 16.w, ), ), SizedBox(width: 6.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( member['full_name'] ?? 'Unknown Member', style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w600, color: Colors.grey.shade800, ), overflow: TextOverflow.ellipsis, maxLines: 1, ), Text( member['mobile_number'] ?? '', style: TextStyle( fontSize: 12.sp, color: Colors.grey.shade600, ), overflow: TextOverflow.ellipsis, maxLines: 1, ), ], ), ), Column( crossAxisAlignment: CrossAxisAlignment.end, mainAxisSize: MainAxisSize.min, children: [ Text( _formatIndianCurrency(amountDue is num ? amountDue.toDouble() : 0), style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w600, color: Colors.grey.shade800, ), ), SizedBox(height: 2.h), ElevatedButton( onPressed: () => _recordPaymentForMember(member['id']), style: ElevatedButton.styleFrom( backgroundColor: Colors.green.shade600, foregroundColor: Colors.white, padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h), minimumSize: Size(0, 0), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(4.r), ), ), child: Text( 'Record', style: TextStyle(fontSize: 10.sp), ), ), ], ), ], ), ), ); } Widget _buildRecentPaymentCard(Payment payment) { return Card( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.r), ), child: Padding( padding: EdgeInsets.all(10.w), child: Row( children: [ Container( padding: EdgeInsets.all(6.w), decoration: BoxDecoration( color: _getPaymentStatusColor(payment.status).withOpacity(0.1), borderRadius: BorderRadius.circular(6.r), ), child: Icon( _getPaymentStatusIcon(payment.status), color: _getPaymentStatusColor(payment.status), size: 16.w, ), ), SizedBox(width: 6.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( payment.user?.fullName ?? 'Unknown Member', style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w600, color: Colors.grey.shade800, ), overflow: TextOverflow.ellipsis, maxLines: 1, ), Text( '${_getMonthName(payment.month)} ${payment.year}', style: TextStyle( fontSize: 12.sp, color: Colors.grey.shade600, ), ), ], ), ), Column( crossAxisAlignment: CrossAxisAlignment.end, mainAxisSize: MainAxisSize.min, children: [ Text( payment.formattedAmount, style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w600, color: Colors.grey.shade800, ), ), SizedBox(height: 1.h), Container( padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 1.h), decoration: BoxDecoration( color: _getPaymentStatusColor(payment.status).withOpacity(0.1), borderRadius: BorderRadius.circular(8.r), border: Border.all( color: _getPaymentStatusColor(payment.status).withOpacity(0.3), ), ), child: Text( payment.status.toUpperCase(), style: TextStyle( fontSize: 8.sp, fontWeight: FontWeight.w600, color: _getPaymentStatusColor(payment.status), ), ), ), ], ), ], ), ), ); } void _recordPaymentForMember(String memberId) { final member = widget.group.members?.firstWhere( (m) => m.userId == memberId, orElse: () => widget.group.members!.first, ); showDialog( context: context, builder: (context) => RecordPaymentDialog( group: widget.group, selectedMember: member, ), ).then((result) { if (result == true) { // Refresh payment data _paymentService.loadGroupPayments(widget.group.id); _paymentService.loadPendingPayments(widget.group.id); _paymentService.loadPaymentSummary(widget.group.id); } }); } Color _getPaymentStatusColor(String status) { switch (status) { case 'success': case 'paid': return Colors.green; case 'pending': return Colors.orange; case 'failed': return Colors.red; case 'overdue': return Colors.red.shade700; case 'cancelled': return Colors.grey; default: return Colors.grey; } } IconData _getPaymentStatusIcon(String status) { switch (status) { case 'success': return Icons.check_circle; case 'pending': return Icons.schedule; case 'failed': return Icons.error; case 'cancelled': return Icons.cancel; default: return Icons.help; } } String _getMonthName(int month) { const months = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]; return months[month - 1]; } String _formatIndianCurrency(double amount) { // Convert to integer to avoid decimal places int intAmount = amount.round(); // Format with Indian numbering system (commas every 2 digits after the first 3) String amountStr = intAmount.toString(); String formatted = ''; if (amountStr.length <= 3) { formatted = amountStr; } else { // For amounts > 999, use Indian comma system int remaining = amountStr.length; int start = 0; // First group (rightmost 3 digits) if (remaining > 3) { formatted = amountStr.substring(amountStr.length - 3); remaining -= 3; start = amountStr.length - 3; } else { formatted = amountStr; remaining = 0; } // Subsequent groups (2 digits each) while (remaining > 0) { int groupSize = remaining >= 2 ? 2 : remaining; int groupStart = start - groupSize; String group = amountStr.substring(groupStart, start); formatted = group + ',' + formatted; start = groupStart; remaining -= groupSize; } } return '₹$formatted'; } Widget _buildCurrentMonthSection(ChitGroup group) { final now = DateTime.now(); final currentMonth = now.month; final currentYear = now.year; final currentMonthName = _getMonthName(currentMonth); // Calculate current month number since chitfund started final startDate = group.startDate; int currentMonthNumber = 1; if (startDate != null) { final monthsSinceStart = (currentYear - startDate.year) * 12 + (currentMonth - startDate.month); currentMonthNumber = monthsSinceStart + 1; } // Calculate next month final nextMonth = currentMonth == 12 ? 1 : currentMonth + 1; final nextYear = currentMonth == 12 ? currentYear + 1 : currentYear; final nextMonthName = _getMonthName(nextMonth); final nextMonthNumber = currentMonthNumber + 1; // Get draw data - show LATEST AVAILABLE draw (most recent conducted draw) // Sort draws by year and month to get the most recent one final sortedDraws = _service.monthlyDraws.toList() ..sort((a, b) { if (a.year != b.year) return b.year.compareTo(a.year); return b.month.compareTo(a.month); }); // Try to find current month's draw first, otherwise show latest available final currentDraw = _service.monthlyDraws.firstWhereOrNull( (draw) => draw.month == currentMonth && draw.year == currentYear, ) ?? (sortedDraws.isNotEmpty ? sortedDraws.first : null); // If we're showing latest draw (not current month), update display names String displayCurrentMonth = currentMonthName; int displayCurrentYear = currentYear; int displayCurrentMonthNum = currentMonthNumber; if (currentDraw != null && (currentDraw.month != currentMonth || currentDraw.year != currentYear)) { // We're showing a past draw as "latest" displayCurrentMonth = _getMonthName(currentDraw.month); displayCurrentYear = currentDraw.year; // Calculate what cycle month this was if (startDate != null) { final monthsSinceStart = (currentDraw.year - startDate.year) * 12 + (currentDraw.month - startDate.month); displayCurrentMonthNum = monthsSinceStart + 1; } } // Helper function to convert month/year to financial data format (e.g., "Oct-25") String getFinancialMonthKey(int month, int year) { const monthAbbreviations = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; final monthAbbr = monthAbbreviations[month - 1]; final yearShort = year.toString().substring(2); // "2025" -> "25" return '$monthAbbr-$yearShort'; } // Get financial data for displayed month (could be current or latest draw month) final financialData = _service.financialData; // For the displayed draw month (current or latest) final displayedMonthKey = getFinancialMonthKey( currentDraw?.month ?? currentMonth, currentDraw?.year ?? currentYear ); final currentMonthFinancial = financialData.firstWhereOrNull( (entry) => entry.monthYear == displayedMonthKey, ); // For next month final nextMonthKey = getFinancialMonthKey(nextMonth, nextYear); final nextMonthFinancial = financialData.firstWhereOrNull( (entry) => entry.monthYear == nextMonthKey, ); // Only show section if we have actual data (not just fallbacks) final hasCurrentData = currentDraw != null || currentMonthFinancial != null; final hasNextData = nextMonthFinancial != null; // If no data at all, don't show confusing fallback values if (!hasCurrentData && !hasNextData && _service.monthlyDraws.isEmpty) { return SizedBox.shrink(); // Hide section if no real data } // Determine bid/prize amounts - ALWAYS use financial data bid_amount (the actual bid for that month) // The draw's prize_amount might be just the monthly installment, but we want to show the actual bid final currentBidAmount = currentMonthFinancial?.bidAmount ?? currentDraw?.prizeAmount; final currentPrizeAmount = currentMonthFinancial?.bidAmount ?? currentDraw?.prizeAmount; // For next month final nextBidAmount = nextMonthFinancial?.bidAmount; final nextPrizePool = nextMonthFinancial?.bidAmount; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Current Month & Draws', style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.w600, color: Colors.black87, ), ), SizedBox(height: 16.h), // Current Month Card Container( width: double.infinity, padding: EdgeInsets.all(16.w), decoration: BoxDecoration( color: Colors.green.shade50, borderRadius: BorderRadius.circular(12.r), border: Border.all(color: Colors.green.shade200), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.calendar_today, color: Colors.green.shade600, size: 20.w), SizedBox(width: 8.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( currentDraw != null && (currentDraw.month != currentMonth || currentDraw.year != currentYear) ? 'Latest Draw' : 'Current Month', style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, color: Colors.green.shade800, ), ), if (currentDraw != null && (currentDraw.month != currentMonth || currentDraw.year != currentYear)) Text( 'No draw for current month yet', style: TextStyle( fontSize: 12.sp, color: Colors.orange.shade700, fontStyle: FontStyle.italic, ), ), ], ), ), ], ), SizedBox(height: 12.h), Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '$displayCurrentMonth $displayCurrentYear', style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.bold, color: Colors.green.shade700, ), ), Text( 'Month $displayCurrentMonthNum of ${group.durationMonths}', style: TextStyle( fontSize: 14.sp, color: Colors.green.shade600, ), ), ], ), ), if (currentBidAmount != null) Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( _formatIndianCurrency(currentBidAmount), style: TextStyle( fontSize: 20.sp, fontWeight: FontWeight.bold, color: Colors.green.shade700, ), ), Text( 'Current Bid', style: TextStyle( fontSize: 12.sp, color: Colors.green.shade600, ), ), ], ) else Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( 'No Data', style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w500, color: Colors.grey.shade500, ), ), Text( 'Pending', style: TextStyle( fontSize: 12.sp, color: Colors.grey.shade500, ), ), ], ), ], ), SizedBox(height: 12.h), // Current Month Draw Winner (if available) Container( width: double.infinity, padding: EdgeInsets.all(12.w), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8.r), border: Border.all(color: Colors.green.shade100), ), child: Row( children: [ Icon(Icons.emoji_events, color: Colors.amber.shade600, size: 20.w), SizedBox(width: 8.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Draw Winner', style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w600, color: Colors.green.shade700, ), ), Text( currentDraw?.winner?.fullName ?? 'Not Conducted', style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w500, color: currentDraw?.winner?.fullName != null ? Colors.green.shade800 : Colors.grey.shade500, ), ), ], ), ), if (currentPrizeAmount != null) Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( _formatIndianCurrency(currentPrizeAmount), style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.bold, color: Colors.green.shade700, ), ), Text( 'Prize Amount', style: TextStyle( fontSize: 12.sp, color: Colors.green.shade600, ), ), ], ) else Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( 'Not Conducted', style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w500, color: Colors.grey.shade500, ), ), Text( 'No Draw Yet', style: TextStyle( fontSize: 11.sp, color: Colors.grey.shade500, ), ), ], ), ], ), ), ], ), ), SizedBox(height: 16.h), // Upcoming Month Card Container( width: double.infinity, padding: EdgeInsets.all(16.w), decoration: BoxDecoration( color: Colors.blue.shade50, borderRadius: BorderRadius.circular(12.r), border: Border.all(color: Colors.blue.shade200), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.schedule, color: Colors.blue.shade600, size: 20.w), SizedBox(width: 8.w), Text( 'Upcoming Month', style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, color: Colors.blue.shade800, ), ), ], ), SizedBox(height: 12.h), Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '$nextMonthName $nextYear', style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.bold, color: Colors.blue.shade700, ), ), Text( 'Month $nextMonthNumber of ${group.durationMonths}', style: TextStyle( fontSize: 14.sp, color: Colors.blue.shade600, ), ), ], ), ), if (nextBidAmount != null) Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( _formatIndianCurrency(nextBidAmount), style: TextStyle( fontSize: 20.sp, fontWeight: FontWeight.bold, color: Colors.blue.shade700, ), ), Text( 'Expected Bid', style: TextStyle( fontSize: 12.sp, color: Colors.blue.shade600, ), ), ], ) else Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( 'TBD', style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w500, color: Colors.grey.shade500, ), ), Text( 'Not Available', style: TextStyle( fontSize: 11.sp, color: Colors.grey.shade500, ), ), ], ), ], ), SizedBox(height: 12.h), // Draw Date Info Container( width: double.infinity, padding: EdgeInsets.all(12.w), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8.r), border: Border.all(color: Colors.blue.shade100), ), child: Row( children: [ Icon(Icons.event, color: Colors.blue.shade600, size: 20.w), SizedBox(width: 8.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Draw Date', style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w600, color: Colors.blue.shade700, ), ), Text( '${group.drawDate}th of $nextMonthName', style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.bold, color: Colors.blue.shade800, ), ), ], ), ), if (nextPrizePool != null) Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( _formatIndianCurrency(nextPrizePool), style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.bold, color: Colors.blue.shade700, ), ), Text( 'Prize Pool', style: TextStyle( fontSize: 12.sp, color: Colors.blue.shade600, ), ), ], ) else Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( 'TBD', style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w500, color: Colors.grey.shade500, ), ), Text( 'Not Available', style: TextStyle( fontSize: 11.sp, color: Colors.grey.shade500, ), ), ], ), ], ), ), ], ), ), ], ); } }