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/models/group_member.dart'; import '../../core/models/chit_group.dart'; import 'add_user_dialog.dart'; class MembersPage extends StatefulWidget { const MembersPage({super.key}); @override State createState() => _MembersPageState(); } class _MembersPageState extends State { final _chitGroupService = Get.find(); final _searchController = TextEditingController(); String _searchQuery = ''; String _selectedFilter = 'all'; // all, active, inactive @override void initState() { super.initState(); _loadAllMembers(); } @override void dispose() { _searchController.dispose(); super.dispose(); } Future _loadAllMembers() async { // Load all groups to get all members await _chitGroupService.loadManagerChitGroups(); } List> _getAllMembers() { final allMembers = >[]; final groups = _chitGroupService.chitGroups; for (var group in groups) { if (group.members != null) { for (var member in group.members!) { // Check if member already in list (user can be in multiple groups) final existingIndex = allMembers.indexWhere( (m) => m['userId'] == member.userId, ); if (existingIndex == -1) { allMembers.add({ 'userId': member.userId, 'member': member, 'groups': [group], }); } else { // Add group to existing member's group list (allMembers[existingIndex]['groups'] as List).add(group); } } } } return allMembers; } List> _getFilteredMembers() { var members = _getAllMembers(); // Apply status filter if (_selectedFilter == 'active') { members = members.where((m) { final member = m['member'] as GroupMember; return member.status.toLowerCase() == 'active'; }).toList(); } else if (_selectedFilter == 'inactive') { members = members.where((m) { final member = m['member'] as GroupMember; return member.status.toLowerCase() != 'active'; }).toList(); } // Apply search filter if (_searchQuery.isNotEmpty) { members = members.where((m) { final member = m['member'] as GroupMember; final name = member.user?.fullName?.toLowerCase() ?? ''; final mobile = member.user?.mobileNumber ?? ''; final query = _searchQuery.toLowerCase(); return name.contains(query) || mobile.contains(query); }).toList(); } return members; } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.grey.shade50, appBar: AppBar( title: Text( 'All Members', style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.w600, ), ), backgroundColor: Colors.green.shade600, foregroundColor: Colors.white, elevation: 2, actions: [ IconButton( icon: Icon(Icons.person_add, size: 24.w), onPressed: _showAddUserDialog, tooltip: 'Add New Member', ), ], ), body: Obx(() { if (_chitGroupService.isLoading.value) { return Center( child: CircularProgressIndicator( color: Colors.green.shade600, ), ); } final filteredMembers = _getFilteredMembers(); return Column( children: [ // Search and Filter Bar Container( padding: EdgeInsets.all(16.w), color: Colors.white, child: Column( children: [ // Search Bar TextField( controller: _searchController, decoration: InputDecoration( hintText: 'Search by name or mobile...', prefixIcon: const Icon(Icons.search), suffixIcon: _searchQuery.isNotEmpty ? IconButton( icon: const Icon(Icons.clear), onPressed: () { setState(() { _searchController.clear(); _searchQuery = ''; }); }, ) : null, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12.r), borderSide: BorderSide(color: Colors.grey.shade300), ), filled: true, fillColor: Colors.grey.shade50, contentPadding: EdgeInsets.symmetric( horizontal: 16.w, vertical: 12.h, ), ), onChanged: (value) { setState(() { _searchQuery = value; }); }, ), SizedBox(height: 12.h), // Filter Chips Row( children: [ Text( 'Filter:', style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w600, color: Colors.grey.shade700, ), ), SizedBox(width: 12.w), _buildFilterChip('All', 'all'), SizedBox(width: 8.w), _buildFilterChip('Active', 'active'), SizedBox(width: 8.w), _buildFilterChip('Inactive', 'inactive'), ], ), ], ), ), // Member Count Container( padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.h), color: Colors.blue.shade50, child: Row( children: [ Icon(Icons.people, color: Colors.blue.shade700, size: 20.w), SizedBox(width: 8.w), Text( '${filteredMembers.length} member${filteredMembers.length != 1 ? 's' : ''}', style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, color: Colors.blue.shade800, ), ), ], ), ), // Members List Expanded( child: filteredMembers.isEmpty ? _buildEmptyState() : RefreshIndicator( onRefresh: _loadAllMembers, child: ListView.builder( padding: EdgeInsets.all(16.w), itemCount: filteredMembers.length, itemBuilder: (context, index) { final memberData = filteredMembers[index]; final member = memberData['member'] as GroupMember; final groups = memberData['groups'] as List; return _buildMemberCard(member, groups); }, ), ), ), ], ); }), ); } Widget _buildFilterChip(String label, String value) { final isSelected = _selectedFilter == value; return FilterChip( label: Text( label, style: TextStyle( fontSize: 14.sp, fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, ), ), selected: isSelected, onSelected: (selected) { setState(() { _selectedFilter = value; }); }, selectedColor: Colors.green.shade100, checkmarkColor: Colors.green.shade700, backgroundColor: Colors.grey.shade100, side: BorderSide( color: isSelected ? Colors.green.shade600 : Colors.grey.shade300, width: isSelected ? 2 : 1, ), ); } Widget _buildMemberCard(GroupMember member, List groups) { final user = member.user; final isActive = member.status.toLowerCase() == 'active'; return Card( margin: EdgeInsets.only(bottom: 12.h), elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16.r), ), child: InkWell( onTap: () => _showMemberDetails(member, groups), borderRadius: BorderRadius.circular(16.r), child: Padding( padding: EdgeInsets.all(16.w), child: Row( children: [ // Avatar CircleAvatar( radius: 28.r, backgroundColor: isActive ? Colors.green.shade600 : Colors.grey.shade400, child: Text( user?.fullName?.substring(0, 1).toUpperCase() ?? 'M', style: TextStyle( color: Colors.white, fontSize: 20.sp, fontWeight: FontWeight.bold, ), ), ), SizedBox(width: 16.w), // Member Info Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( user?.fullName ?? 'Unknown', style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, color: Colors.grey.shade800, ), ), SizedBox(height: 4.h), Text( user?.mobileNumber ?? '', style: TextStyle( fontSize: 14.sp, color: Colors.grey.shade600, ), ), SizedBox(height: 8.h), Wrap( spacing: 8.w, children: [ // Status Badge Container( padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), decoration: BoxDecoration( color: isActive ? Colors.green.shade100 : Colors.grey.shade200, borderRadius: BorderRadius.circular(8.r), border: Border.all( color: isActive ? Colors.green.shade600 : Colors.grey.shade400, ), ), child: Text( isActive ? 'Active' : 'Inactive', style: TextStyle( fontSize: 12.sp, fontWeight: FontWeight.w600, color: isActive ? Colors.green : Colors.grey.shade600, ), ), ), // Groups Count Container( padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), decoration: BoxDecoration( color: Colors.blue.shade50, borderRadius: BorderRadius.circular(8.r), border: Border.all(color: Colors.blue.shade200), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.group, size: 14.w, color: Colors.blue.shade700), SizedBox(width: 4.w), Text( '${groups.length} group${groups.length != 1 ? 's' : ''}', style: TextStyle( fontSize: 12.sp, fontWeight: FontWeight.w600, color: Colors.blue, ), ), ], ), ), ], ), ], ), ), // Arrow Icon( Icons.arrow_forward_ios, size: 16.w, color: Colors.grey.shade400, ), ], ), ), ), ); } Widget _buildEmptyState() { return Center( child: Padding( padding: EdgeInsets.all(32.w), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.people_outline, size: 80.w, color: Colors.grey.shade300, ), SizedBox(height: 24.h), Text( _searchQuery.isNotEmpty ? 'No members found' : 'No members yet', style: TextStyle( fontSize: 20.sp, fontWeight: FontWeight.bold, color: Colors.grey.shade700, ), ), SizedBox(height: 12.h), Text( _searchQuery.isNotEmpty ? 'Try a different search term' : 'Add members to your chit groups to see them here', textAlign: TextAlign.center, style: TextStyle( fontSize: 16.sp, color: Colors.grey.shade600, ), ), if (_searchQuery.isEmpty) ...[ SizedBox(height: 32.h), ElevatedButton.icon( onPressed: _showAddUserDialog, icon: Icon(Icons.person_add, size: 20.w), label: Text( 'Add Member', style: TextStyle(fontSize: 16.sp), ), style: ElevatedButton.styleFrom( backgroundColor: Colors.green.shade600, foregroundColor: Colors.white, padding: EdgeInsets.symmetric(horizontal: 24.w, vertical: 16.h), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.r), ), ), ), ], ], ), ), ); } void _showMemberDetails(GroupMember member, List groups) { showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (context) => Container( height: MediaQuery.of(context).size.height * 0.7, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.only( topLeft: Radius.circular(24.r), topRight: Radius.circular(24.r), ), ), child: Column( children: [ // Handle Container( margin: EdgeInsets.only(top: 12.h), width: 40.w, height: 4.h, decoration: BoxDecoration( color: Colors.grey.shade300, borderRadius: BorderRadius.circular(2.r), ), ), SizedBox(height: 20.h), // Header Padding( padding: EdgeInsets.symmetric(horizontal: 24.w), child: Row( children: [ CircleAvatar( radius: 32.r, backgroundColor: Colors.green.shade600, child: Text( member.user?.fullName?.substring(0, 1).toUpperCase() ?? 'M', style: TextStyle( color: Colors.white, fontSize: 24.sp, fontWeight: FontWeight.bold, ), ), ), SizedBox(width: 16.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( member.user?.fullName ?? 'Unknown', style: TextStyle( fontSize: 20.sp, fontWeight: FontWeight.bold, ), ), Text( member.user?.mobileNumber ?? '', style: TextStyle( fontSize: 16.sp, color: Colors.grey.shade600, ), ), ], ), ), IconButton( icon: const Icon(Icons.close), onPressed: () => Navigator.pop(context), ), ], ), ), SizedBox(height: 24.h), // Content Expanded( child: ListView( padding: EdgeInsets.symmetric(horizontal: 24.w), children: [ // Stats Row( children: [ Expanded( child: _buildStatCard( 'Total Paid', '₹${_formatCurrency(member.totalPaid)}', Icons.payment, Colors.green, ), ), SizedBox(width: 12.w), Expanded( child: _buildStatCard( 'Total Won', '₹${_formatCurrency(member.totalWon)}', Icons.emoji_events, Colors.amber, ), ), ], ), SizedBox(height: 24.h), // Groups Text( 'Member of Groups (${groups.length})', style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, ), ), SizedBox(height: 12.h), ...groups.map((group) => _buildGroupChip(group)), SizedBox(height: 24.h), // Additional Info _buildInfoRow('Joined', _formatDate(member.joinedDate)), SizedBox(height: 12.h), _buildInfoRow('Status', member.status), ], ), ), ], ), ), ); } Widget _buildStatCard(String label, String value, IconData icon, Color color) { return Container( padding: EdgeInsets.all(16.w), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(12.r), border: Border.all(color: color.withOpacity(0.3)), ), child: Column( children: [ Icon(icon, color: color, size: 24.w), SizedBox(height: 8.h), Text( value, style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.bold, color: color, ), ), SizedBox(height: 4.h), Text( label, style: TextStyle( fontSize: 12.sp, color: Colors.grey.shade600, ), ), ], ), ); } Widget _buildGroupChip(ChitGroup group) { return Container( margin: EdgeInsets.only(bottom: 8.h), padding: EdgeInsets.all(12.w), decoration: BoxDecoration( color: Colors.grey.shade50, borderRadius: BorderRadius.circular(12.r), border: Border.all(color: Colors.grey.shade200), ), child: Row( children: [ Icon( Icons.account_balance_wallet, color: Colors.green.shade600, size: 20.w, ), SizedBox(width: 12.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( group.name, style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w600, ), ), Text( '${group.status} • ₹${_formatCurrency(group.monthlyInstallment)}/month', style: TextStyle( fontSize: 12.sp, color: Colors.grey.shade600, ), ), ], ), ), ], ), ); } Widget _buildInfoRow(String label, String value) { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 100.w, child: Text( label, style: TextStyle( fontSize: 14.sp, color: Colors.grey.shade600, ), ), ), Expanded( child: Text( value, style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w600, color: Colors.grey.shade800, ), ), ), ], ); } void _showAddUserDialog() { showDialog( context: context, barrierDismissible: false, builder: (context) => const AddUserDialog(), ).then((result) { if (result == true) { // Reload members _loadAllMembers(); } }); } String _formatCurrency(double amount) { final amountStr = amount.toStringAsFixed(0); return amountStr.replaceAllMapped( RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'), (Match m) => '${m[1]},', ); } String _formatDate(DateTime date) { return '${date.day}/${date.month}/${date.year}'; } }