import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:kmobile/api/services/deposit_service.dart'; import 'package:kmobile/data/models/user.dart'; import 'package:kmobile/di/injection.dart'; import 'package:kmobile/features/fund_transfer/screens/transaction_pin_screen.dart'; import 'package:kmobile/l10n/app_localizations.dart'; class CreateDepositScreen extends StatefulWidget { final User selectedAccount; final Map? initialData; const CreateDepositScreen({ super.key, required this.selectedAccount, this.initialData, }); @override State createState() => _CreateDepositScreenState(); } class _CreateDepositScreenState extends State { final _formKey = GlobalKey(); bool _isLoading = false; // Controllers late final _fromAcctNoController = TextEditingController(text: widget.selectedAccount.accountNo.toString()); late final _cifNoController = TextEditingController( text: widget.initialData?['cifNo']?.toString()); late final _idNoController = TextEditingController( text: widget.initialData?['idno']?.toString()); late final _customerNameController = TextEditingController( text: widget.initialData?['customername']?.toString()); late final _nationalityController = TextEditingController( text: widget.initialData?['nationality']?.toString()); late final _addressLine1Controller = TextEditingController( text: widget.initialData?['addressline1']?.toString()); late final _addressLine2Controller = TextEditingController( text: widget.initialData?['addressline2']?.toString()); late final _pincodeController = TextEditingController( text: widget.initialData?['pincode']?.toString()); late final _productController = TextEditingController(); late final _typeController = TextEditingController(); late final _customerCategoryController = TextEditingController(); late final _termLocationController = TextEditingController(); late final _currencyController = TextEditingController(); late final _acctSgmtCodeController = TextEditingController(); late final _interestPaymentMethodController = TextEditingController(); late final _taxFileNumberIndicatorController = TextEditingController(); late final _termLengthController = TextEditingController(); late final _termBasisController = TextEditingController(); late final _termValueDepositedController = TextEditingController(); late final _interestFrequencyController = TextEditingController(); late final _termDaysController = TextEditingController(); late final _termMonthsController = TextEditingController(); late final _termYearsController = TextEditingController(); late final _nominationRequiredController = TextEditingController(); late final _printNomineeNameController = TextEditingController(); // RD specific controllers late final _rdInstallmentValueController = TextEditingController(); late final _monthlyRDInstallmentDueDayController = TextEditingController(); late final _rdInstlFreqController = TextEditingController(); Map get _productOptions { final l10n = AppLocalizations.of(context); return { '20': l10n.termDepositOption, '25': l10n.fixedDepositOption, '28': l10n.recurringDepositOption, }; } Map get _typeOptions { final l10n = AppLocalizations.of(context); return { '01': l10n.memberOption, '02': l10n.nonMemberOption, '03': l10n.staffOption, '04': l10n.govtOption, '05': l10n.schoolOption, '06': l10n.panchayatOption, '07': l10n.trustsOption, '08': l10n.municipalCouncilOption, '09': l10n.nroOption, '10': l10n.bankOption, '11': l10n.noFrillOption, '12': l10n.specialSchemesOption, '13': l10n.minorOption, }; } Map get _customerCategoryOptions { final l10n = AppLocalizations.of(context); return { '1': l10n.publicIndividualOption, '2': l10n.societiesOption, '3': l10n.seniorCitizenOption, '5': l10n.governmentOption, '6': l10n.localBodiesOption, '7': l10n.otherOption, }; } Map get _termLocationOptions { final l10n = AppLocalizations.of(context); return { '10': l10n.sbCaOption, '13': l10n.days46_90Option, '14': l10n.days91_180Option, '15': l10n.days180_1YearOption, '16': l10n.year1_18MonthsOption, '20': l10n.months18_2YearsOption, '17': l10n.years2_3Option, '18': l10n.years3_10Option, '22': l10n.above10YearsOption, '24': l10n.devKanyaYojnaOption, '25': l10n.hazarDainLakhOption, }; } Map get _currencyOptions { final l10n = AppLocalizations.of(context); return { '1': l10n.currencyINR, }; } Map get _acctSgmtCodeOptions { final l10n = AppLocalizations.of(context); return { '0706': l10n.publicIndividualOption, '5002': l10n.societiesOption, '5005': l10n.governmentOption, '5050': l10n.glOthersOption, }; } Map get _interestPaymentMethodOptions { final l10n = AppLocalizations.of(context); return { 'R': l10n.reInvestOption, }; } Map get _termBasisOptions { final l10n = AppLocalizations.of(context); return { 'D': l10n.daysOption, 'M': l10n.monthsOption, }; } Map get _interestFrequencyOptions { final l10n = AppLocalizations.of(context); return { 'M': l10n.onMaturityOption, '1M': l10n.monthlyOption, '3M': l10n.quarterlyOption, '1A': l10n.anniversaryMonthlyOption, '3A': l10n.anniversaryQuarterlyOption, }; } Map get _rdInstlFreqOptions { final l10n = AppLocalizations.of(context); return { 'M': l10n.monthlyOption, }; } @override void dispose() { _fromAcctNoController.dispose(); _cifNoController.dispose(); _idNoController.dispose(); _customerNameController.dispose(); _nationalityController.dispose(); _addressLine1Controller.dispose(); _addressLine2Controller.dispose(); _pincodeController.dispose(); _productController.dispose(); _typeController.dispose(); _customerCategoryController.dispose(); _termLocationController.dispose(); _currencyController.dispose(); _acctSgmtCodeController.dispose(); _interestPaymentMethodController.dispose(); _taxFileNumberIndicatorController.dispose(); _termLengthController.dispose(); _termBasisController.dispose(); _termValueDepositedController.dispose(); _interestFrequencyController.dispose(); _termDaysController.dispose(); _termMonthsController.dispose(); _termYearsController.dispose(); _nominationRequiredController.dispose(); _printNomineeNameController.dispose(); _rdInstallmentValueController.dispose(); _monthlyRDInstallmentDueDayController.dispose(); _rdInstlFreqController.dispose(); super.dispose(); } void _handleCreate() { if (_formKey.currentState!.validate()) { Navigator.push( context, MaterialPageRoute( builder: (context) => TransactionPinScreen( onPinCompleted: (ctx, pin) async { Navigator.pop(context); _executeCreation(pin); }, ), ), ); } } void _executeCreation(String pin) async { setState(() { _isLoading = true; }); try { final product = _productController.text; dynamic response; if (product == '20' || product == '25') { response = await getIt().createFdTd( fromacctno: _fromAcctNoController.text, cifNo: _cifNoController.text, idno: _idNoController.text, customername: _customerNameController.text, nationality: _nationalityController.text, addressline1: _addressLine1Controller.text, addressline2: _addressLine2Controller.text, pincode: _pincodeController.text, product: _productController.text, type: _typeController.text, customercategory: _customerCategoryController.text, termlocation: _termLocationController.text, currency: _currencyController.text, acctsgmtcode: _acctSgmtCodeController.text, interestpaymentmethod: _interestPaymentMethodController.text, taxfilenumberindicator: _taxFileNumberIndicatorController.text, termlenght: _termLengthController.text, termbasis: _termBasisController.text, termvaluedeposited: _termValueDepositedController.text, interestfrequency: _interestFrequencyController.text, termdays: _termDaysController.text, termmonths: _termMonthsController.text, termyears: _termYearsController.text, nominationRequired: _nominationRequiredController.text, printNomineename: _printNomineeNameController.text, tpin: pin, ); } else if (product == '28') { response = await getIt().createRd( fromacctno: _fromAcctNoController.text, cifNo: _cifNoController.text, idno: _idNoController.text, customername: _customerNameController.text, nationality: _nationalityController.text, addressline1: _addressLine1Controller.text, addressline2: _addressLine2Controller.text, pincode: _pincodeController.text, product: _productController.text, type: _typeController.text, customercategory: _customerCategoryController.text, termlocation: _termLocationController.text, currency: _currencyController.text, acctsgmtcode: _acctSgmtCodeController.text, interestpaymentmethod: _interestPaymentMethodController.text, taxfilenumberindicator: _taxFileNumberIndicatorController.text, termlenght: _termLengthController.text, termbasis: _termBasisController.text, termvaluedeposited: _termValueDepositedController.text, interestfrequency: _interestFrequencyController.text, termdays: _termDaysController.text, termmonths: _termMonthsController.text, termyears: _termYearsController.text, nominationRequired: _nominationRequiredController.text, printNomineename: _printNomineeNameController.text, rdinstallmentvalue: _rdInstallmentValueController.text, monthlyRDInstallmentdueday: _monthlyRDInstallmentDueDayController.text, rdInstlFreq: _rdInstlFreqController.text, tpin: pin, ); } if (response != null && response is Map) { if (mounted) { final l10n = AppLocalizations.of(context); if (response['error'] == 'INCORRECT_TPIN' || response['message'] == 'INCORRECT_TPIN') { _showSimpleDialog(l10n.invalidTpin, l10n.incorrectTpinMessage); } else if (response['status'] == 'FAILED') { _showFailureDialog(response['message']?.toString()); } else if (response.containsKey('accountno')) { _showResponseDialog(response); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(l10n.unexpectedResponse)), ); } } } else { if (mounted) { final l10n = AppLocalizations.of(context); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(l10n.failedToCreateDeposit)), ); } } } on DioException catch (e) { if (mounted) { final l10n = AppLocalizations.of(context); try { final errorBody = e.response?.data; if (errorBody is Map && (errorBody['error'] == 'INCORRECT_TPIN' || errorBody['message'] == 'INCORRECT_TPIN')) { _showSimpleDialog(l10n.invalidTpin, l10n.incorrectTpinMessage); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("${l10n.error}: ${e.message}")), ); } } catch (_) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(l10n.internalServerError)), ); } } } catch (e) { if (mounted) { final l10n = AppLocalizations.of(context); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("${l10n.error}: $e")), ); } } finally { if (mounted) { setState(() { _isLoading = false; }); } } } Future _showSimpleDialog(String title, String message) async { final l10n = AppLocalizations.of(context); return showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return AlertDialog( title: Text(title), content: SingleChildScrollView( child: ListBody( children: [ Text(message), ], ), ), actions: [ TextButton( child: Text(l10n.ok), onPressed: () { Navigator.of(context).pop(); }, ), ], ); }, ); } void _showFailureDialog(String? apiMessage) { final l10n = AppLocalizations.of(context); showDialog( context: context, builder: (context) => AlertDialog( title: Row( children: [ Icon(Icons.error_outline, color: Theme.of(context).colorScheme.error), const SizedBox(width: 8), Text(l10n.creationFailed), ], ), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( l10n.accountCreationFailed, style: const TextStyle(fontWeight: FontWeight.bold), ), const SizedBox(height: 12), if (apiMessage != null) ...[ Text(l10n.reason(apiMessage)), const SizedBox(height: 12), ], Text( l10n.creationFailedDescription, ), ], ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: Text(l10n.ok), ), ], ), ); } void _showResponseDialog(Map response) { final l10n = AppLocalizations.of(context); final accountNo = response['accountno']?.toString() ?? 'N/A'; final accountTypeRaw = response['accounttype']?.toString() ?? ''; final accountTypeText = _productOptions[accountTypeRaw] ?? accountTypeRaw; final amount = response['amount']?.toString() ?? '0.0'; showDialog( context: context, barrierDismissible: false, builder: (context) => AlertDialog( title: Text(l10n.depositCreatedSuccessfully), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("${l10n.accountNumberLabel}: $accountNo"), const SizedBox(height: 8), Text("${l10n.accountTypeLabel}: $accountTypeText"), const SizedBox(height: 8), Text(l10n.amountValue(amount)), ], ), actions: [ TextButton( onPressed: () { Navigator.of(context).popUntil((route) => route.isFirst); }, child: Text(l10n.ok), ), ], ), ); } @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); return Scaffold( appBar: AppBar( title: Text(l10n.createDeposit), ), body: SingleChildScrollView( padding: const EdgeInsets.all(16.0), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Tile 1: Customer Info Card( elevation: 2, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(l10n.customerInformation, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 16), _buildTextField(_fromAcctNoController, l10n.fromAccountNo, mandatory: true, readOnly: true), _buildTextField(_cifNoController, l10n.cifNo), _buildTextField(_idNoController, l10n.idNo), _buildTextField(_customerNameController, l10n.customerName), _buildTextField(_nationalityController, l10n.nationality), _buildTextField(_addressLine1Controller, l10n.addressLine1), _buildTextField(_addressLine2Controller, l10n.addressLine2), _buildTextField(_pincodeController, l10n.pincodeLabel, keyboardType: TextInputType.number), ], ), ), ), const SizedBox(height: 16), // Tile 2: Deposit Details Card( elevation: 2, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(l10n.depositDetails, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 16), _buildDropdownField( _productController, l10n.product, _productOptions, mandatory: true, onChanged: (val) { setState(() { _productController.text = val ?? ''; }); }), _buildDropdownField(_typeController, l10n.type, _typeOptions, mandatory: true), _buildDropdownField(_customerCategoryController, l10n.customerCategory, _customerCategoryOptions, mandatory: true), _buildDropdownField(_termLocationController, l10n.termLocation, _termLocationOptions, mandatory: true), _buildDropdownField( _currencyController, l10n.currency, _currencyOptions, mandatory: true), _buildDropdownField(_acctSgmtCodeController, l10n.accountSegmentCode, _acctSgmtCodeOptions, mandatory: true), ], ), ), ), const SizedBox(height: 16), // Tile 3: Interest & Term Settings Card( elevation: 2, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(l10n.interestTermSettings, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 16), _buildDropdownField(_interestPaymentMethodController, l10n.interestPaymentMethod, _interestPaymentMethodOptions, mandatory: true), _buildTextField(_taxFileNumberIndicatorController, l10n.taxFileNumberIndicator), _buildTextField(_termLengthController, l10n.termLength), _buildDropdownField( _termBasisController, l10n.termBasis, _termBasisOptions, mandatory: true), _buildTextField(_termValueDepositedController, l10n.termValueDeposited, keyboardType: TextInputType.number, mandatory: _productController.text != '28'), _buildDropdownField(_interestFrequencyController, l10n.interestFrequency, _interestFrequencyOptions), _buildTextField(_termDaysController, l10n.termDays, keyboardType: TextInputType.number), _buildTextField(_termMonthsController, l10n.termMonths, keyboardType: TextInputType.number), _buildTextField(_termYearsController, l10n.termYears, keyboardType: TextInputType.number, mandatory: true), _buildTextField( _nominationRequiredController, l10n.nominationRequired), _buildTextField( _printNomineeNameController, l10n.printNomineeName), ], ), ), ), const SizedBox(height: 16), // Tile 4: RD Specific Fields (Conditional) if (_productController.text == '28') Card( elevation: 2, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(l10n.recurringDepositSettings, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 16), _buildTextField(_rdInstallmentValueController, l10n.rdInstallmentValue, keyboardType: TextInputType.number), _buildTextField(_monthlyRDInstallmentDueDayController, l10n.monthlyRDInstallmentDueDay, keyboardType: TextInputType.number), _buildDropdownField(_rdInstlFreqController, l10n.rdInstallmentFrequency, _rdInstlFreqOptions), ], ), ), ), const SizedBox(height: 24), ElevatedButton( onPressed: _isLoading ? null : _handleCreate, style: ElevatedButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.primaryContainer, foregroundColor: Theme.of(context).colorScheme.onPrimaryContainer, padding: const EdgeInsets.symmetric(vertical: 16), elevation: 4, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), child: _isLoading ? const SizedBox( height: 20, width: 20, child: CircularProgressIndicator( strokeWidth: 2, ), ) : Text( l10n.proceedButton, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), ), const SizedBox(height: 32), ], ), ), ), ); } Widget _buildDropdownField(TextEditingController controller, String label, Map options, {bool readOnly = false, bool mandatory = false, void Function(String?)? onChanged}) { final l10n = AppLocalizations.of(context); String? currentValue = options.containsKey(controller.text) ? controller.text : null; return Padding( padding: const EdgeInsets.only(bottom: 16.0), child: DropdownButtonFormField( value: currentValue, onChanged: readOnly || _isLoading ? null : (newValue) { if (newValue != null) { setState(() { controller.text = newValue; }); } if (onChanged != null) { onChanged(newValue); } }, decoration: InputDecoration( labelText: mandatory ? '$label *' : label, border: const OutlineInputBorder(), contentPadding: const EdgeInsets.symmetric(vertical: 16, horizontal: 12), ), validator: (value) { if (mandatory && (value == null || value.isEmpty)) { return l10n.fieldRequired; } return null; }, items: options.entries.map((entry) { return DropdownMenuItem( value: entry.key, child: Text(entry.value), ); }).toList(), ), ); } Widget _buildTextField(TextEditingController controller, String label, {TextInputType keyboardType = TextInputType.text, bool readOnly = false, bool mandatory = false, VoidCallback? onTap}) { final l10n = AppLocalizations.of(context); return Padding( padding: const EdgeInsets.only(bottom: 16.0), child: TextFormField( controller: controller, readOnly: readOnly || onTap != null || _isLoading, onTap: _isLoading ? null : onTap, decoration: InputDecoration( labelText: mandatory ? '$label *' : label, border: const OutlineInputBorder(), suffixIcon: onTap != null ? const Icon(Icons.calendar_today) : null, contentPadding: const EdgeInsets.symmetric(vertical: 16, horizontal: 12), ), keyboardType: keyboardType, validator: (value) { if (mandatory && (value == null || value.isEmpty)) { return l10n.fieldRequired; } return null; }, ), ); } }