import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:kmobile/api/services/cheque_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 PositivePayScreen extends StatefulWidget { final List users; final int selectedIndex; const PositivePayScreen({ super.key, required this.users, required this.selectedIndex, }); @override State createState() => _PositivePayScreenState(); } class _PositivePayScreenState extends State { User? _selectedAccount; List _filteredUsers = []; final _formKey = GlobalKey(); final _chequeNumberController = TextEditingController(); final _chequeDateController = TextEditingController(); final _amountController = TextEditingController(); final _payeeController = TextEditingController(); final _chequeService = getIt(); String? _ciFromCheque; String? _ciToCheque; @override void initState() { super.initState(); _filteredUsers = widget.users .where((user) => ['SA', 'SB', 'CA', 'CC'].contains(user.accountType)) .toList(); // Pre-fill the account number if possible if (widget.users.isNotEmpty && widget.selectedIndex < widget.users.length) { if (_filteredUsers.isNotEmpty) { if (_filteredUsers.contains(widget.users[widget.selectedIndex])) { _selectedAccount = widget.users[widget.selectedIndex]; } else { _selectedAccount = _filteredUsers.first; } } else { _selectedAccount = widget.users[widget.selectedIndex]; } } else { if (_filteredUsers.isNotEmpty) { _selectedAccount = _filteredUsers.first; } } _loadChequeDetails(); } Future _loadChequeDetails() async { if (_selectedAccount == null) return; String instrType; switch (_selectedAccount!.accountType) { case 'SA': case 'SB': instrType = '10'; break; case 'CA': instrType = '11'; break; case 'CC': instrType = '13'; break; default: instrType = '10'; } try { final data = await _chequeService.ChequeEnquiry( accountNumber: _selectedAccount!.accountNo!, instrType: instrType, ); final ciCheque = data.where((cheque) => cheque.type == 'CI').toList(); if (mounted) { setState(() { if (ciCheque.isNotEmpty) { _ciFromCheque = ciCheque.first.fromCheque; _ciToCheque = ciCheque.first.toCheque; } else { _ciFromCheque = null; _ciToCheque = null; } }); } } catch (e) { if (mounted) { setState(() { _ciFromCheque = null; _ciToCheque = null; }); } } } @override void dispose() { _chequeNumberController.dispose(); _chequeDateController.dispose(); _amountController.dispose(); _payeeController.dispose(); super.dispose(); } Future _showResponseDialog(String title, String message) async { return showDialog( context: context, barrierDismissible: false, // user must tap button! builder: (BuildContext context) { return AlertDialog( title: Text(title), content: SingleChildScrollView( child: ListBody( children: [ Text(message), ], ), ), actions: [ TextButton( child: Text(AppLocalizations.of(context).closeButton), onPressed: () { Navigator.of(context).pop(); }, ), ], ); }, ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text( AppLocalizations.of(context) .positivePay, // Will be replaced by localization ), centerTitle: false, ), body: SingleChildScrollView( padding: const EdgeInsets.all(16.0), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const SizedBox(height: 16.0), DropdownButtonFormField( value: _selectedAccount, decoration: InputDecoration( labelText: AppLocalizations.of(context).accountNumber, border: const OutlineInputBorder(), ), items: _filteredUsers.map((user) { return DropdownMenuItem( value: user, child: Text(user.accountNo.toString()), ); }).toList(), onChanged: (User? newUser) { setState(() { _selectedAccount = newUser; _loadChequeDetails(); }); }, validator: (value) { if (value == null) { return AppLocalizations.of(context).accountNumberRequired; } return null; }, ), const SizedBox(height: 16.0), TextFormField( controller: _chequeNumberController, decoration: InputDecoration( labelText: AppLocalizations.of(context).chequeNumberLabel, border: const OutlineInputBorder(), errorMaxLines: 2, ), keyboardType: TextInputType.number, validator: (value) { if (value == null || value.isEmpty) { return AppLocalizations.of(context).pleaseEnterChequeNumber; } final chequeNumber = int.tryParse(value); final fromCheque = int.tryParse(_ciFromCheque ?? ''); final toCheque = int.tryParse(_ciToCheque ?? ''); if (chequeNumber == null) { return AppLocalizations.of(context) .invalidChequeNumberFormatError; } if (fromCheque != null && toCheque != null) { if (chequeNumber < fromCheque || chequeNumber > toCheque) { return AppLocalizations.of(context) .chequeNumberRangeError(_ciFromCheque!, _ciToCheque!); } } return null; }, ), const SizedBox(height: 16.0), TextFormField( controller: _chequeDateController, decoration: InputDecoration( labelText: AppLocalizations.of(context).chequeIssuedDate, border: const OutlineInputBorder(), suffixIcon: const Icon(Icons.calendar_today), ), readOnly: true, onTap: () async { DateTime? pickedDate = await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime(2000), lastDate: DateTime.now(), ); if (pickedDate != null) { // Format the date as you wish String formattedDate = "${pickedDate.year}-${pickedDate.month.toString().padLeft(2, '0')}-${pickedDate.day.toString().padLeft(2, '0')}"; _chequeDateController.text = formattedDate; } }, validator: (value) { if (value == null || value.isEmpty) { return AppLocalizations.of(context) .pleaseSelectChequeIssuedDate; } return null; }, ), const SizedBox(height: 16.0), TextFormField( controller: _payeeController, decoration: InputDecoration( labelText: AppLocalizations.of(context).payeeName, border: const OutlineInputBorder(), ), ), const SizedBox(height: 16.0), TextFormField( controller: _amountController, decoration: InputDecoration( labelText: AppLocalizations.of(context).amountLabel, border: const OutlineInputBorder(), prefixText: '₹ ', ), keyboardType: const TextInputType.numberWithOptions(decimal: true), validator: (value) { if (value == null || value.isEmpty) { return AppLocalizations.of(context).pleaseEnterAmount; } return null; }, ), const SizedBox(height: 32.0), ElevatedButton( onPressed: () { if (_formKey.currentState!.validate()) { // Process data Navigator.push( context, MaterialPageRoute( builder: (context) => TransactionPinScreen( onPinCompleted: (ctx, pin) async { Navigator.pop(context); try { final response = await _chequeService.registerPPS( cheque_no: _chequeNumberController.text, account_number: _selectedAccount!.accountNo!, issue_date: _chequeDateController.text, amount: _amountController.text, payee_name: _payeeController.text, tpin: pin, ); if (!mounted) return; String responseString = response.toString(); if (responseString == 'PPS Registered Successfully') { _showResponseDialog('REGISTRATION SUCCESFUL', 'Your Positive Pay Request has been registered succesfully'); } if (responseString .contains('Cheque already registered')) { _showResponseDialog('ERROR', 'Your Request for the selected cheque number has already been registered'); } if (responseString.contains('INCORRECT_TPIN')) { _showResponseDialog('Invalid TPIN', 'The TPIN you entered is incorrect. Please try again.'); } } on DioException catch (e) { try { final errorBodyString = e.toString().split('Exception: ')[1]; final errorBody = jsonDecode(errorBodyString); if (errorBody.containsKey('error') && errorBody['error'] == 'INCORRECT_TPIN') { _showResponseDialog('Invalid TPIN', 'The TPIN you entered is incorrect. Please try again.'); } else { _showResponseDialog( 'Error', 'Internal Server Error'); } } catch (_) { _showResponseDialog( 'Error', 'Internal Server Error'); } } }, ), ), ); } }, child: Text(AppLocalizations.of(context).proceedButton), ), ], ), ), ), ); } }