Manage Beneficiary Services 2

This commit is contained in:
2025-08-10 11:18:00 +05:30
parent 83609fb778
commit 3024ddef15
2 changed files with 142 additions and 81 deletions

View File

@@ -1,17 +1,23 @@
// ignore_for_file: prefer_final_fields, unused_field
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:kmobile/api/services/beneficiary_service.dart'; import 'package:kmobile/api/services/beneficiary_service.dart';
import 'package:kmobile/data/models/ifsc.dart'; import 'package:kmobile/data/models/ifsc.dart';
import 'package:kmobile/data/models/beneficiary.dart'; import 'package:kmobile/data/models/beneficiary.dart';
import 'package:kmobile/data/models/user.dart';
import 'beneficiary_result_page.dart'; import 'beneficiary_result_page.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart';
import '../../../di/injection.dart'; import '../../../di/injection.dart';
import '../../../l10n/app_localizations.dart'; import '../../../l10n/app_localizations.dart';
class AddBeneficiaryScreen extends StatefulWidget { class AddBeneficiaryScreen extends StatefulWidget {
const AddBeneficiaryScreen({super.key}); final List<User>? users;
final int? selectedIndex;
const AddBeneficiaryScreen({
super.key,
this.users,
this.selectedIndex,
});
@override @override
State<AddBeneficiaryScreen> createState() => _AddBeneficiaryScreen(); State<AddBeneficiaryScreen> createState() => _AddBeneficiaryScreen();
@@ -20,6 +26,7 @@ class AddBeneficiaryScreen extends StatefulWidget {
class _AddBeneficiaryScreen extends State<AddBeneficiaryScreen> { class _AddBeneficiaryScreen extends State<AddBeneficiaryScreen> {
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
late User selectedUser = (widget.users ?? [])[widget.selectedIndex!];
final TextEditingController accountNumberController = TextEditingController(); final TextEditingController accountNumberController = TextEditingController();
final TextEditingController confirmAccountNumberController = final TextEditingController confirmAccountNumberController =
TextEditingController(); TextEditingController();
@@ -82,28 +89,83 @@ bool _isLoading = false; //for validateIFSC()
Future<void> _validateBeneficiary() async { Future<void> _validateBeneficiary() async {
var beneficiaryService = getIt<BeneficiaryService>(); // start spinner / disable button
setState(() { setState(() {
_isValidating = true; _isValidating = true;
_validationError = null; _validationError = null;
});
try {
final name = await beneficiaryService
.validateBeneficiaryWithinBank(accountNumberController.text);
setState(() {
_beneficiaryName = name;
_isBeneficiaryValidated = true;
_isValidating = false;
});
} catch (e) {
setState(() {
_validationError = "Account Number not from KCCB";
_isValidating = false;
_isBeneficiaryValidated = false; _isBeneficiaryValidated = false;
_beneficiaryName = null; nameController.text = ''; // clear previous name
});
final String accountNo = accountNumberController.text.trim();
final String ifsc = ifscController.text.trim();
final String remitter = selectedUser.name ?? '';
final service = getIt<BeneficiaryService>();
try {
// Step 1: call validate API -> get refNo
final String? refNo = await service.validateBeneficiary(
accountNo: accountNo,
ifscCode: ifsc,
remitterName: remitter,
);
if (refNo == null || refNo.isEmpty) {
setState(() {
_validationError = 'Validation request failed. Please check details.';
_isBeneficiaryValidated = false;
});
return;
}
// Step 2: poll checkValidationStatus for up to 30 seconds
const int timeoutSeconds = 30;
const int intervalSeconds = 2;
int elapsed = 0;
String? foundName;
while (elapsed < timeoutSeconds) {
final String? name = await service.checkValidationStatus(refNo);
if (name != null && name.trim().isNotEmpty) {
foundName = name.trim();
break;
}
await Future.delayed(const Duration(seconds: intervalSeconds));
elapsed += intervalSeconds;
}
if (foundName != null) {
setState(() {
nameController.text = foundName!;
_isBeneficiaryValidated = true;
_validationError = null;
});
} else {
setState(() {
_validationError = 'Beneficiary not found within timeout.';
_isBeneficiaryValidated = false;
});
}
} catch (e, st) {
// handle unexpected errors
// print or log if you want
debugPrint('Error validating beneficiary: $e\n$st');
setState(() {
_validationError = 'Something went wrong. Please try again.';
_isBeneficiaryValidated = false;
});
} finally {
if (mounted) {
setState(() {
_isValidating = false;
}); });
} }
} }
}
String _selectedAccountType = 'Savings'; // default value String _selectedAccountType = 'Savings'; // default value
@@ -361,8 +423,7 @@ void validateAndAddBeneficiary() async {
), ),
), ),
), ),
//Validate Beneficiary Name
//validate Beneficiary Button
if (!_isBeneficiaryValidated) if (!_isBeneficiaryValidated)
Padding( Padding(
padding: const EdgeInsets.only(top: 12.0), padding: const EdgeInsets.only(top: 12.0),
@@ -372,7 +433,7 @@ void validateAndAddBeneficiary() async {
onPressed: _isValidating onPressed: _isValidating
? null ? null
: () { : () {
if (accountNumberController.text.length == 11 && if (
confirmAccountNumberController.text == confirmAccountNumberController.text ==
accountNumberController.text) { accountNumberController.text) {
_validateBeneficiary(); _validateBeneficiary();
@@ -388,20 +449,17 @@ void validateAndAddBeneficiary() async {
width: 20, width: 20,
height: 20, height: 20,
child: CircularProgressIndicator(strokeWidth: 2), child: CircularProgressIndicator(strokeWidth: 2),
) )
: const Text('Validate Beneficiary'), : const Text('Validate Beneficiary'),
), ),
), ),
), ),
//Beneficiary Name (Disabled) //Beneficiary Name (Disabled)
const SizedBox(height: 24),
TextFormField( TextFormField(
controller: nameController, controller: nameController,
enabled: false, enabled: false,
decoration: InputDecoration( decoration: InputDecoration(
labelText: AppLocalizations.of(context).beneficiaryName, labelText: AppLocalizations.of(context).beneficiaryName,
// prefixIcon: Icon(Icons.person),
border: const OutlineInputBorder(), border: const OutlineInputBorder(),
isDense: true, isDense: true,
filled: true, filled: true,
@@ -410,10 +468,7 @@ void validateAndAddBeneficiary() async {
borderSide: BorderSide(color: Colors.black), borderSide: BorderSide(color: Colors.black),
), ),
focusedBorder: const OutlineInputBorder( focusedBorder: const OutlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(color: Colors.black, width: 2),
color: Colors.black,
width: 2,
),
), ),
), ),
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
@@ -421,7 +476,6 @@ void validateAndAddBeneficiary() async {
? AppLocalizations.of(context).nameRequired ? AppLocalizations.of(context).nameRequired
: null, : null,
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
// 🔹 Account Type Dropdown // 🔹 Account Type Dropdown
DropdownButtonFormField<String>( DropdownButtonFormField<String>(

View File

@@ -1,13 +1,17 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:kmobile/data/models/beneficiary.dart'; import 'package:kmobile/data/models/beneficiary.dart';
import 'package:kmobile/features/beneficiaries/screens/add_beneficiary_screen.dart'; import 'package:kmobile/features/beneficiaries/screens/add_beneficiary_screen.dart';
import '../../../data/models/user.dart';
import '../../../l10n/app_localizations.dart'; import '../../../l10n/app_localizations.dart';
import '../../../di/injection.dart'; import '../../../di/injection.dart';
import 'package:kmobile/api/services/beneficiary_service.dart'; import 'package:kmobile/api/services/beneficiary_service.dart';
import 'package:shimmer/shimmer.dart'; import 'package:shimmer/shimmer.dart';
//import 'package:kmobile/data/models/user.dart';
class ManageBeneficiariesScreen extends StatefulWidget { class ManageBeneficiariesScreen extends StatefulWidget {
// final List<User> users;
// final int selectedIndex;
const ManageBeneficiariesScreen({super.key}); const ManageBeneficiariesScreen({super.key});
@override @override
@@ -17,10 +21,13 @@ class ManageBeneficiariesScreen extends StatefulWidget {
class _ManageBeneficiariesScreen extends State<ManageBeneficiariesScreen> { class _ManageBeneficiariesScreen extends State<ManageBeneficiariesScreen> {
var service = getIt<BeneficiaryService>(); var service = getIt<BeneficiaryService>();
// late User selectedUser = widget.users[widget.selectedIndex];
//final BeneficiaryService _service = BeneficiaryService(); //final BeneficiaryService _service = BeneficiaryService();
bool _isLoading = true; bool _isLoading = true;
int selectedAccountIndex = 0;
List<Beneficiary> _beneficiaries = []; List<Beneficiary> _beneficiaries = [];
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@@ -101,7 +108,7 @@ class _ManageBeneficiariesScreen extends State<ManageBeneficiariesScreen> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => const AddBeneficiaryScreen(), builder: (context) => AddBeneficiaryScreen(),
), ),
); );
}, },