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

394 lines
15 KiB
Dart

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../core/services/api_service.dart';
import '../../core/models/user.dart';
class AddUserDialog extends StatefulWidget {
const AddUserDialog({super.key});
@override
State<AddUserDialog> createState() => _AddUserDialogState();
}
class _AddUserDialogState extends State<AddUserDialog> {
final _formKey = GlobalKey<FormState>();
final _mobileNumberController = TextEditingController();
final _fullNameController = TextEditingController();
final _emailController = TextEditingController();
final _addressController = TextEditingController();
final _emergencyContactController = TextEditingController();
final ApiService _apiService = ApiService();
bool _isLoading = false;
bool _isCheckingMobile = false;
@override
void dispose() {
_mobileNumberController.dispose();
_fullNameController.dispose();
_emailController.dispose();
_addressController.dispose();
_emergencyContactController.dispose();
super.dispose();
}
Future<bool> _checkMobileNumberExists(String mobileNumber) async {
if (mobileNumber.length != 10) return false;
setState(() => _isCheckingMobile = true);
try {
final response = await _apiService.getAllUsers();
if (response['success'] == true && response['data'] != null) {
final usersData = response['data']['users'] as List;
final users = usersData.map((userData) => User.fromJson(userData)).toList();
final exists = users.any((user) => user.mobileNumber == mobileNumber);
if (exists) {
Get.snackbar(
'Mobile Number Exists',
'A user with this mobile number already exists!',
backgroundColor: Colors.orange.shade100,
colorText: Colors.orange.shade800,
snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 2),
);
}
return exists;
}
return false;
} catch (e) {
print('Error checking mobile number: $e');
return false; // Allow creation if check fails
} finally {
setState(() => _isCheckingMobile = false);
}
}
Future<void> _addUser() async {
if (!_formKey.currentState!.validate()) return;
setState(() => _isLoading = true);
try {
// Check if mobile number already exists
final mobileExists = await _checkMobileNumberExists(_mobileNumberController.text.trim());
if (mobileExists) {
Get.snackbar(
'Error',
'A user with this mobile number already exists!',
backgroundColor: Colors.orange.shade100,
colorText: Colors.orange.shade800,
snackPosition: SnackPosition.TOP,
);
return;
}
// Call the API to create a new user
final response = await _apiService.createMember(
_mobileNumberController.text.trim(),
_fullNameController.text.trim(),
email: _emailController.text.trim().isNotEmpty ? _emailController.text.trim() : null,
address: _addressController.text.trim().isNotEmpty ? _addressController.text.trim() : null,
emergencyContact: _emergencyContactController.text.trim().isNotEmpty ? _emergencyContactController.text.trim() : null,
);
if (response['success'] == true) {
Navigator.of(context).pop(); // Close dialog
Get.snackbar(
'Success',
'User added successfully! You can now select them for chitfunds.',
backgroundColor: Colors.green.shade100,
colorText: Colors.green.shade800,
snackPosition: SnackPosition.TOP,
);
} else {
throw Exception(response['message'] ?? 'Failed to create user');
}
} catch (e) {
Get.snackbar(
'Error',
'Failed to add user. Please try again.',
backgroundColor: Colors.red.shade100,
colorText: Colors.red.shade800,
snackPosition: SnackPosition.TOP,
);
} finally {
setState(() => _isLoading = false);
}
}
@override
Widget build(BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16.r)),
child: Container(
width: 500.w,
constraints: BoxConstraints(maxHeight: 0.8.sh),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Header
Container(
padding: EdgeInsets.all(20.w),
decoration: BoxDecoration(
color: Colors.green.shade600,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16.r),
topRight: Radius.circular(16.r),
),
),
child: Row(
children: [
Icon(
Icons.person_add,
color: Colors.white,
size: 24.w,
),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Add New User',
style: TextStyle(
color: Colors.white,
fontSize: 18.sp,
fontWeight: FontWeight.w600,
),
),
Text(
'Create a new user account',
style: TextStyle(
color: Colors.white.withOpacity(0.8),
fontSize: 14.sp,
),
),
],
),
),
IconButton(
onPressed: () => Navigator.of(context).pop(),
icon: const Icon(Icons.close, color: Colors.white),
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
),
],
),
),
// Form Content
Flexible(
child: SingleChildScrollView(
padding: EdgeInsets.all(20.w),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// User Information Section
_buildSectionTitle('User Information'),
SizedBox(height: 16.h),
// Mobile Number
TextFormField(
controller: _mobileNumberController,
decoration: InputDecoration(
labelText: 'Mobile Number *',
hintText: 'Enter 10-digit mobile number',
prefixIcon: const Icon(Icons.phone),
suffixIcon: _isCheckingMobile
? SizedBox(
width: 20.w,
height: 20.w,
child: const CircularProgressIndicator(strokeWidth: 2),
)
: null,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.r),
),
),
keyboardType: TextInputType.phone,
maxLength: 10,
onChanged: (value) {
if (value.length == 10) {
_checkMobileNumberExists(value);
}
},
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'Mobile number is required';
}
if (value.length != 10) {
return 'Mobile number must be 10 digits';
}
if (!RegExp(r'^[0-9]+$').hasMatch(value)) {
return 'Mobile number must contain only digits';
}
return null;
},
),
SizedBox(height: 16.h),
// Full Name
TextFormField(
controller: _fullNameController,
decoration: InputDecoration(
labelText: 'Full Name *',
hintText: 'Enter full name',
prefixIcon: const Icon(Icons.person),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.r),
),
),
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'Full name is required';
}
if (value.trim().length < 2) {
return 'Name must be at least 2 characters';
}
return null;
},
),
SizedBox(height: 16.h),
// Email (Optional)
TextFormField(
controller: _emailController,
decoration: InputDecoration(
labelText: 'Email (Optional)',
hintText: 'Enter email address',
prefixIcon: const Icon(Icons.email),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.r),
),
),
keyboardType: TextInputType.emailAddress,
validator: (value) {
if (value != null && value.trim().isNotEmpty) {
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) {
return 'Please enter a valid email address';
}
}
return null;
},
),
SizedBox(height: 16.h),
// Address (Optional)
TextFormField(
controller: _addressController,
decoration: InputDecoration(
labelText: 'Address (Optional)',
hintText: 'Enter address',
prefixIcon: const Icon(Icons.location_on),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.r),
),
),
maxLines: 2,
),
SizedBox(height: 16.h),
// Emergency Contact (Optional)
TextFormField(
controller: _emergencyContactController,
decoration: InputDecoration(
labelText: 'Emergency Contact (Optional)',
hintText: 'Enter emergency contact number',
prefixIcon: const Icon(Icons.emergency),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.r),
),
),
keyboardType: TextInputType.phone,
maxLength: 10,
validator: (value) {
if (value != null && value.trim().isNotEmpty) {
if (value.length != 10) {
return 'Emergency contact must be 10 digits';
}
if (!RegExp(r'^[0-9]+$').hasMatch(value)) {
return 'Emergency contact must contain only digits';
}
}
return null;
},
),
SizedBox(height: 24.h),
// Action Buttons
Row(
children: [
Expanded(
child: OutlinedButton(
onPressed: () => Navigator.of(context).pop(),
style: OutlinedButton.styleFrom(
padding: EdgeInsets.symmetric(vertical: 16.h),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.r),
),
),
child: Text(
'Cancel',
style: TextStyle(fontSize: 16.sp),
),
),
),
SizedBox(width: 16.w),
Expanded(
child: ElevatedButton(
onPressed: _isLoading ? null : _addUser,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green.shade600,
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 16.h),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.r),
),
),
child: _isLoading
? SizedBox(
height: 20.h,
width: 20.w,
child: const CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: Text(
'Add User',
style: TextStyle(fontSize: 16.sp),
),
),
),
],
),
],
),
),
),
),
],
),
),
);
}
Widget _buildSectionTitle(String title) {
return Text(
title,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: Colors.grey.shade800,
),
);
}
}