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

419 lines
15 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/models/chit_group.dart';
import '../../core/models/user.dart';
class SelectMemberDialog extends StatefulWidget {
final ChitGroup group;
const SelectMemberDialog({
super.key,
required this.group,
});
@override
State<SelectMemberDialog> createState() => _SelectMemberDialogState();
}
class _SelectMemberDialogState extends State<SelectMemberDialog> {
final ChitGroupService _service = ChitGroupService.to;
final TextEditingController _searchController = TextEditingController();
List<User> _users = [];
List<User> _filteredUsers = [];
bool _isLoading = false;
bool _isAdding = false;
String _searchQuery = '';
User? _selectedUser;
@override
void initState() {
super.initState();
_loadUsers();
}
@override
void dispose() {
_searchController.dispose();
super.dispose();
}
Future<void> _loadUsers() async {
setState(() => _isLoading = true);
try {
final users = await _service.getAllUsers(search: _searchQuery);
setState(() {
_users = users;
_filteredUsers = users;
});
} catch (e) {
Get.snackbar('Error', 'Failed to load users');
} finally {
setState(() => _isLoading = false);
}
}
void _filterUsers(String query) {
setState(() {
_searchQuery = query;
if (query.isEmpty) {
_filteredUsers = _users;
} else {
_filteredUsers = _users.where((user) {
return user.fullName.toLowerCase().contains(query.toLowerCase()) ||
user.mobileNumber.contains(query);
}).toList();
}
});
}
Future<void> _addSelectedUserToGroup() async {
if (_selectedUser == null) {
Get.snackbar(
'Error',
'Please select a user first',
backgroundColor: Colors.orange.shade100,
colorText: Colors.orange.shade800,
snackPosition: SnackPosition.TOP,
);
return;
}
setState(() => _isAdding = true);
try {
final success = await _service.addMemberToGroup(widget.group.id, {
'mobile_number': _selectedUser!.mobileNumber,
});
if (success) {
Get.back(); // Close dialog
Get.snackbar(
'Success',
'${_selectedUser!.fullName} added to chitfund successfully!',
backgroundColor: Colors.green.shade100,
colorText: Colors.green.shade800,
snackPosition: SnackPosition.TOP,
);
}
} catch (e) {
Get.snackbar(
'Error',
'Failed to add member. Please try again.',
backgroundColor: Colors.red.shade100,
colorText: Colors.red.shade800,
snackPosition: SnackPosition.TOP,
);
} finally {
setState(() => _isAdding = false);
}
}
@override
Widget build(BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16.r)),
child: Container(
width: 600.w,
height: 0.8.sh,
child: Column(
children: [
// Header
Container(
padding: EdgeInsets.all(20.w),
decoration: BoxDecoration(
color: Colors.blue.shade600,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16.r),
topRight: Radius.circular(16.r),
),
),
child: Row(
children: [
Icon(
Icons.people,
color: Colors.white,
size: 24.w,
),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Add Member to Chitfund',
style: TextStyle(
color: Colors.white,
fontSize: 18.sp,
fontWeight: FontWeight.w600,
),
),
Text(
'Chitfund: ${widget.group.name}',
style: TextStyle(
color: Colors.white.withOpacity(0.8),
fontSize: 14.sp,
),
),
],
),
),
IconButton(
onPressed: () => Get.back(),
icon: const Icon(Icons.close, color: Colors.white),
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
),
],
),
),
// User Selection
Expanded(
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Search Bar
TextField(
controller: _searchController,
decoration: InputDecoration(
hintText: 'Search by name or mobile number...',
prefixIcon: const Icon(Icons.search),
suffixIcon: _isLoading
? SizedBox(
width: 20.w,
height: 20.w,
child: const CircularProgressIndicator(strokeWidth: 2),
)
: IconButton(
icon: const Icon(Icons.refresh),
onPressed: _loadUsers,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.r),
),
),
onChanged: _filterUsers,
),
SizedBox(height: 16.h),
// User Dropdown
Text(
'Select User',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: Colors.grey.shade800,
),
),
SizedBox(height: 8.h),
DropdownButtonFormField<User>(
value: _selectedUser,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.r),
),
contentPadding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.h),
),
hint: Text(
_isLoading ? 'Loading users...' : 'Choose a user to add',
style: TextStyle(fontSize: 14.sp),
),
items: _filteredUsers.map((user) {
return DropdownMenuItem<User>(
value: user,
child: Row(
children: [
CircleAvatar(
radius: 12.r,
backgroundColor: Colors.blue.shade100,
child: Text(
user.fullName.isNotEmpty ? user.fullName[0].toUpperCase() : '?',
style: TextStyle(
color: Colors.blue.shade700,
fontWeight: FontWeight.w600,
fontSize: 12.sp,
),
),
),
SizedBox(width: 8.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
user.fullName,
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w600,
),
overflow: TextOverflow.ellipsis,
),
Text(
user.mobileNumber,
style: TextStyle(
fontSize: 12.sp,
color: Colors.grey.shade600,
),
),
],
),
),
],
),
);
}).toList(),
onChanged: (User? newValue) {
setState(() {
_selectedUser = newValue;
});
},
),
SizedBox(height: 16.h),
// Selected User Info
if (_selectedUser != null)
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: Row(
children: [
Icon(
Icons.person,
color: Colors.blue.shade600,
size: 20.w,
),
SizedBox(width: 8.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Selected: ${_selectedUser!.fullName}',
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w600,
color: Colors.blue.shade800,
),
),
Text(
'Mobile: ${_selectedUser!.mobileNumber}',
style: TextStyle(
fontSize: 12.sp,
color: Colors.blue.shade600,
),
),
],
),
),
],
),
),
// Empty State
if (_filteredUsers.isEmpty && !_isLoading)
Expanded(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.people_outline,
size: 64.w,
color: Colors.grey.shade400,
),
SizedBox(height: 16.h),
Text(
_searchQuery.isEmpty
? 'No users found'
: 'No users match your search',
style: TextStyle(
fontSize: 16.sp,
color: Colors.grey.shade600,
),
),
if (_searchQuery.isNotEmpty) ...[
SizedBox(height: 8.h),
TextButton(
onPressed: () {
_searchController.clear();
_filterUsers('');
},
child: const Text('Clear search'),
),
],
],
),
),
),
],
),
),
),
// Footer
Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.grey.shade50,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(16.r),
bottomRight: Radius.circular(16.r),
),
),
child: Row(
children: [
Expanded(
child: Text(
'${_filteredUsers.length} users found',
style: TextStyle(
fontSize: 14.sp,
color: Colors.grey.shade600,
),
),
),
SizedBox(width: 8.w),
OutlinedButton(
onPressed: () => Get.back(),
child: const Text('Cancel'),
),
SizedBox(width: 8.w),
ElevatedButton(
onPressed: _isAdding ? null : _addSelectedUserToGroup,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue.shade600,
foregroundColor: Colors.white,
),
child: _isAdding
? SizedBox(
width: 16.w,
height: 16.w,
child: const CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: const Text('Add Member'),
),
],
),
),
],
),
),
);
}
}