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

3220 lines
104 KiB
Dart

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<GroupDetailsPage> createState() => _GroupDetailsPageState();
}
class _GroupDetailsPageState extends State<GroupDetailsPage> 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<PaymentService>();
// 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<String>(
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<FinancialTableEntry> 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<Widget> 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<String, dynamic> 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<String>(
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<void> _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<void> _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<String, dynamic> 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<String, dynamic> 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);
// DEBUG: Check winner data
if (currentDraw != null) {
print('🎯 Draw found for month ${currentDraw.month}/${currentDraw.year}');
print('🎯 Winner ID: ${currentDraw.winnerId}');
print('🎯 Winner object: ${currentDraw.winner}');
print('🎯 Winner name: ${currentDraw.winner?.fullName}');
} else {
print('🎯 No draw found');
}
// 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,
),
),
SizedBox(height: 4.h),
Text(
currentDraw?.winner?.fullName ?? 'Not Conducted',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
color: currentDraw?.winner?.fullName != null ? Colors.green.shade900 : Colors.grey.shade500,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
),
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,
),
),
],
),
],
),
),
],
),
),
],
);
}
}