import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:kmobile/data/models/user.dart'; import 'package:kmobile/di/injection.dart'; import 'package:flutter/material.dart'; import 'package:kmobile/api/services/cheque_service.dart'; import 'package:kmobile/features/fund_transfer/screens/transaction_pin_screen.dart'; import 'package:kmobile/l10n/app_localizations.dart'; class StopSingleChequeScreen extends StatefulWidget { final User selectedAccount; final String date; final String instrType; final String fromCheque; final String toCheque; const StopSingleChequeScreen( {super.key, required this.selectedAccount, required this.date, required this.instrType, required this.fromCheque, required this.toCheque}); @override State createState() => _StopSingleChequeScreenState(); } class _StopSingleChequeScreenState extends State { final _formKey = GlobalKey(); final _stopFromChequeNoController = TextEditingController(); final _stopIssueDateController = TextEditingController(); final _stopExpiryDateController = TextEditingController(); final _stopAmountController = TextEditingController(); final _chequeService = getIt(); String? _selectedComment; final _otherCommentController = TextEditingController(); bool _showOtherCommentField = false; final List _commentOptions = [ 'Cheque Lost', 'Cheque Stolen', 'Cheque Missing', 'Cheque Damaged', 'Other' ]; String _formatDate(String dateString) { if (dateString.length != 8) { return dateString; // Return as is if not in expected ddmmyyyy format } try { final day = dateString.substring(0, 2); final month = dateString.substring(2, 4); final year = dateString.substring(4, 8); return '$day/$month/$year'; } catch (e) { return dateString; // Return original string on error } } Future _selectDate(TextEditingController controller) async { final DateTime? picked = await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime.now(), lastDate: DateTime(2101), ); if (picked != null) { setState(() { controller.text = '${picked.day.toString().padLeft(2, '0')}/${picked.month.toString().padLeft(2, '0')}/${picked.year}'; }); } } 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).stopSingleChequeTitle), ), body: Padding( padding: const EdgeInsets.all(16.0), child: Form( key: _formKey, child: ListView( children: [ Card( elevation: 0, margin: const EdgeInsets.symmetric(vertical: 8.0), child: ListTile( leading: Image.asset( 'assets/images/logo.png', width: 40, height: 40, ), title: Text(widget.selectedAccount.accountNo!), subtitle: Text(AppLocalizations.of(context).accountNumberLabel), ), ), const SizedBox(height: 24), TextFormField( controller: _stopFromChequeNoController, decoration: InputDecoration( labelText: AppLocalizations.of(context).chequeNumberLabel, border: OutlineInputBorder(), errorMaxLines: 2, ), keyboardType: TextInputType.number, validator: (value) { if (value == null || value.isEmpty) { return AppLocalizations.of(context) .pleaseEnterChequeNumberError; } final chequeNumber = int.tryParse(value); final fromCheque = int.tryParse(widget.fromCheque); final toCheque = int.tryParse(widget.toCheque); if (chequeNumber == null || fromCheque == null || toCheque == null) { return AppLocalizations.of(context) .invalidChequeNumberFormatError; } if (chequeNumber < fromCheque || chequeNumber > toCheque) { return AppLocalizations.of(context).chequeNumberRangeError( widget.fromCheque, widget.toCheque); } return null; }, ), const SizedBox(height: 16), TextFormField( initialValue: widget.instrType, readOnly: true, decoration: InputDecoration( labelText: AppLocalizations.of(context).instrumentTypeLabel, border: const OutlineInputBorder(), ), ), const SizedBox(height: 16), TextFormField( controller: _stopIssueDateController, readOnly: true, onTap: () => _selectDate(_stopIssueDateController), decoration: InputDecoration( labelText: AppLocalizations.of(context).stopIssueDateLabel, border: const OutlineInputBorder(), suffixIcon: IconButton( icon: const Icon(Icons.calendar_today), onPressed: () => _selectDate(_stopIssueDateController), ), ), keyboardType: TextInputType.datetime, ), const SizedBox(height: 16), TextFormField( controller: _stopExpiryDateController, readOnly: true, onTap: () => _selectDate(_stopExpiryDateController), decoration: InputDecoration( labelText: AppLocalizations.of(context).stopExpiryDateLabel, border: const OutlineInputBorder(), suffixIcon: IconButton( icon: const Icon(Icons.calendar_today), onPressed: () => _selectDate(_stopExpiryDateController), ), ), keyboardType: TextInputType.datetime, ), const SizedBox(height: 16), TextFormField( controller: _stopAmountController, decoration: InputDecoration( labelText: AppLocalizations.of(context).stopAmountHint, border: const OutlineInputBorder(), ), keyboardType: TextInputType.number, ), const SizedBox(height: 16), DropdownButtonFormField( value: _selectedComment, items: _commentOptions.map((String value) { return DropdownMenuItem( value: value, child: Text(value), ); }).toList(), onChanged: (newValue) { setState(() { _selectedComment = newValue; _showOtherCommentField = newValue == 'Other'; }); }, decoration: InputDecoration( labelText: AppLocalizations.of(context).stopCommentHint, border: const OutlineInputBorder(), ), ), if (_showOtherCommentField) Padding( padding: const EdgeInsets.only(top: 16.0), child: TextFormField( controller: _otherCommentController, decoration: const InputDecoration( labelText: "Other Reasons :", border: OutlineInputBorder(), ), validator: (value) { return null; }, ), ), const SizedBox(height: 16), TextFormField( initialValue: _formatDate(widget.date), readOnly: true, decoration: InputDecoration( labelText: AppLocalizations.of(context).chequebookIssueDateHint, border: const OutlineInputBorder(), ), ), const SizedBox(height: 32), ElevatedButton( onPressed: () { if (_formKey.currentState!.validate()) { Navigator.push( context, MaterialPageRoute( builder: (context) => TransactionPinScreen( onPinCompleted: (ctx, pin) async { Navigator.pop(context); try { final response = await _chequeService.stopCheque( accountno: widget.selectedAccount.accountNo!, stopFromChequeNo: _stopFromChequeNoController.text, instrType: widget.instrType, stopToChequeNo: _stopFromChequeNoController.text, stopIssueDate: _stopIssueDateController.text, stopExpiryDate: _stopExpiryDateController.text, stopAmount: _stopAmountController.text, stopComment: _selectedComment == 'Other' ? _otherCommentController.text : _selectedComment ?? '', chequeIssueDate: widget.date, tpin: pin, ); if (!mounted) return; final decodedResponse = jsonDecode(response); String responseString = response.toString(); // used as the case only for incorrect TPIN final status = decodedResponse['status']; final message = decodedResponse['message']; final code = decodedResponse['code']; if (status == 'SUCCESS') { _showResponseDialog('Success', message); } if (status == 'ERROR') { String errMessage = "error"; if(code == '0429') { errMessage = 'The selected Cheque is already stopped'; } else if(code == '0748') { errMessage = 'The selected Cheque is already presented'; } _showResponseDialog('Error', errMessage); } 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).stopChequeButton), ), ], ), ), ), ); } }