Edit Limit for beneficiaries and APY Register UI
This commit is contained in:
@@ -126,4 +126,26 @@ class BeneficiaryService {
|
|||||||
throw Exception('Unexpected error: ${e.toString()}');
|
throw Exception('Unexpected error: ${e.toString()}');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Response> updateLimit({
|
||||||
|
required String beneficiaryAccountNo,
|
||||||
|
required String newLimit,
|
||||||
|
}) async {
|
||||||
|
log('inside update limit of beneficiary service');
|
||||||
|
final response = await _dio.patch(
|
||||||
|
'/api/beneficiary/update-limit',
|
||||||
|
data: {
|
||||||
|
'beneficiaryAccountNo': beneficiaryAccountNo,
|
||||||
|
'newLimit': int.tryParse(newLimit),
|
||||||
|
},
|
||||||
|
options: Options(
|
||||||
|
sendTimeout: const Duration(seconds: 60),
|
||||||
|
receiveTimeout: const Duration(seconds: 60),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (response.statusCode != 200) {
|
||||||
|
throw Exception("INTERNAL SERVER ERROR");
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:kmobile/data/models/beneficiary.dart';
|
import 'package:kmobile/data/models/beneficiary.dart';
|
||||||
import 'package:kmobile/di/injection.dart';
|
import 'package:kmobile/di/injection.dart';
|
||||||
import 'package:kmobile/widgets/bank_logos.dart';
|
import 'package:kmobile/widgets/bank_logos.dart';
|
||||||
@@ -6,16 +7,39 @@ import 'package:kmobile/api/services/beneficiary_service.dart';
|
|||||||
|
|
||||||
import '../../../l10n/app_localizations.dart';
|
import '../../../l10n/app_localizations.dart';
|
||||||
|
|
||||||
class BeneficiaryDetailsScreen extends StatelessWidget {
|
class BeneficiaryDetailsScreen extends StatefulWidget {
|
||||||
final Beneficiary beneficiary;
|
final Beneficiary beneficiary;
|
||||||
|
|
||||||
BeneficiaryDetailsScreen({super.key, required this.beneficiary});
|
const BeneficiaryDetailsScreen({super.key, required this.beneficiary});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<BeneficiaryDetailsScreen> createState() =>
|
||||||
|
_BeneficiaryDetailsScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BeneficiaryDetailsScreenState extends State<BeneficiaryDetailsScreen> {
|
||||||
final service = getIt<BeneficiaryService>();
|
final service = getIt<BeneficiaryService>();
|
||||||
|
late String _currentLimit;
|
||||||
|
final _limitController = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_currentLimit = (widget.beneficiary.transactionLimit == null ||
|
||||||
|
widget.beneficiary.transactionLimit!.isEmpty)
|
||||||
|
? 'N/A'
|
||||||
|
: widget.beneficiary.transactionLimit!;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_limitController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void _deleteBeneficiary(BuildContext context) async {
|
void _deleteBeneficiary(BuildContext context) async {
|
||||||
try {
|
try {
|
||||||
await service.deleteBeneficiary(beneficiary.accountNo);
|
await service.deleteBeneficiary(widget.beneficiary.accountNo);
|
||||||
if (!context.mounted) {
|
if (!context.mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -77,6 +101,73 @@ class BeneficiaryDetailsScreen extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _showEditLimitDialog() async {
|
||||||
|
_limitController.text = _currentLimit == 'N/A' ? '' : _currentLimit;
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (dialogContext) {
|
||||||
|
final localizations = AppLocalizations.of(dialogContext);
|
||||||
|
final theme = Theme.of(dialogContext);
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text(localizations.editLimit),
|
||||||
|
content: TextField(
|
||||||
|
controller: _limitController,
|
||||||
|
autofocus: true,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
inputFormatters: [
|
||||||
|
FilteringTextInputFormatter.allow(RegExp(r'^\d+')),
|
||||||
|
],
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: localizations.limitAmount,
|
||||||
|
prefixText: '₹',
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(dialogContext).pop(),
|
||||||
|
child: Text(localizations.cancel),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final value = _limitController.text;
|
||||||
|
if (value.isEmpty || int.tryParse(value) == null) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await service.updateLimit(
|
||||||
|
beneficiaryAccountNo: widget.beneficiary.accountNo,
|
||||||
|
newLimit: value,
|
||||||
|
);
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() {
|
||||||
|
_currentLimit = value;
|
||||||
|
});
|
||||||
|
Navigator.of(dialogContext).pop();
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(localizations.limitUpdatedSuccess),
|
||||||
|
behavior: SnackBarBehavior.floating,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
Navigator.of(dialogContext).pop();
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text("${localizations.error}: $e"),
|
||||||
|
behavior: SnackBarBehavior.floating,
|
||||||
|
backgroundColor: theme.colorScheme.error,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(localizations.save),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@@ -96,11 +187,11 @@ class BeneficiaryDetailsScreen extends StatelessWidget {
|
|||||||
CircleAvatar(
|
CircleAvatar(
|
||||||
radius: 24,
|
radius: 24,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
child: getBankLogo(beneficiary.bankName, context),
|
child: getBankLogo(widget.beneficiary.bankName, context),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
Text(
|
Text(
|
||||||
beneficiary.name,
|
widget.beneficiary.name,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 20, fontWeight: FontWeight.bold),
|
fontSize: 20, fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
@@ -108,29 +199,28 @@ class BeneficiaryDetailsScreen extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
_buildDetailRow('${AppLocalizations.of(context).bankName} ',
|
_buildDetailRow('${AppLocalizations.of(context).bankName} ',
|
||||||
beneficiary.bankName ?? 'N/A'),
|
widget.beneficiary.bankName ?? 'N/A'),
|
||||||
_buildDetailRow(
|
_buildDetailRow(
|
||||||
'${AppLocalizations.of(context).accountNumber} ',
|
'${AppLocalizations.of(context).accountNumber} ',
|
||||||
beneficiary.accountNo),
|
widget.beneficiary.accountNo),
|
||||||
_buildDetailRow(
|
_buildDetailRow(
|
||||||
'${AppLocalizations.of(context).accountType} ',
|
'${AppLocalizations.of(context).accountType} ',
|
||||||
beneficiary.accountType),
|
widget.beneficiary.accountType),
|
||||||
_buildDetailRow('${AppLocalizations.of(context).ifscCode} ',
|
_buildDetailRow('${AppLocalizations.of(context).ifscCode} ',
|
||||||
beneficiary.ifscCode),
|
widget.beneficiary.ifscCode),
|
||||||
_buildDetailRow('${AppLocalizations.of(context).branchName} ',
|
_buildDetailRow('${AppLocalizations.of(context).branchName} ',
|
||||||
beneficiary.branchName ?? 'N/A'),
|
widget.beneficiary.branchName ?? 'N/A'),
|
||||||
_buildDetailRow("Beneficiary Transactional Limit", beneficiary.transactionLimit ?? 'N/A'),
|
_buildDetailRow(
|
||||||
|
"Beneficiary Transactional Limit", _currentLimit),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
children: [
|
children: [
|
||||||
// ElevatedButton.icon(
|
ElevatedButton.icon(
|
||||||
// onPressed: () {
|
onPressed: _showEditLimitDialog,
|
||||||
// // Set Transaction Limit for this beneficiary
|
icon: const Icon(Icons.currency_rupee),
|
||||||
// },
|
label: Text(AppLocalizations.of(context).editLimit),
|
||||||
// icon: const Icon(Icons.currency_rupee),
|
),
|
||||||
// label: const Text('Set Limit'),
|
|
||||||
// ),
|
|
||||||
ElevatedButton.icon(
|
ElevatedButton.icon(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// Delete beneficiary option
|
// Delete beneficiary option
|
||||||
@@ -184,3 +274,4 @@ class BeneficiaryDetailsScreen extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
396
lib/features/yojna/screens/apy_register_screen.dart
Normal file
396
lib/features/yojna/screens/apy_register_screen.dart
Normal file
@@ -0,0 +1,396 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:kmobile/l10n/app_localizations.dart';
|
||||||
|
|
||||||
|
class APYRegisterScreen extends StatefulWidget {
|
||||||
|
final Map<String, dynamic>? initialData;
|
||||||
|
const APYRegisterScreen({super.key, this.initialData});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<APYRegisterScreen> createState() => _APYRegisterScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _APYRegisterScreenState extends State<APYRegisterScreen> {
|
||||||
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
|
// Controllers
|
||||||
|
late final _titleController = TextEditingController(text: widget.initialData?['customertitle']?.toString());
|
||||||
|
late final _firstNameController = TextEditingController(text: widget.initialData?['customerfirstname']?.toString());
|
||||||
|
late final _middleNameController = TextEditingController(text: widget.initialData?['customermiddlename']?.toString());
|
||||||
|
late final _lastNameController = TextEditingController(text: widget.initialData?['customerlastname']?.toString());
|
||||||
|
late final _customerNoController = TextEditingController(text: widget.initialData?['customerno']?.toString());
|
||||||
|
late final _accountNoController = TextEditingController(text: widget.initialData?['accountno']?.toString());
|
||||||
|
late final _balanceController = TextEditingController(text: widget.initialData?['availablebalance']?.toString());
|
||||||
|
late final _dobController = TextEditingController(text: widget.initialData?['customerdob']?.toString());
|
||||||
|
late final _genderController = TextEditingController(text: widget.initialData?['gender']?.toString());
|
||||||
|
late final _marriedController = TextEditingController(text: widget.initialData?['married']?.toString());
|
||||||
|
late final _mobileController = TextEditingController(text: widget.initialData?['mobilenumber']?.toString());
|
||||||
|
late final _emailController = TextEditingController(text: widget.initialData?['emailid']?.toString());
|
||||||
|
late final _aadhaarController = TextEditingController(text: widget.initialData?['aadharno']?.toString());
|
||||||
|
late final _pincodeController = TextEditingController(text: widget.initialData?['pincode']?.toString());
|
||||||
|
|
||||||
|
late final _pensionAmountController = TextEditingController(text: widget.initialData?['pensionamtoptedfor']?.toString() ?? '1000');
|
||||||
|
late final _ageOfJoiningController = TextEditingController(text: widget.initialData?['ageofjoining']?.toString());
|
||||||
|
late final _spouseNameController = TextEditingController(text: widget.initialData?['nameofspouse']?.toString());
|
||||||
|
late final _incomeTaxPayerController = TextEditingController(text: widget.initialData?['whetherincometaxpayer']?.toString());
|
||||||
|
late final _otherSchemeController = TextEditingController(text: widget.initialData?['beneficaryofothersociatysecurityschemes']?.toString());
|
||||||
|
late final _collectionChannelController = TextEditingController(text: widget.initialData?['collectionchannel']?.toString() ?? '1');
|
||||||
|
late final _contributionTypeController = TextEditingController(text: widget.initialData?['contributionType']?.toString() ?? 'C');
|
||||||
|
late final _debitDateController = TextEditingController(text: widget.initialData?['subsequentContributionDebitDate']?.toString());
|
||||||
|
|
||||||
|
late final _nomineeNameController = TextEditingController(text: widget.initialData?['nomineename']?.toString());
|
||||||
|
late final _nomineeRelationController = TextEditingController(text: widget.initialData?['relationwithsubscriber']?.toString());
|
||||||
|
late final _nomineeMinorController = TextEditingController(text: widget.initialData?['nomineeminor']?.toString() ?? 'N');
|
||||||
|
late final _nomineeDobController = TextEditingController();
|
||||||
|
late final _guardianNameController = TextEditingController();
|
||||||
|
|
||||||
|
late final _fatcaController = TextEditingController(text: widget.initialData?['fatcacrsapplicable']?.toString());
|
||||||
|
late final _birthCountryController = TextEditingController(text: widget.initialData?['countryofbirth']?.toString());
|
||||||
|
late final _citizenshipCountryController = TextEditingController(text: widget.initialData?['countryofcitizenship']?.toString());
|
||||||
|
late final _taxResidenceCountryController = TextEditingController(text: widget.initialData?['countryofresidencefortaxpurpose']?.toString());
|
||||||
|
late final _usPersonController = TextEditingController(text: widget.initialData?['uspersonflag']?.toString());
|
||||||
|
|
||||||
|
final Map<String, String> _yesNoOptions = {
|
||||||
|
'Y': 'Yes',
|
||||||
|
'N': 'No',
|
||||||
|
};
|
||||||
|
|
||||||
|
final Map<String, String> _titleOptions = {
|
||||||
|
'01': 'Mr.',
|
||||||
|
'02': 'Mrs.',
|
||||||
|
'03': 'Miss',
|
||||||
|
};
|
||||||
|
|
||||||
|
final Map<String, String> _pensionOptions = {
|
||||||
|
'1000': '₹1000',
|
||||||
|
'2000': '₹2000',
|
||||||
|
'3000': '₹3000',
|
||||||
|
'4000': '₹4000',
|
||||||
|
'5000': '₹5000',
|
||||||
|
};
|
||||||
|
|
||||||
|
final Map<String, String> _collectionChannelOptions = {
|
||||||
|
'1': 'Bank',
|
||||||
|
};
|
||||||
|
|
||||||
|
Map<String, String> _getPeriodicityOptions(AppLocalizations l10n) => {
|
||||||
|
'C': l10n.monthly,
|
||||||
|
'Q': l10n.quarterly,
|
||||||
|
'H': l10n.halfYearly,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Contribution Calculation Map
|
||||||
|
final Map<String, Map<String, String>> _contributionRates = {
|
||||||
|
'C': {
|
||||||
|
'1000': '90',
|
||||||
|
'2000': '178',
|
||||||
|
'3000': '268',
|
||||||
|
'4000': '356',
|
||||||
|
'5000': '446',
|
||||||
|
},
|
||||||
|
'Q': {
|
||||||
|
'1000': '268',
|
||||||
|
'2000': '530',
|
||||||
|
'3000': '1061',
|
||||||
|
'4000': '356', // Following prompt's example exactly
|
||||||
|
'5000': '1329',
|
||||||
|
},
|
||||||
|
'H': {
|
||||||
|
'1000': '531',
|
||||||
|
'2000': '1050',
|
||||||
|
'3000': '1582',
|
||||||
|
'4000': '2101',
|
||||||
|
'5000': '2632',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
String get _calculatedContribution {
|
||||||
|
final periodicity = _contributionTypeController.text;
|
||||||
|
final amount = _pensionAmountController.text;
|
||||||
|
return _contributionRates[periodicity]?[amount] ?? '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_titleController.dispose();
|
||||||
|
_firstNameController.dispose();
|
||||||
|
_middleNameController.dispose();
|
||||||
|
_lastNameController.dispose();
|
||||||
|
_customerNoController.dispose();
|
||||||
|
_accountNoController.dispose();
|
||||||
|
_balanceController.dispose();
|
||||||
|
_dobController.dispose();
|
||||||
|
_genderController.dispose();
|
||||||
|
_marriedController.dispose();
|
||||||
|
_mobileController.dispose();
|
||||||
|
_emailController.dispose();
|
||||||
|
_aadhaarController.dispose();
|
||||||
|
_pincodeController.dispose();
|
||||||
|
_pensionAmountController.dispose();
|
||||||
|
_ageOfJoiningController.dispose();
|
||||||
|
_spouseNameController.dispose();
|
||||||
|
_incomeTaxPayerController.dispose();
|
||||||
|
_otherSchemeController.dispose();
|
||||||
|
_collectionChannelController.dispose();
|
||||||
|
_contributionTypeController.dispose();
|
||||||
|
_debitDateController.dispose();
|
||||||
|
_nomineeNameController.dispose();
|
||||||
|
_nomineeRelationController.dispose();
|
||||||
|
_nomineeMinorController.dispose();
|
||||||
|
_nomineeDobController.dispose();
|
||||||
|
_guardianNameController.dispose();
|
||||||
|
_fatcaController.dispose();
|
||||||
|
_birthCountryController.dispose();
|
||||||
|
_citizenshipCountryController.dispose();
|
||||||
|
_taxResidenceCountryController.dispose();
|
||||||
|
_usPersonController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isFetched(String key) {
|
||||||
|
return widget.initialData != null &&
|
||||||
|
widget.initialData!.containsKey(key) &&
|
||||||
|
widget.initialData![key]?.toString().isNotEmpty == true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleRegister() {
|
||||||
|
if (_formKey.currentState!.validate()) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(content: Text('Registration logic to be implemented')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final l10n = AppLocalizations.of(context);
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(l10n.apyRegistration),
|
||||||
|
),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Form(
|
||||||
|
key: _formKey,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
// Tile 1: Customer & APY Details
|
||||||
|
Card(
|
||||||
|
elevation: 2,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(l10n.details,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 18, fontWeight: FontWeight.bold)),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
_buildDropdownField(_titleController, l10n.customerTitle,
|
||||||
|
_titleOptions,
|
||||||
|
readOnly: _isFetched('customertitle')),
|
||||||
|
_buildTextField(
|
||||||
|
_firstNameController, l10n.customerFirstName,
|
||||||
|
readOnly: _isFetched('customerfirstname')),
|
||||||
|
_buildTextField(
|
||||||
|
_middleNameController, l10n.customerMiddleName,
|
||||||
|
readOnly: _isFetched('customermiddlename')),
|
||||||
|
_buildTextField(
|
||||||
|
_lastNameController, l10n.customerLastName,
|
||||||
|
readOnly: _isFetched('customerlastname')),
|
||||||
|
_buildTextField(_accountNoController, l10n.accountNumber,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
readOnly: _isFetched('accountno')),
|
||||||
|
_buildTextField(
|
||||||
|
_balanceController, l10n.availableBalance,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
readOnly: _isFetched('availablebalance')),
|
||||||
|
_buildTextField(_dobController, l10n.customerDobFormat,
|
||||||
|
mandatory: true, readOnly: _isFetched('customerdob')),
|
||||||
|
_buildTextField(_emailController, l10n.emailId,
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
readOnly: _isFetched('emailid')),
|
||||||
|
_buildDropdownField(
|
||||||
|
_marriedController, l10n.marriedYesNo, _yesNoOptions,
|
||||||
|
mandatory: true),
|
||||||
|
_buildTextField(_nomineeNameController, l10n.nomineeName,
|
||||||
|
mandatory: true),
|
||||||
|
_buildDropdownField(_nomineeMinorController,
|
||||||
|
l10n.nomineeMinor, _yesNoOptions,
|
||||||
|
mandatory: true, onChanged: (val) {
|
||||||
|
setState(() {
|
||||||
|
_nomineeMinorController.text = val ?? 'N';
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
if (_nomineeMinorController.text == 'Y') ...[
|
||||||
|
_buildTextField(_nomineeDobController, l10n.nomineeDob,
|
||||||
|
mandatory: true),
|
||||||
|
_buildTextField(
|
||||||
|
_guardianNameController, l10n.guardianName,
|
||||||
|
mandatory: true),
|
||||||
|
],
|
||||||
|
_buildDropdownField(_otherSchemeController,
|
||||||
|
l10n.isBeneficiaryOtherScheme, _yesNoOptions,
|
||||||
|
mandatory: true),
|
||||||
|
_buildDropdownField(_incomeTaxPayerController,
|
||||||
|
l10n.isIncomeTaxPayer, _yesNoOptions,
|
||||||
|
mandatory: true),
|
||||||
|
_buildTextField(_spouseNameController, l10n.nameOfSpouse,
|
||||||
|
mandatory: true),
|
||||||
|
_buildTextField(_ageOfJoiningController, l10n.ageOfJoining,
|
||||||
|
keyboardType: TextInputType.number, mandatory: true),
|
||||||
|
_buildDropdownField(_collectionChannelController,
|
||||||
|
l10n.collectionChannel, _collectionChannelOptions,
|
||||||
|
mandatory: true),
|
||||||
|
_buildDropdownField(
|
||||||
|
_fatcaController, l10n.fatcaApplicable, _yesNoOptions),
|
||||||
|
_buildTextField(
|
||||||
|
_birthCountryController, l10n.countryOfBirth),
|
||||||
|
_buildTextField(
|
||||||
|
_citizenshipCountryController, l10n.countryOfCitizenship),
|
||||||
|
_buildTextField(_taxResidenceCountryController,
|
||||||
|
l10n.countryOfTaxResidence),
|
||||||
|
_buildDropdownField(
|
||||||
|
_usPersonController, l10n.usPersonFlag, _yesNoOptions),
|
||||||
|
const Divider(height: 32),
|
||||||
|
_buildDropdownField(_pensionAmountController,
|
||||||
|
l10n.pensionAmount, _pensionOptions,
|
||||||
|
mandatory: true, onChanged: (val) {
|
||||||
|
setState(() {
|
||||||
|
_pensionAmountController.text = val ?? '1000';
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
_buildDropdownField(_contributionTypeController,
|
||||||
|
l10n.periodicity, _getPeriodicityOptions(l10n),
|
||||||
|
mandatory: true, onChanged: (val) {
|
||||||
|
setState(() {
|
||||||
|
_contributionTypeController.text = val ?? 'C';
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
_buildTextField(
|
||||||
|
_debitDateController, l10n.subsequentDebitDate,
|
||||||
|
mandatory: true),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
// Tile 2: Contribution Amount
|
||||||
|
Card(
|
||||||
|
elevation: 2,
|
||||||
|
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'${l10n.contributionAmount} (${_getPeriodicityOptions(l10n)[_contributionTypeController.text]})',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onSecondaryContainer)),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text('₹ $_calculatedContribution',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 32,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onSecondaryContainer)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _handleRegister,
|
||||||
|
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: Text(
|
||||||
|
l10n.register,
|
||||||
|
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDropdownField(
|
||||||
|
TextEditingController controller, String label, Map<String, String> options,
|
||||||
|
{bool readOnly = false, bool mandatory = false, void Function(String?)? onChanged}) {
|
||||||
|
String? currentValue =
|
||||||
|
options.containsKey(controller.text) ? controller.text : null;
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 16.0),
|
||||||
|
child: DropdownButtonFormField<String>(
|
||||||
|
value: currentValue,
|
||||||
|
onChanged: readOnly ? null : (newValue) {
|
||||||
|
if (onChanged != null) {
|
||||||
|
onChanged(newValue);
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
controller.text = 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 'This field is required';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
items: options.entries.map((entry) {
|
||||||
|
return DropdownMenuItem<String>(
|
||||||
|
value: entry.key,
|
||||||
|
child: Text(entry.value),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTextField(TextEditingController controller, String label,
|
||||||
|
{TextInputType keyboardType = TextInputType.text,
|
||||||
|
bool readOnly = false,
|
||||||
|
bool mandatory = false}) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 16.0),
|
||||||
|
child: TextFormField(
|
||||||
|
controller: controller,
|
||||||
|
readOnly: readOnly,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: mandatory ? '$label *' : label,
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
contentPadding:
|
||||||
|
const EdgeInsets.symmetric(vertical: 16, horizontal: 12),
|
||||||
|
),
|
||||||
|
keyboardType: keyboardType,
|
||||||
|
validator: (value) {
|
||||||
|
if (mandatory && (value == null || value.isEmpty)) {
|
||||||
|
return 'This field is required';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:kmobile/data/models/user.dart';
|
import 'package:kmobile/data/models/user.dart';
|
||||||
|
import 'package:kmobile/features/yojna/screens/apy_register_screen.dart';
|
||||||
import 'package:kmobile/l10n/app_localizations.dart';
|
import 'package:kmobile/l10n/app_localizations.dart';
|
||||||
|
|
||||||
class APYScreen extends StatefulWidget {
|
class APYScreen extends StatefulWidget {
|
||||||
@@ -18,6 +19,7 @@ class APYScreen extends StatefulWidget {
|
|||||||
class _APYScreenState extends State<APYScreen> {
|
class _APYScreenState extends State<APYScreen> {
|
||||||
User? _selectedAccount;
|
User? _selectedAccount;
|
||||||
List<User> _filteredUsers = [];
|
List<User> _filteredUsers = [];
|
||||||
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -54,6 +56,8 @@ class _APYScreenState extends State<APYScreen> {
|
|||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Form(
|
||||||
|
key: _formKey,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
@@ -105,9 +109,81 @@ class _APYScreenState extends State<APYScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (_formKey.currentState!.validate()) {
|
||||||
|
final mockData = {
|
||||||
|
"accountno": _selectedAccount?.accountNo ?? "50069506061",
|
||||||
|
"customerfirstname": "TAMANA",
|
||||||
|
"customermiddlename": "",
|
||||||
|
"customerlastname": "",
|
||||||
|
"availablebalance": "634000",
|
||||||
|
"customerdob": "06061998",
|
||||||
|
"emailid": "",
|
||||||
|
"gender": "F",
|
||||||
|
"married": "Y",
|
||||||
|
"nomineename": "shubham Kada",
|
||||||
|
"relationwithsubscriber": "S",
|
||||||
|
"mobilenumber": "",
|
||||||
|
"nomineeminor": "N",
|
||||||
|
"customerno": "30028309887",
|
||||||
|
"beneficaryofothersociatysecurityschemes": "N",
|
||||||
|
"whetherincometaxpayer": "N",
|
||||||
|
"customertitle": "02",
|
||||||
|
"aadharno": "",
|
||||||
|
"nameofspouse": "shubham kada",
|
||||||
|
"ageofjoining": "27",
|
||||||
|
"pensionamtoptedfor": "1000",
|
||||||
|
"montlycontributioncalculate": "90",
|
||||||
|
"collectionchannel": "1",
|
||||||
|
"subsequentContributionDebitDate": "02012026",
|
||||||
|
"secondnomineeminor": "N",
|
||||||
|
"secondnomineename": "shubham kad",
|
||||||
|
"secondrelationshipwithsubscriber": "",
|
||||||
|
"pincode": "176215",
|
||||||
|
"fatcacrsapplicable": "N",
|
||||||
|
"countryofbirth": "India",
|
||||||
|
"countryofcitizenship": "India",
|
||||||
|
"countryofresidencefortaxpurpose": "India",
|
||||||
|
"uspersonflag": "N",
|
||||||
|
"fatcadeclarationcount": "",
|
||||||
|
"documentevidencingcitizenshipflag": "",
|
||||||
|
"reasonfornoevidence": "",
|
||||||
|
"nameofdocumentforcitizenshipevidence": "",
|
||||||
|
"modeofcollection": "Transfer Voucher",
|
||||||
|
"contributionType": "C"
|
||||||
|
};
|
||||||
|
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => APYRegisterScreen(initialData: mockData),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor:
|
||||||
|
Theme.of(context).colorScheme.primaryContainer,
|
||||||
|
foregroundColor:
|
||||||
|
Theme.of(context).colorScheme.onPrimaryContainer,
|
||||||
|
minimumSize: const Size(double.infinity, 50),
|
||||||
|
elevation: 4,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
l10n.proceedButton,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -641,6 +641,7 @@
|
|||||||
"collectionChannel": "Collection Channel",
|
"collectionChannel": "Collection Channel",
|
||||||
"nomineeDetails": "Nominee Details",
|
"nomineeDetails": "Nominee Details",
|
||||||
"nomineeAddress": "Nominee Address",
|
"nomineeAddress": "Nominee Address",
|
||||||
|
"relationWithNominee": "Relation with Nominee",
|
||||||
"nomineeRelationship": "Nominee Relationship",
|
"nomineeRelationship": "Nominee Relationship",
|
||||||
"nomineeMinor": "Nominee Minor",
|
"nomineeMinor": "Nominee Minor",
|
||||||
"response": "Response",
|
"response": "Response",
|
||||||
@@ -648,7 +649,37 @@
|
|||||||
"address1": "Address 1",
|
"address1": "Address 1",
|
||||||
"address2": "Address 2",
|
"address2": "Address 2",
|
||||||
"city": "City",
|
"city": "City",
|
||||||
"relationWithNominee": "Relation with Nominee",
|
"relationwithnominee": "Relation with Nominee",
|
||||||
"policyStatus": "Policy Status",
|
"policyStatus": "Policy Status",
|
||||||
"emailId": "Email ID"
|
"emailId": "Email ID",
|
||||||
|
"customerFirstName": "Customer First Name",
|
||||||
|
"customerMiddleName": "Customer Middle Name",
|
||||||
|
"customerLastName": "Customer Last Name",
|
||||||
|
"pensionAmountOptedFor": "Pension Amount Opted For",
|
||||||
|
"monthlyContribution": "Monthly Contribution",
|
||||||
|
"ageOfJoining": "Age of Joining",
|
||||||
|
"nameOfSpouse": "Name of Spouse",
|
||||||
|
"isIncomeTaxPayer": "Whether Income Tax Payer",
|
||||||
|
"isBeneficiaryOtherScheme": "Beneficiary of other Social Security Schemes",
|
||||||
|
"relationWithSubscriber": "Relation with Subscriber",
|
||||||
|
"secondNomineeName": "Second Nominee Name",
|
||||||
|
"secondNomineeRelationship": "Second Nominee Relationship",
|
||||||
|
"secondNomineeMinor": "Second Nominee Minor",
|
||||||
|
"fatcaApplicable": "FATCA/CRS Applicable",
|
||||||
|
"countryOfBirth": "Country of Birth",
|
||||||
|
"countryOfCitizenship": "Country of Citizenship",
|
||||||
|
"countryOfTaxResidence": "Country of Residence for Tax Purpose",
|
||||||
|
"usPersonFlag": "US Person Flag",
|
||||||
|
"modeOfCollection": "Mode of Collection",
|
||||||
|
"contributionType": "Contribution Type",
|
||||||
|
"subsequentDebitDate": "Subsequent Contribution Debit Date",
|
||||||
|
"customerTitle": "Title",
|
||||||
|
"nomineeDob": "Nominee Date of Birth",
|
||||||
|
"guardianName": "Guardian's Name",
|
||||||
|
"pensionAmount": "Pension Amount",
|
||||||
|
"periodicity": "Periodicity",
|
||||||
|
"contributionAmount": "Contribution Amount",
|
||||||
|
"monthly": "Monthly",
|
||||||
|
"quarterly": "Quarterly",
|
||||||
|
"halfYearly": "Half Yearly"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -649,6 +649,38 @@
|
|||||||
"address1": "पता 1",
|
"address1": "पता 1",
|
||||||
"address2": "पता 2",
|
"address2": "पता 2",
|
||||||
"city": "शहर",
|
"city": "शहर",
|
||||||
|
"relationwithnominee": "नामांकित व्यक्ति के साथ संबंध",
|
||||||
|
"policyStatus": "पॉलिसी की स्थिति",
|
||||||
|
"emailId": "ईमेल आईडी",
|
||||||
|
"customerFirstName": "ग्राहक का पहला नाम",
|
||||||
|
"customerMiddleName": "ग्राहक का मध्य नाम",
|
||||||
|
"customerLastName": "ग्राहक का अंतिम नाम",
|
||||||
|
"pensionAmountOptedFor": "चुनी गई पेंशन राशि",
|
||||||
|
"monthlyContribution": "मासिक योगदान",
|
||||||
|
"ageOfJoining": "शामिल होने की आयु",
|
||||||
|
"nameOfSpouse": "पति/पत्नी का नाम",
|
||||||
|
"isIncomeTaxPayer": "क्या आयकर दाता है",
|
||||||
|
"isBeneficiaryOtherScheme": "अन्य सामाजिक सुरक्षा योजनाओं के लाभार्थी",
|
||||||
|
"relationWithSubscriber": "अभिदाता के साथ संबंध",
|
||||||
|
"secondNomineeName": "दूसरे नामांकित व्यक्ति का नाम",
|
||||||
"relationWithNominee": "नामांकित व्यक्ति के साथ संबंध",
|
"relationWithNominee": "नामांकित व्यक्ति के साथ संबंध",
|
||||||
"policyStatus": "पॉलिसी की स्थिति"
|
"secondNomineeRelationship": "दूसरे नामांकित व्यक्ति का संबंध",
|
||||||
|
"secondNomineeMinor": "दूसरा नामांकित व्यक्ति नाबालिग है",
|
||||||
|
"fatcaApplicable": "FATCA/CRS लागू है",
|
||||||
|
"countryOfBirth": "जन्म का देश",
|
||||||
|
"countryOfCitizenship": "नागरिकता का देश",
|
||||||
|
"countryOfTaxResidence": "कर उद्देश्य के लिए निवास का देश",
|
||||||
|
"usPersonFlag": "अमेरिकी व्यक्ति ध्वज",
|
||||||
|
"modeOfCollection": "संग्रह का माध्यम",
|
||||||
|
"contributionType": "योगदान का प्रकार",
|
||||||
|
"subsequentDebitDate": "अगली योगदान डेबिट तिथि",
|
||||||
|
"customerTitle": "शीर्षक",
|
||||||
|
"nomineeDob": "नामांकित व्यक्ति की जन्म तिथि",
|
||||||
|
"guardianName": "अभिभावक का नाम",
|
||||||
|
"pensionAmount": "पेंशन राशि",
|
||||||
|
"periodicity": "आवधिकता",
|
||||||
|
"contributionAmount": "योगदान राशि",
|
||||||
|
"monthly": "मासिक",
|
||||||
|
"quarterly": "त्रैमासिक",
|
||||||
|
"halfYearly": "अर्धवार्षिक"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user