chitfund/luckychit/lib/interfaces/manager/import_existing_group_dialo...

605 lines
25 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/utils/snackbar_util.dart';
import 'package:intl/intl.dart';
class ImportExistingGroupDialog extends StatefulWidget {
const ImportExistingGroupDialog({super.key});
@override
State<ImportExistingGroupDialog> createState() => _ImportExistingGroupDialogState();
}
class _ImportExistingGroupDialogState extends State<ImportExistingGroupDialog> {
final _formKey = GlobalKey<FormState>();
final _nameController = TextEditingController();
final _totalValueController = TextEditingController();
final _monthlyInstallmentController = TextEditingController();
final _durationMonthsController = TextEditingController();
final _maxMembersController = TextEditingController();
final _foremanCommissionController = TextEditingController();
final _drawDateController = TextEditingController();
final _currentMonthController = TextEditingController();
DateTime? _selectedStartDate;
bool _isLoading = false;
@override
void initState() {
super.initState();
_drawDateController.text = '15';
_foremanCommissionController.text = '250';
_currentMonthController.text = '1';
// Default to 6 months ago
_selectedStartDate = DateTime.now().subtract(const Duration(days: 180));
}
@override
void dispose() {
_nameController.dispose();
_totalValueController.dispose();
_monthlyInstallmentController.dispose();
_durationMonthsController.dispose();
_maxMembersController.dispose();
_foremanCommissionController.dispose();
_drawDateController.dispose();
_currentMonthController.dispose();
super.dispose();
}
Future<void> _selectStartDate() async {
final DateTime? picked = await showDatePicker(
context: context,
initialDate: _selectedStartDate ?? DateTime.now().subtract(const Duration(days: 180)),
firstDate: DateTime(2020),
lastDate: DateTime.now(),
helpText: 'Select when the group started',
);
if (picked != null) {
setState(() {
_selectedStartDate = picked;
_calculateCurrentMonth();
});
}
}
void _calculateCurrentMonth() {
if (_selectedStartDate != null) {
final now = DateTime.now();
final diff = now.difference(_selectedStartDate!);
final monthsPassed = (diff.inDays / 30).floor() + 1;
_currentMonthController.text = monthsPassed.toString();
}
}
void _calculateMonthlyInstallment() {
final totalValue = double.tryParse(_totalValueController.text);
final durationMonths = int.tryParse(_durationMonthsController.text);
final commission = double.tryParse(_foremanCommissionController.text) ?? 250.0;
if (totalValue != null && durationMonths != null) {
final subscriptionPerMonth = totalValue / durationMonths;
final monthlyInstallment = subscriptionPerMonth + commission;
_monthlyInstallmentController.text = monthlyInstallment.toStringAsFixed(0);
}
}
Future<void> _handleImport() async {
if (_formKey.currentState!.validate()) {
if (_selectedStartDate == null) {
SnackbarUtil.showError('Please select a start date');
return;
}
setState(() => _isLoading = true);
try {
final data = {
'name': _nameController.text.trim(),
'total_value': double.parse(_totalValueController.text),
'monthly_installment': double.parse(_monthlyInstallmentController.text),
'duration_months': int.parse(_durationMonthsController.text),
'max_members': int.parse(_maxMembersController.text),
'foreman_commission_amount': double.parse(_foremanCommissionController.text),
'foreman_commission_type': 'fixed',
'draw_date': int.parse(_drawDateController.text),
'start_date': _selectedStartDate!.toIso8601String(),
'current_month': int.parse(_currentMonthController.text),
'is_import': true, // Flag to indicate this is an existing group
'status': 'active', // Start as active directly
};
final success = await ChitGroupService.to.createChitGroup(data);
if (success) {
SnackbarUtil.showSuccess(
'Existing group imported successfully! You can now add members and past draw results.',
title: 'Import Successful',
);
Get.back(result: true);
} else {
SnackbarUtil.showError('Failed to import group. Please try again.');
}
} catch (e) {
SnackbarUtil.showError('Error: ${e.toString()}');
} finally {
setState(() => _isLoading = false);
}
}
}
@override
Widget build(BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.r)),
child: Container(
width: 600.w,
constraints: BoxConstraints(maxHeight: 700.h),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Header
Container(
padding: EdgeInsets.all(20.w),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue.shade600, Colors.blue.shade700],
),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.r),
topRight: Radius.circular(20.r),
),
),
child: Row(
children: [
Icon(Icons.upload, color: Colors.white, size: 28.w),
SizedBox(width: 12.w),
Expanded(
child: Text(
'Import Existing Chit Group',
style: TextStyle(
fontSize: 20.sp,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
IconButton(
icon: Icon(Icons.close, color: Colors.white, size: 24.w),
onPressed: () => Get.back(),
),
],
),
),
// Form
Expanded(
child: SingleChildScrollView(
padding: EdgeInsets.all(24.w),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Info Box
Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(12.r),
border: Border.all(color: Colors.blue.shade200),
),
child: Row(
children: [
Icon(Icons.info_outline, color: Colors.blue.shade700, size: 20.w),
SizedBox(width: 12.w),
Expanded(
child: Text(
'Import a chit group that has already started. You can add past draw results and payments later.',
style: TextStyle(
fontSize: 14.sp,
color: Colors.blue.shade900,
),
),
),
],
),
),
SizedBox(height: 24.h),
// Group Name
Text(
'Group Name',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
),
SizedBox(height: 8.h),
TextFormField(
controller: _nameController,
decoration: InputDecoration(
hintText: 'e.g., January 2024 Batch',
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12.r)),
filled: true,
fillColor: Colors.grey.shade50,
),
validator: (value) => value?.isEmpty ?? true ? 'Required' : null,
),
SizedBox(height: 20.h),
// Start Date
Text(
'When Did This Group Start?',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
),
SizedBox(height: 8.h),
InkWell(
onTap: _selectStartDate,
child: Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.shade300),
borderRadius: BorderRadius.circular(12.r),
color: Colors.grey.shade50,
),
child: Row(
children: [
Icon(Icons.calendar_today, color: Colors.blue.shade600),
SizedBox(width: 12.w),
Text(
_selectedStartDate != null
? DateFormat('dd/MM/yyyy').format(_selectedStartDate!)
: 'Select start date',
style: TextStyle(fontSize: 16.sp),
),
],
),
),
),
SizedBox(height: 20.h),
// Current Month
Text(
'Current Month Number',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
),
SizedBox(height: 8.h),
TextFormField(
controller: _currentMonthController,
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: 'e.g., 6 (if 6 months have passed)',
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12.r)),
filled: true,
fillColor: Colors.grey.shade50,
suffixText: 'months elapsed',
),
validator: (value) {
if (value?.isEmpty ?? true) return 'Required';
final month = int.tryParse(value!);
if (month == null || month < 1) return 'Must be at least 1';
return null;
},
),
SizedBox(height: 20.h),
// Financial Details Header
Divider(),
SizedBox(height: 12.h),
Text(
'Chitfund Details',
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.bold,
color: Colors.grey.shade800,
),
),
SizedBox(height: 16.h),
// Total Value
Text('Total Value', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600)),
SizedBox(height: 8.h),
TextFormField(
controller: _totalValueController,
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: 'e.g., 100000',
prefixText: '',
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12.r)),
filled: true,
fillColor: Colors.grey.shade50,
),
onChanged: (_) => _calculateMonthlyInstallment(),
validator: (value) {
if (value?.isEmpty ?? true) return 'Required';
if (double.tryParse(value!) == null) return 'Invalid number';
return null;
},
),
SizedBox(height: 16.h),
// Duration
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Duration (Months)', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600)),
SizedBox(height: 8.h),
TextFormField(
controller: _durationMonthsController,
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: 'e.g., 20',
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12.r)),
filled: true,
fillColor: Colors.grey.shade50,
),
onChanged: (_) => _calculateMonthlyInstallment(),
validator: (value) {
if (value?.isEmpty ?? true) return 'Required';
if (int.tryParse(value!) == null) return 'Invalid';
return null;
},
),
],
),
),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Max Members', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600)),
SizedBox(height: 8.h),
TextFormField(
controller: _maxMembersController,
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: 'e.g., 20',
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12.r)),
filled: true,
fillColor: Colors.grey.shade50,
),
validator: (value) {
if (value?.isEmpty ?? true) return 'Required';
if (int.tryParse(value!) == null) return 'Invalid';
return null;
},
),
],
),
),
],
),
SizedBox(height: 16.h),
// Monthly Installment & Commission
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Monthly Installment', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600)),
SizedBox(height: 8.h),
TextFormField(
controller: _monthlyInstallmentController,
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: 'e.g., 5250',
prefixText: '',
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12.r)),
filled: true,
fillColor: Colors.grey.shade50,
),
validator: (value) {
if (value?.isEmpty ?? true) return 'Required';
if (double.tryParse(value!) == null) return 'Invalid';
return null;
},
),
],
),
),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Commission', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600)),
SizedBox(height: 8.h),
TextFormField(
controller: _foremanCommissionController,
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: 'e.g., 250',
prefixText: '',
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12.r)),
filled: true,
fillColor: Colors.grey.shade50,
),
onChanged: (_) => _calculateMonthlyInstallment(),
validator: (value) {
if (value?.isEmpty ?? true) return 'Required';
if (double.tryParse(value!) == null) return 'Invalid';
return null;
},
),
],
),
),
],
),
SizedBox(height: 16.h),
// Draw Date
Text('Draw Date (Day of Month)', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600)),
SizedBox(height: 8.h),
TextFormField(
controller: _drawDateController,
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: 'e.g., 15 (15th of every month)',
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12.r)),
filled: true,
fillColor: Colors.grey.shade50,
),
validator: (value) {
if (value?.isEmpty ?? true) return 'Required';
final date = int.tryParse(value!);
if (date == null || date < 1 || date > 31) return '1-31 only';
return null;
},
),
SizedBox(height: 24.h),
// Summary Box
Container(
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.info_outline, color: Colors.green.shade700, size: 20.w),
SizedBox(width: 8.w),
Text(
'Next Steps After Import',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
color: Colors.green.shade800,
),
),
],
),
SizedBox(height: 12.h),
_buildNextStep('1', 'Add all members to the group'),
_buildNextStep('2', 'Record past draw results (if any)'),
_buildNextStep('3', 'Record past payments for each member'),
_buildNextStep('4', 'Continue with regular monthly operations'),
],
),
),
],
),
),
),
),
// Actions
Container(
padding: EdgeInsets.all(20.w),
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(20.r),
bottomRight: Radius.circular(20.r),
),
),
child: Row(
children: [
Expanded(
child: OutlinedButton(
onPressed: _isLoading ? null : () => Get.back(),
style: OutlinedButton.styleFrom(
padding: EdgeInsets.symmetric(vertical: 16.h),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.r),
),
),
child: Text(
'Cancel',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
),
),
),
SizedBox(width: 16.w),
Expanded(
flex: 2,
child: ElevatedButton(
onPressed: _isLoading ? null : _handleImport,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue.shade600,
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 16.h),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.r),
),
elevation: 2,
),
child: _isLoading
? SizedBox(
height: 20.h,
width: 20.w,
child: const CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: Text(
'Import Group',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
),
),
),
],
),
),
],
),
),
);
}
Widget _buildNextStep(String number, String text) {
return Padding(
padding: EdgeInsets.only(bottom: 8.h),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 24.w,
height: 24.w,
decoration: BoxDecoration(
color: Colors.green.shade600,
shape: BoxShape.circle,
),
child: Center(
child: Text(
number,
style: TextStyle(
color: Colors.white,
fontSize: 12.sp,
fontWeight: FontWeight.bold,
),
),
),
),
SizedBox(width: 12.w),
Expanded(
child: Padding(
padding: EdgeInsets.only(top: 2.h),
child: Text(
text,
style: TextStyle(
fontSize: 14.sp,
color: Colors.green.shade900,
),
),
),
),
],
),
);
}
}