From dfdc29330957e1ff03b5f1c2318bab940f879fe1 Mon Sep 17 00:00:00 2001 From: asif Date: Thu, 12 Mar 2026 13:44:13 +0530 Subject: [PATCH] Yojna & Cheque done --- .../screens/account_opening_screen.dart | 175 +++++ .../account_opening/screens/fd_screen.dart | 244 +++++++ .../screens/interest_rates_screen.dart | 17 + .../account_opening/screens/loan_screen.dart | 19 + .../account_opening/screens/rd_screen.dart | 19 + .../account_opening/screens/td_screen.dart | 19 + .../card/screens/card_details_screen.dart | 8 +- .../card/screens/card_management_screen.dart | 3 +- .../card_pin_change_details_screen.dart | 36 +- .../screens/cheque_management_screen.dart | 56 +- .../cheque/screens/positive_pay_screen.dart | 334 +++++++++ .../screens/revoke _stop_multiple_screen.dart | 338 +++++++++ .../cheque/screens/revoke_stop_screen.dart | 684 +++++++++--------- .../screens/revoke_stop_single_screen.dart | 304 ++++++++ .../screens/stop_multiple_cheques_screen.dart | 5 + .../screens/stop_single_cheque_screen.dart | 10 +- .../dashboard/screens/dashboard_screen.dart | 9 +- .../screens/enquiry_screen.dart | 0 .../service/screens/service_screen.dart | 93 ++- lib/features/yojna/screens/apy_screen.dart | 113 +++ .../yojna/screens/gov_scheme_screen.dart | 165 +++++ .../yojna/screens/pm_main_screen.dart | 339 +++++++++ .../yojna/screens/pmjjby_enquiry_screen.dart | 218 ++++++ lib/features/yojna/screens/pmjjby_screen.dart | 333 +++++++++ .../yojna/screens/pmsby_enquiry_screen.dart | 218 ++++++ lib/features/yojna/screens/pmsby_screen.dart | 350 +++++++++ 26 files changed, 3687 insertions(+), 422 deletions(-) create mode 100644 lib/features/account_opening/screens/account_opening_screen.dart create mode 100644 lib/features/account_opening/screens/fd_screen.dart create mode 100644 lib/features/account_opening/screens/interest_rates_screen.dart create mode 100644 lib/features/account_opening/screens/loan_screen.dart create mode 100644 lib/features/account_opening/screens/rd_screen.dart create mode 100644 lib/features/account_opening/screens/td_screen.dart create mode 100644 lib/features/cheque/screens/positive_pay_screen.dart create mode 100644 lib/features/cheque/screens/revoke _stop_multiple_screen.dart create mode 100644 lib/features/cheque/screens/revoke_stop_single_screen.dart rename lib/features/{enquiry => service}/screens/enquiry_screen.dart (100%) create mode 100644 lib/features/yojna/screens/apy_screen.dart create mode 100644 lib/features/yojna/screens/gov_scheme_screen.dart create mode 100644 lib/features/yojna/screens/pm_main_screen.dart create mode 100644 lib/features/yojna/screens/pmjjby_enquiry_screen.dart create mode 100644 lib/features/yojna/screens/pmjjby_screen.dart create mode 100644 lib/features/yojna/screens/pmsby_enquiry_screen.dart create mode 100644 lib/features/yojna/screens/pmsby_screen.dart diff --git a/lib/features/account_opening/screens/account_opening_screen.dart b/lib/features/account_opening/screens/account_opening_screen.dart new file mode 100644 index 0000000..4fb0b4d --- /dev/null +++ b/lib/features/account_opening/screens/account_opening_screen.dart @@ -0,0 +1,175 @@ +import 'package:flutter/material.dart'; // Keep if User model is generic +import 'package:kmobile/features/account_opening/screens/fd_screen.dart'; +import 'package:kmobile/features/account_opening/screens/loan_screen.dart'; +import 'package:kmobile/features/account_opening/screens/rd_screen.dart'; +import 'package:kmobile/features/account_opening/screens/td_screen.dart'; +import 'package:material_symbols_icons/material_symbols_icons.dart'; +import '../../../l10n/app_localizations.dart'; + +class AccountOpeningScreen extends StatefulWidget { + const AccountOpeningScreen({ + super.key, + }); + + @override + State createState() => _AccountOpeningScreenState(); +} + +class _AccountOpeningScreenState extends State { + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text( + "Account Opening", + ), + centerTitle: false, + ), + body: Stack( + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded( + child: AccountOpeningCardTile( + icon: Symbols.savings, + label: "Fixed Deposit (FD)", + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const FdScreen( + ), + ), + ); + }, + ), + ), + Expanded( + child: AccountOpeningCardTile( + icon: Symbols.currency_rupee, + label: "Term Deposit", + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const TermDepositScreen() + ), + ); + }, + ), + ), + Expanded( + child: AccountOpeningCardTile( + icon: Symbols.account_balance, + label: AppLocalizations.of(context).recurringDeposit, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const RecurringDepositScreen() ), + ); + }, + ), + ), + Expanded( + child: AccountOpeningCardTile( + icon: Symbols.credit_card, + label: "Request Loan", + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const LoanScreen() + ), + ); + }, + ), + ), + ], + ), + ), + IgnorePointer( + child: Center( + child: Opacity( + opacity: 0.07, + child: ClipOval( + child: Image.asset( + 'assets/images/logo.png', + width: 200, + height: 200, + ), + ), + ), + ), + ), + ], + ), + ); + } +} + +class AccountOpeningCardTile extends StatelessWidget { + final IconData icon; + final String label; + final VoidCallback onTap; + final bool disable; + + const AccountOpeningCardTile({ + super.key, + required this.icon, + required this.label, + required this.onTap, + this.disable = false, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + return Card( + margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12.0), + ), + elevation: 4, + child: InkWell( + onTap: + disable ? null : onTap, + borderRadius: BorderRadius.circular(12.0), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 24.0, horizontal: 16.0), + child: Center( + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + icon, + size: 48, + color: disable + ? theme.disabledColor + : theme.colorScheme.primary, + ), + const SizedBox(height: 12), + Text( + label, + textAlign: TextAlign.center, + style: theme.textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.bold, + color: disable + ? theme.disabledColor + : theme.colorScheme.onSurface, + ), + ), + ], + ), + ), + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/features/account_opening/screens/fd_screen.dart b/lib/features/account_opening/screens/fd_screen.dart new file mode 100644 index 0000000..107ff1d --- /dev/null +++ b/lib/features/account_opening/screens/fd_screen.dart @@ -0,0 +1,244 @@ +import 'package:flutter/material.dart'; +import 'package:kmobile/features/account_opening/screens/interest_rates_screen.dart'; + +class FdScreen extends StatefulWidget { + const FdScreen({super.key}); + + @override + State createState() => _FdScreenState(); +} + +class _FdScreenState extends State { + final TextEditingController _debitAccountController = TextEditingController(); + final TextEditingController _amountController = TextEditingController(); + final TextEditingController _yearsController = TextEditingController(); + final TextEditingController _monthsController = TextEditingController(); + final TextEditingController _daysController = TextEditingController(); + + String _rateOfInterest = "N/A"; + String _maturityDate = "N/A"; + String _maturityAmount = "N/A"; + + @override + void dispose() { + _debitAccountController.dispose(); + _amountController.dispose(); + _yearsController.dispose(); + _monthsController.dispose(); + _daysController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Fixed Deposit (FD)'), + ), + body: SingleChildScrollView( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Explanation Tile + Card( + margin: const EdgeInsets.only(bottom: 20), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Earn more on your savings with a simple, secure Fixed Deposit.', + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ), + ), + ), + + // Debit Account Number + TextFormField( + controller: _debitAccountController, + decoration: const InputDecoration( + labelText: 'Debit Account Number', + border: OutlineInputBorder(), + ), + keyboardType: TextInputType.number, + ), + const SizedBox(height: 20), + + // Enter Amount + TextFormField( + controller: _amountController, + decoration: const InputDecoration( + labelText: 'Enter Amount', + border: OutlineInputBorder(), + prefixText: '₹ ' + ), + keyboardType: TextInputType.number, + ), + const SizedBox(height: 20), + + // Duration and Interest Rates Link + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Duration', + style: Theme.of(context).textTheme.titleLarge, + ), + GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const InterestRatesScreen()), + ); + }, + child: Text( + 'Interest Rates', + style: Theme.of(context).textTheme.titleSmall + ), + ), + ], + ), + const SizedBox(height: 10), + + // Duration TextBoxes + Row( + children: [ + Expanded( + child: TextFormField( + controller: _yearsController, + decoration: const InputDecoration( + labelText: 'Years', + border: OutlineInputBorder(), + ), + keyboardType: TextInputType.number, + ), + ), + const SizedBox(width: 10), + Expanded( + child: TextFormField( + controller: _monthsController, + decoration: const InputDecoration( + labelText: 'Months', + border: OutlineInputBorder(), + ), + keyboardType: TextInputType.number, + ), + ), + const SizedBox(width: 10), + Expanded( + child: TextFormField( + controller: _daysController, + decoration: const InputDecoration( + labelText: 'Days', + border: OutlineInputBorder(), + ), + keyboardType: TextInputType.number, + ), + ), + ], + ), + const SizedBox(height: 20), + + // Rate of Interest and Maturity Date Display + Row( + children: [ + Expanded( + child: Card( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Rate of Interest', + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 5), + Text( + _rateOfInterest, + style: Theme.of(context).textTheme.headlineSmall, + ), + ], + ), + ), + ), + ), + const SizedBox(width: 10), + Expanded( + child: Card( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Maturity Date', + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 5), + Text( + _maturityDate, + style: Theme.of(context).textTheme.headlineSmall, + ), + ], + ), + ), + ), + ), + ], + ), + const SizedBox(height: 10), + // Maturity Amount Display + Row( + children: [ + Expanded( + child: Card( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Maturity Amount', + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 5), + Text( + _maturityAmount, + style: Theme.of(context).textTheme.headlineSmall, + ), + ], + ), + ), + ), + ), + ], + ), + const SizedBox(height: 30), + + // Proceed Button + Center( + child: ElevatedButton( + onPressed: () { + // TODO: Implement proceed logic + }, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 15), + ), + child: const Text( + 'Proceed', + style: TextStyle(fontSize: 18), + ), + ), + ), + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/features/account_opening/screens/interest_rates_screen.dart b/lib/features/account_opening/screens/interest_rates_screen.dart new file mode 100644 index 0000000..e886578 --- /dev/null +++ b/lib/features/account_opening/screens/interest_rates_screen.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +class InterestRatesScreen extends StatelessWidget { + const InterestRatesScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Interest Rates'), + ), + body: const Center( + child: Text('This page will display a table of interest rates.'), + ), + ); + } +} \ No newline at end of file diff --git a/lib/features/account_opening/screens/loan_screen.dart b/lib/features/account_opening/screens/loan_screen.dart new file mode 100644 index 0000000..6b47f53 --- /dev/null +++ b/lib/features/account_opening/screens/loan_screen.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +class LoanScreen extends StatelessWidget { + const LoanScreen({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Request Loan"), + ), + body: const Center( + child: Text("Loan Account"), + ), + ); + } +} diff --git a/lib/features/account_opening/screens/rd_screen.dart b/lib/features/account_opening/screens/rd_screen.dart new file mode 100644 index 0000000..4febb94 --- /dev/null +++ b/lib/features/account_opening/screens/rd_screen.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +class RecurringDepositScreen extends StatelessWidget { + const RecurringDepositScreen({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Recurring Deposit"), + ), + body: const Center( + child: Text("Recurring Deposit"), + ), + ); + } +} diff --git a/lib/features/account_opening/screens/td_screen.dart b/lib/features/account_opening/screens/td_screen.dart new file mode 100644 index 0000000..57a8b25 --- /dev/null +++ b/lib/features/account_opening/screens/td_screen.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +class TermDepositScreen extends StatelessWidget { + const TermDepositScreen({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Term Deposit (TD)"), + ), + body: const Center( + child: Text("Term Deposit (TD)"), + ), + ); + } +} \ No newline at end of file diff --git a/lib/features/card/screens/card_details_screen.dart b/lib/features/card/screens/card_details_screen.dart index d3583b2..2275154 100644 --- a/lib/features/card/screens/card_details_screen.dart +++ b/lib/features/card/screens/card_details_screen.dart @@ -90,12 +90,12 @@ class CardTile extends StatelessWidget { const Text( "Kangra Central Co-operative Bank", style: TextStyle( - color: Colors.white, - fontSize: 15.5, + color: Color.fromARGB(255, 238, 237, 237), + fontSize: 15, fontWeight: FontWeight.bold, ), - overflow: TextOverflow.ellipsis, - maxLines: 1, + overflow: TextOverflow.visible, + maxLines: 2, ), ], ), diff --git a/lib/features/card/screens/card_management_screen.dart b/lib/features/card/screens/card_management_screen.dart index 1243f84..6a9962a 100644 --- a/lib/features/card/screens/card_management_screen.dart +++ b/lib/features/card/screens/card_management_screen.dart @@ -19,7 +19,6 @@ class _CardManagementScreen extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - automaticallyImplyLeading: false, title: Text( AppLocalizations.of(context).cardManagement, ), @@ -61,7 +60,7 @@ class _CardManagementScreen extends State { ), ); }, - disabled: true, + disabled: false, ), Divider(height: 1, color: Theme.of(context).dividerColor), CardManagementTile( diff --git a/lib/features/card/screens/card_pin_change_details_screen.dart b/lib/features/card/screens/card_pin_change_details_screen.dart index d5a196a..5145fcc 100644 --- a/lib/features/card/screens/card_pin_change_details_screen.dart +++ b/lib/features/card/screens/card_pin_change_details_screen.dart @@ -45,7 +45,7 @@ class _CardPinChangeDetailsScreen extends State { return Scaffold( appBar: AppBar( title: Text( - AppLocalizations.of(context).cardDetails, + AppLocalizations.of(context).changeCardPin, ), centerTitle: false, ), @@ -66,12 +66,8 @@ class _CardPinChangeDetailsScreen extends State { isDense: true, filled: true, fillColor: Theme.of(context).scaffoldBackgroundColor, - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.black), - ), - focusedBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.black, width: 2), - ), + enabledBorder: const OutlineInputBorder(), + focusedBorder: const OutlineInputBorder(), ), keyboardType: TextInputType.number, textInputAction: TextInputAction.next, @@ -92,13 +88,8 @@ class _CardPinChangeDetailsScreen extends State { filled: true, fillColor: Theme.of(context).scaffoldBackgroundColor, - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.black), - ), - focusedBorder: const OutlineInputBorder( - borderSide: - BorderSide(color: Colors.black, width: 2), - ), + enabledBorder: const OutlineInputBorder(), + focusedBorder: const OutlineInputBorder(), ), keyboardType: TextInputType.number, textInputAction: TextInputAction.next, @@ -123,13 +114,8 @@ class _CardPinChangeDetailsScreen extends State { filled: true, fillColor: Theme.of(context).scaffoldBackgroundColor, - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.black), - ), - focusedBorder: const OutlineInputBorder( - borderSide: - BorderSide(color: Colors.black, width: 2), - ), + enabledBorder: const OutlineInputBorder(), + focusedBorder: const OutlineInputBorder(), ), validator: (value) => value != null && value.isNotEmpty @@ -149,12 +135,8 @@ class _CardPinChangeDetailsScreen extends State { isDense: true, filled: true, fillColor: Theme.of(context).scaffoldBackgroundColor, - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.black), - ), - focusedBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.black, width: 2), - ), + enabledBorder: const OutlineInputBorder(), + focusedBorder: const OutlineInputBorder(), ), textInputAction: TextInputAction.done, keyboardType: TextInputType.phone, diff --git a/lib/features/cheque/screens/cheque_management_screen.dart b/lib/features/cheque/screens/cheque_management_screen.dart index 1cb5267..ada04a2 100644 --- a/lib/features/cheque/screens/cheque_management_screen.dart +++ b/lib/features/cheque/screens/cheque_management_screen.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:kmobile/data/models/user.dart'; import 'package:kmobile/features/cheque/screens/cheque_enquiry_screen.dart'; +import 'package:kmobile/features/cheque/screens/positive_pay_screen.dart'; +import 'package:kmobile/features/cheque/screens/revoke_stop_screen.dart'; import 'package:kmobile/features/cheque/screens/stop_cheque_screen.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart'; import '../../../l10n/app_localizations.dart'; @@ -42,8 +44,6 @@ class _ChequeManagementScreen extends State { child: ChequeManagementCardTile( icon: Symbols.payments, label: AppLocalizations.of(context).chequeEnquiryTitle, - subtitle: - AppLocalizations.of(context).chequeEnquirySubtitle, onTap: () { Navigator.push( context, @@ -61,7 +61,6 @@ class _ChequeManagementScreen extends State { child: ChequeManagementCardTile( icon: Symbols.block_sharp, label: AppLocalizations.of(context).stopCheque, - subtitle: AppLocalizations.of(context).stopChequeSubtitle, onTap: () { Navigator.push( context, @@ -75,6 +74,40 @@ class _ChequeManagementScreen extends State { }, ), ), + Expanded( + child: ChequeManagementCardTile( + icon: Symbols.stop_circle, + label: AppLocalizations.of(context).revokeStop, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => RevokeStopChequeScreen( + users: users, + selectedIndex: selectedAccountIndex, + ), + ), + ); + }, + ), + ), + Expanded( + child: ChequeManagementCardTile( + icon: Symbols.check_circle, // Using check_circle for Positive Pay + label: AppLocalizations.of(context).positivePayTitle, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => PositivePayScreen( + users: users, + selectedIndex: selectedAccountIndex, + ), + ), + ); + }, + ), + ), ], ), ), @@ -101,7 +134,6 @@ class _ChequeManagementScreen extends State { class ChequeManagementCardTile extends StatelessWidget { final IconData icon; final String label; - final String? subtitle; final VoidCallback onTap; final bool disable; @@ -109,7 +141,6 @@ class ChequeManagementCardTile extends StatelessWidget { super.key, required this.icon, required this.label, - this.subtitle, required this.onTap, this.disable = false, }); @@ -152,19 +183,6 @@ class ChequeManagementCardTile extends StatelessWidget { : theme.colorScheme.onSurface, ), ), - if (subtitle != null) - Padding( - padding: const EdgeInsets.only(top: 8.0), - child: Text( - subtitle!, - textAlign: TextAlign.center, - style: theme.textTheme.bodyMedium?.copyWith( - color: disable - ? theme.disabledColor - : theme.colorScheme.onSurfaceVariant, - ), - ), - ), ], ), ), @@ -173,4 +191,4 @@ class ChequeManagementCardTile extends StatelessWidget { ), ); } -} +} \ No newline at end of file diff --git a/lib/features/cheque/screens/positive_pay_screen.dart b/lib/features/cheque/screens/positive_pay_screen.dart new file mode 100644 index 0000000..0f8472b --- /dev/null +++ b/lib/features/cheque/screens/positive_pay_screen.dart @@ -0,0 +1,334 @@ +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), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/features/cheque/screens/revoke _stop_multiple_screen.dart b/lib/features/cheque/screens/revoke _stop_multiple_screen.dart new file mode 100644 index 0000000..a70f45f --- /dev/null +++ b/lib/features/cheque/screens/revoke _stop_multiple_screen.dart @@ -0,0 +1,338 @@ +import 'dart:convert'; + +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 RevokeStopMultipleChequesScreen extends StatefulWidget { + final User selectedAccount; + final String date; + final String instrType; + final String fromCheque; + final String toCheque; + + const RevokeStopMultipleChequesScreen( + {super.key, + required this.selectedAccount, + required this.date, + required this.instrType, + required this.fromCheque, + required this.toCheque}); + + @override + State createState() => + _RevokeStopMultipleChequesScreenState(); +} + +class _RevokeStopMultipleChequesScreenState extends State { + final _formKey = GlobalKey(); + final _stopFromChequeNoController = TextEditingController(); + final _stopToChequeNoController = 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 Found', + 'Cheque Fixed', + 'Other' + ]; + + 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: const Text('Close'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(AppLocalizations.of(context).revokeMultipleStops), + ), + 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).accountNumberTitle), + ), + ), + const SizedBox(height: 24), + TextFormField( + controller: _stopFromChequeNoController, + decoration: InputDecoration( + labelText: AppLocalizations.of(context).fromChequeNumberHint, + border: const 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( + controller: _stopToChequeNoController, + decoration: InputDecoration( + labelText: AppLocalizations.of(context).toChequeNumberHint, + border: const 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).revokeIssueDate, + 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).revokeExpiryDate, + 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).revokeAmount, + prefixText: '₹ ', + 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).revokeComment, + 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: 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.revokeStop( + accountno: widget.selectedAccount.accountNo!, + removeFromChequeNo: + _stopFromChequeNoController.text, + instrType: widget.instrType, + removeToChequeNo: + _stopToChequeNoController.text, + removeIssueDate: _stopIssueDateController.text, + removeExpiryDate: _stopExpiryDateController.text, + removeAmount: _stopAmountController.text, + removeComment: _selectedComment == 'Other' + ? _otherCommentController.text + : _selectedComment ?? '', + 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 == '0172') { + errMessage = 'The selected Cheque is not 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 Exception 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).revokeStopButton), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/features/cheque/screens/revoke_stop_screen.dart b/lib/features/cheque/screens/revoke_stop_screen.dart index 6f61929..56981d5 100644 --- a/lib/features/cheque/screens/revoke_stop_screen.dart +++ b/lib/features/cheque/screens/revoke_stop_screen.dart @@ -1,351 +1,361 @@ -// 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/cheque/screens/revoke_stop_multiple_screen.dart'; -// // import 'package:kmobile/features/cheque/screens/revoke_stop_single_screen.dart'; -// import 'package:kmobile/l10n/app_localizations.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/cheque/screens/revoke%20_stop_multiple_screen.dart'; +import 'package:kmobile/features/cheque/screens/revoke_stop_single_screen.dart'; +import 'package:kmobile/l10n/app_localizations.dart'; -// class RevokeStopChequeScreen extends StatefulWidget { -// final List users; -// final int selectedIndex; +class RevokeStopChequeScreen extends StatefulWidget { + final List users; + final int selectedIndex; -// const RevokeStopChequeScreen( -// { -// super.key, -// required this.users, -// required this.selectedIndex, -// }); + const RevokeStopChequeScreen( + { + super.key, + required this.users, + required this.selectedIndex, + }); -// @override -// State createState() => _RevokeStopChequeScreenState(); -// } + @override + State createState() => _RevokeStopChequeScreenState(); +} -// class _RevokeStopChequeScreenState extends State { -// User? _selectedAccount; -// var service = getIt(); -// bool _isLoading = true; -// List _stCheques = []; -// List _filteredUsers = []; +class _RevokeStopChequeScreenState extends State { + User? _selectedAccount; + var service = getIt(); + bool _isLoading = true; + List _stCheques = []; + List _filteredUsers = []; + String? _ciFromCheque; + String? _ciToCheque; -// @override -// void initState() { -// super.initState(); -// _filteredUsers = widget.users -// .where((user) => ['SA', 'SB', 'CA', 'CC'].contains(user.accountType)) -// .toList(); + @override + void initState() { + super.initState(); + _filteredUsers = widget.users + .where((user) => ['SA', 'SB', 'CA', 'CC'].contains(user.accountType)) + .toList(); -// 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; -// } -// } + 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; + } + } -// _loadCheques(); -// } + _loadCheques(); + } -// Future _loadCheques() async { -// if (_selectedAccount == null) { -// setState(() { -// _isLoading = false; -// _stCheques = []; -// }); -// return; -// } -// setState(() { -// _isLoading = true; -// }); + Future _loadCheques() async { + if (_selectedAccount == null) { + setState(() { + _isLoading = false; + _stCheques = []; + }); + return; + } + setState(() { + _isLoading = true; + }); -// 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'; -// } + 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 service.ChequeEnquiry( -// accountNumber: _selectedAccount!.accountNo!, instrType: instrType); -// final stCheques = data.where((cheque) => cheque.type == 'ST').toList(); -// setState(() { -// _stCheques = stCheques; -// _isLoading = false; -// }); -// } catch (e) { -// setState(() { -// _isLoading = false; -// _stCheques = []; -// }); -// ScaffoldMessenger.of(context).showSnackBar( -// SnackBar( -// content: Text('Failed to fetch cheque status: ${e.toString()}'), -// ), -// ); -// } -// } + try { + final data = await service.ChequeEnquiry( + accountNumber: _selectedAccount!.accountNo!, instrType: instrType); + final stCheques = data.where((cheque) => cheque.type == 'ST').toList(); + final ciCheque = data.where((cheque) => cheque.type == 'CI').toList(); + setState(() { + _stCheques = stCheques; + if (ciCheque.isNotEmpty) { + _ciFromCheque = ciCheque.first.fromCheque; + _ciToCheque = ciCheque.first.toCheque; + } else { + _ciFromCheque = null; + _ciToCheque = null; + } + _isLoading = false; + }); + } catch (e) { + setState(() { + _isLoading = false; + _stCheques = []; + }); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Failed to fetch cheque status: ${e.toString()}'), + ), + ); + } + } -// String _getAccountTypeDisplayName(String accountType) { -// switch (accountType.toLowerCase()) { -// case 'sa': -// return AppLocalizations.of(context).savingsAccount; -// case 'sb': -// return AppLocalizations.of(context).savingsAccount; -// case 'ca': -// return "Current Account"; -// case 'cc': -// return "Cash Credit Account"; -// default: -// return accountType; -// } -// } + String _getAccountTypeDisplayName(String accountType) { + switch (accountType.toLowerCase()) { + case 'sa': + return AppLocalizations.of(context).savingsAccount; + case 'sb': + return AppLocalizations.of(context).savingsAccount; + case 'ca': + return "Current Account"; + case 'cc': + return "Cash Credit Account"; + default: + return accountType; + } + } -// @override -// Widget build(BuildContext context) { -// return Scaffold( -// appBar: AppBar( -// title: Text(AppLocalizations.of(context).revokeStop), -// centerTitle: false, -// ), -// body: Stack( -// children: [ -// Padding( -// padding: const EdgeInsets.all(16.0), -// child: Column(children: [ -// Card( -// elevation: 4, -// margin: const EdgeInsets.symmetric(vertical: 8.0), -// child: Padding( -// padding: const EdgeInsets.all(16.0), -// child: Row( -// mainAxisAlignment: MainAxisAlignment.spaceBetween, -// children: [ -// Text( -// AppLocalizations.of(context).accountNumber, -// style: const TextStyle( -// fontWeight: FontWeight.bold, fontSize: 18), -// ), -// const SizedBox(width: 16), -// if (_selectedAccount != null) -// Expanded( -// child: DropdownButton( -// value: _selectedAccount, -// onChanged: (User? newUser) { -// if (newUser != null) { -// setState(() { -// _selectedAccount = newUser; -// _loadCheques(); -// }); -// } -// }, -// items: _filteredUsers.map((user) { -// return DropdownMenuItem( -// value: user, -// child: Text(user.accountNo.toString()), -// ); -// }).toList(), -// ), -// ) -// else -// Text(AppLocalizations.of(context).noAccountsFound), -// ], -// ), -// ), -// ), -// const SizedBox(height: 20), -// Row( -// children: [ -// Expanded( -// child: Card( -// color: Theme.of(context).colorScheme.primaryContainer, -// elevation: 4, -// child: InkWell( -// onTap: () { -// if (_selectedAccount != null && -// _stCheques.isNotEmpty) { -// Navigator.push( -// context, -// MaterialPageRoute( -// builder: (context) => -// RevokeStopSingleChequeScreen( -// selectedAccount: _selectedAccount!, -// date: _stCheques.first.Date!, -// instrType: _stCheques.first.InstrType!, -// fromCheque: _stCheques.first.fromCheque!, -// toCheque: _stCheques.first.toCheque!, -// ), -// ), -// ); -// } else { -// ScaffoldMessenger.of(context).showSnackBar( -// const SnackBar( -// content: Text("No stopped cheques present"), -// ), -// ); -// } -// }, -// child: Padding( -// padding: const EdgeInsets.all(16.0), -// child: Center( -// child: Text( -// AppLocalizations.of(context).revokeSingleStopTitle, -// textAlign: TextAlign.center, -// style: TextStyle( -// fontSize: 16, -// fontWeight: FontWeight.bold, -// color: Theme.of(context) -// .colorScheme -// .onPrimaryContainer, -// ), -// ), -// ), -// ), -// ), -// ), -// ), -// const SizedBox(width: 10), -// Expanded( -// child: Card( -// color: Theme.of(context).colorScheme.primaryContainer, -// elevation: 4, -// child: InkWell( -// onTap: () { -// if (_selectedAccount != null && -// _stCheques.isNotEmpty) { -// Navigator.push( -// context, -// MaterialPageRoute( -// builder: (context) => -// RevokeStopMultipleChequesScreen( -// selectedAccount: _selectedAccount!, -// date: _stCheques.first.Date!, -// instrType: _stCheques.first.InstrType!, -// fromCheque: _stCheques.first.fromCheque!, -// toCheque: _stCheques.first.toCheque!, -// ), -// ), -// ); -// } else { -// ScaffoldMessenger.of(context).showSnackBar( -// SnackBar( -// content: Text(AppLocalizations.of(context) -// .pleaseSelectAccountFirst), -// ), -// ); -// } -// }, -// child: Padding( -// padding: const EdgeInsets.all(16.0), -// child: Center( -// child: Text( -// AppLocalizations.of(context).revokeMultipleStops, -// textAlign: TextAlign.center, -// style: TextStyle( -// fontSize: 16, -// fontWeight: FontWeight.bold, -// color: Theme.of(context) -// .colorScheme -// .onSecondaryContainer, -// ), -// ), -// ), -// ), -// ), -// ), -// ), -// ], -// ), -// const SizedBox(height: 20), -// Expanded( -// child: _isLoading -// ? const Center(child: CircularProgressIndicator()) -// : _stCheques.isEmpty -// ? Center( -// child: Text(AppLocalizations.of(context) -// .noChequeIssuedStatus)) -// : ListView.builder( -// itemCount: _stCheques.length, -// itemBuilder: (context, index) { -// return _buildSTTile(context, _stCheques[index]); -// }, -// ), -// ), -// ]), -// ), -// IgnorePointer( -// child: Center( -// child: Opacity( -// opacity: 0.07, // Reduced opacity -// child: ClipOval( -// child: Image.asset( -// 'assets/images/logo.png', -// width: 200, // Adjust size as needed -// height: 200, // Adjust size as needed -// ), -// ), -// ), -// ), -// ), -// ], -// ), -// ); -// } + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(AppLocalizations.of(context).revokeStop), + centerTitle: false, + ), + body: Stack( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Column(children: [ + Card( + elevation: 4, + margin: const EdgeInsets.symmetric(vertical: 8.0), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + AppLocalizations.of(context).accountNumber, + style: const TextStyle( + fontWeight: FontWeight.bold, fontSize: 18), + ), + const SizedBox(width: 16), + if (_selectedAccount != null) + Expanded( + child: DropdownButton( + value: _selectedAccount, + onChanged: (User? newUser) { + if (newUser != null) { + setState(() { + _selectedAccount = newUser; + _loadCheques(); + }); + } + }, + items: _filteredUsers.map((user) { + return DropdownMenuItem( + value: user, + child: Text(user.accountNo.toString()), + ); + }).toList(), + ), + ) + else + Text(AppLocalizations.of(context).noAccountsFound), + ], + ), + ), + ), + const SizedBox(height: 20), + Row( + children: [ + Expanded( + child: Card( + color: Theme.of(context).colorScheme.primaryContainer, + elevation: 4, + child: InkWell( + onTap: () { + if (_selectedAccount != null && + _stCheques.isNotEmpty) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + RevokeStopSingleChequeScreen( + selectedAccount: _selectedAccount!, + date: _stCheques.first.Date!, + instrType: _stCheques.first.InstrType!, + fromCheque: _ciFromCheque ?? _stCheques.first.fromCheque!, + toCheque: _ciToCheque ?? _stCheques.first.toCheque!, + ), + ), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text("No stopped cheques present"), + ), + ); + } + }, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Center( + child: Text( + AppLocalizations.of(context).revokeSingleStopTitle, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Theme.of(context) + .colorScheme + .onPrimaryContainer, + ), + ), + ), + ), + ), + ), + ), + const SizedBox(width: 10), + Expanded( + child: Card( + color: Theme.of(context).colorScheme.primaryContainer, + elevation: 4, + child: InkWell( + onTap: () { + if (_selectedAccount != null && + _stCheques.isNotEmpty) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + RevokeStopMultipleChequesScreen( + selectedAccount: _selectedAccount!, + date: _stCheques.first.Date!, + instrType: _stCheques.first.InstrType!, + fromCheque: _ciFromCheque ?? _stCheques.first.fromCheque!, + toCheque: _ciToCheque ?? _stCheques.first.toCheque!, + ), + ), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(AppLocalizations.of(context) + .pleaseSelectAccountFirst), + ), + ); + } + }, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Center( + child: Text( + AppLocalizations.of(context).revokeMultipleStops, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Theme.of(context) + .colorScheme + .onSecondaryContainer, + ), + ), + ), + ), + ), + ), + ), + ], + ), + const SizedBox(height: 20), + Expanded( + child: _isLoading + ? const Center(child: CircularProgressIndicator()) + : _stCheques.isEmpty + ? Center( + child: Text(AppLocalizations.of(context) + .noChequeIssuedStatus)) + : ListView.builder( + itemCount: _stCheques.length, + itemBuilder: (context, index) { + return _buildSTTile(context, _stCheques[index]); + }, + ), + ), + ]), + ), + IgnorePointer( + child: Center( + child: Opacity( + opacity: 0.07, // Reduced opacity + child: ClipOval( + child: Image.asset( + 'assets/images/logo.png', + width: 200, // Adjust size as needed + height: 200, // Adjust size as needed + ), + ), + ), + ), + ), + ], + ), + ); + } -// Widget _buildSTTile(BuildContext context, Cheque cheque) { -// return Card( -// margin: const EdgeInsets.symmetric( -// vertical: 8.0, -// ), -// child: Padding( -// padding: const EdgeInsets.all(16.0), -// child: Column( -// crossAxisAlignment: CrossAxisAlignment.start, -// children: [ -// Text(AppLocalizations.of(context).stopChequeLabel, -// style: Theme.of(context).textTheme.titleLarge), -// const SizedBox(height: 8), -// _buildInfoRow('From Cheque:', cheque.fromCheque), -// _buildInfoRow('To Cheque:', cheque.toCheque), -// _buildInfoRow('Account Type:', -// _getAccountTypeDisplayName(_selectedAccount!.accountType!)), -// _buildInfoRow('Branch Code:', cheque.branchCode), -// _buildInfoRow('Stop Issue Date:', cheque.stopIssueDate), -// _buildInfoRow('Stop Expiry Date:', cheque.StopExpiryDate), -// _buildInfoRow('Cheques Count:', cheque.Chequescount), -// ], -// ), -// ), -// ); -// } + Widget _buildSTTile(BuildContext context, Cheque cheque) { + return Card( + margin: const EdgeInsets.symmetric( + vertical: 8.0, + ), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(AppLocalizations.of(context).stopChequeLabel, + style: Theme.of(context).textTheme.titleLarge), + const SizedBox(height: 8), + _buildInfoRow('From Cheque:', cheque.fromCheque), + _buildInfoRow('To Cheque:', cheque.toCheque), + _buildInfoRow('Account Type:', + _getAccountTypeDisplayName(_selectedAccount!.accountType!)), + _buildInfoRow('Branch Code:', cheque.branchCode), + _buildInfoRow('Stop Issue Date:', cheque.stopIssueDate), + _buildInfoRow('Stop Expiry Date:', cheque.StopExpiryDate), + _buildInfoRow('Cheques Count:', cheque.Chequescount), + ], + ), + ), + ); + } -// Widget _buildInfoRow(String label, String? value) { -// return Padding( -// padding: const EdgeInsets.symmetric(vertical: 4.0), -// child: Row( -// mainAxisAlignment: MainAxisAlignment.spaceBetween, -// children: [ -// Text(label, style: const TextStyle(fontWeight: FontWeight.bold)), -// Text(value ?? ''), -// ], -// ), -// ); -// } -// } + Widget _buildInfoRow(String label, String? value) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(label, style: const TextStyle(fontWeight: FontWeight.bold)), + Text(value ?? ''), + ], + ), + ); + } +} diff --git a/lib/features/cheque/screens/revoke_stop_single_screen.dart b/lib/features/cheque/screens/revoke_stop_single_screen.dart new file mode 100644 index 0000000..df3c678 --- /dev/null +++ b/lib/features/cheque/screens/revoke_stop_single_screen.dart @@ -0,0 +1,304 @@ +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 RevokeStopSingleChequeScreen extends StatefulWidget { + final User selectedAccount; + final String date; + final String instrType; + final String fromCheque; + final String toCheque; + + const RevokeStopSingleChequeScreen( + {super.key, + required this.selectedAccount, + required this.date, + required this.instrType, + required this.fromCheque, + required this.toCheque}); + + @override + State createState() => _RevokeStopSingleChequeScreenState(); +} + +class _RevokeStopSingleChequeScreenState 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 Found', + 'Cheque Fixed', + 'Other' + ]; + + 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).revokeSingleStopTitle)), + 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: const 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).revokeIssueDate, + 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).revokeExpiryDate, + 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).revokeAmount, + 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).revokeComment, + 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: 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.revokeStop( + accountno: widget.selectedAccount.accountNo!, + removeFromChequeNo: + _stopFromChequeNoController.text, + instrType: widget.instrType, + removeToChequeNo: + _stopFromChequeNoController.text, + removeIssueDate: _stopIssueDateController.text, + removeExpiryDate: _stopExpiryDateController.text, + removeAmount: _stopAmountController.text, + removeComment: _selectedComment == 'Other' + ? _otherCommentController.text + : _selectedComment ?? '', + 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 == '0172') { + errMessage = 'The selected Cheque is not 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).revokeStopButton), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/features/cheque/screens/stop_multiple_cheques_screen.dart b/lib/features/cheque/screens/stop_multiple_cheques_screen.dart index 65d4c3c..15f7b31 100644 --- a/lib/features/cheque/screens/stop_multiple_cheques_screen.dart +++ b/lib/features/cheque/screens/stop_multiple_cheques_screen.dart @@ -309,6 +309,7 @@ class _StopMultipleChequesScreenState extends State { ); 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']; @@ -323,6 +324,10 @@ class _StopMultipleChequesScreenState extends State { } _showResponseDialog('Error', errMessage); } + if(responseString.contains('INCORRECT_TPIN')){ + _showResponseDialog('Invalid TPIN', + 'The TPIN you entered is incorrect. Please try again.'); + } } on Exception catch (e) { try { final errorBodyString = diff --git a/lib/features/cheque/screens/stop_single_cheque_screen.dart b/lib/features/cheque/screens/stop_single_cheque_screen.dart index c2a639f..559a53c 100644 --- a/lib/features/cheque/screens/stop_single_cheque_screen.dart +++ b/lib/features/cheque/screens/stop_single_cheque_screen.dart @@ -278,7 +278,7 @@ class _StopSingleChequeScreenState extends State { ); 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']; @@ -293,12 +293,16 @@ class _StopSingleChequeScreenState extends State { } _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') && + final errorBody = jsonDecode(errorBodyString); + if (errorBody.containsKey('error') && errorBody['error'] == 'INCORRECT_TPIN') { _showResponseDialog('Invalid TPIN', 'The TPIN you entered is incorrect. Please try again.'); diff --git a/lib/features/dashboard/screens/dashboard_screen.dart b/lib/features/dashboard/screens/dashboard_screen.dart index 225f854..40c6f74 100644 --- a/lib/features/dashboard/screens/dashboard_screen.dart +++ b/lib/features/dashboard/screens/dashboard_screen.dart @@ -11,11 +11,11 @@ import 'package:kmobile/features/auth/controllers/auth_state.dart'; import 'package:kmobile/features/cheque/screens/cheque_management_screen.dart'; import 'package:kmobile/features/customer_info/screens/customer_info_screen.dart'; import 'package:kmobile/features/beneficiaries/screens/manage_beneficiaries_screen.dart'; -import 'package:kmobile/features/enquiry/screens/enquiry_screen.dart'; import 'package:kmobile/features/fund_transfer/screens/fund_transfer_screen.dart'; import 'package:kmobile/features/profile/profile_screen.dart'; import 'package:kmobile/features/quick_pay/screens/quick_pay_screen.dart'; import 'package:kmobile/features/service/screens/branch_locator_screen.dart'; +import 'package:kmobile/features/yojna/screens/gov_scheme_screen.dart'; import 'package:kmobile/security/secure_storage.dart'; import 'package:local_auth/local_auth.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart'; @@ -636,13 +636,14 @@ class _DashboardScreenState extends State ManageBeneficiariesScreen( customerName: currAccount.name!))); }, disable: false), - _buildQuickLink(Symbols.support_agent, - AppLocalizations.of(context).contactUs, () { + _buildQuickLink(Symbols.family_group, + AppLocalizations.of(context).governmentSchemes, () { Navigator.push( context, MaterialPageRoute( builder: (context) => - const EnquiryScreen())); + GovSchemeScreen(users: users, + selectedIndex: selectedAccountIndex))); }), _buildQuickLink( Symbols.checkbook, diff --git a/lib/features/enquiry/screens/enquiry_screen.dart b/lib/features/service/screens/enquiry_screen.dart similarity index 100% rename from lib/features/enquiry/screens/enquiry_screen.dart rename to lib/features/service/screens/enquiry_screen.dart diff --git a/lib/features/service/screens/service_screen.dart b/lib/features/service/screens/service_screen.dart index f0b2924..19c0a97 100644 --- a/lib/features/service/screens/service_screen.dart +++ b/lib/features/service/screens/service_screen.dart @@ -1,4 +1,7 @@ +import 'package:kmobile/features/account_opening/screens/account_opening_screen.dart'; +import 'package:kmobile/features/card/screens/card_management_screen.dart'; import 'package:kmobile/features/service/screens/atm_locator_screen.dart'; +import 'package:kmobile/features/service/screens/enquiry_screen.dart'; import '../../../l10n/app_localizations.dart'; import 'package:flutter/material.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart'; @@ -43,7 +46,6 @@ class _ServiceScreen extends State { disabled: false, ), ), - const SizedBox(height: 16), Expanded( child: ServiceManagementTile( icon: Symbols.question_mark, @@ -57,7 +59,6 @@ class _ServiceScreen extends State { disabled: false, ), ), - const SizedBox(height: 16), Expanded( child: ServiceManagementTile( icon: Symbols.location_pin, @@ -71,7 +72,46 @@ class _ServiceScreen extends State { disabled: false, ), ), - // No Spacer() needed here as Expanded children will fill space + // Expanded( + // child: ServiceManagementTile( + // icon: Symbols.box, + // label: "Account Opening", + // onTap: () { + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (context) => const AccountOpeningScreen())); + // }, + // disabled: false, + // ), + // ), + // Expanded( + // child: ServiceManagementTile( + // icon: Symbols.credit_card, + // label: AppLocalizations.of(context).cardManagement, + // onTap: () { + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (context) => const CardManagementScreen())); + // }, + // disabled: false, + // ), + // ), + Expanded( + child: ServiceManagementTile( + icon: Symbols.support_agent, + label: AppLocalizations.of(context).contactUs, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const EnquiryScreen())); + }, + disabled: false, + ), + ), + // No Spacer() needed here as Expanded children will fill space ], ), ), @@ -122,32 +162,33 @@ class ServiceManagementTile extends StatelessWidget { onTap: disabled ? null : onTap, // Disable InkWell if the tile is disabled borderRadius: BorderRadius.circular(12.0), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 24.0, horizontal: 16.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - icon, - size: 48, // Make icon larger - color: - disabled ? theme.disabledColor : theme.colorScheme.primary, + child: Center( + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + icon, + size: 48, // Make icon larger + color: + disabled ? theme.disabledColor : theme.colorScheme.primary, + ), + const SizedBox(height: 12), + Text( + label, + textAlign: TextAlign.center, + style: theme.textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.bold, + color: disabled + ? theme.disabledColor + : theme.colorScheme.onSurface, + ), + ), + ], ), - const SizedBox(height: 12), - Text( - label, - textAlign: TextAlign.center, - style: theme.textTheme.titleLarge?.copyWith( - fontWeight: FontWeight.bold, - color: disabled - ? theme.disabledColor - : theme.colorScheme.onSurface, - ), - ), - ], + ), ), ), - ), ); } } diff --git a/lib/features/yojna/screens/apy_screen.dart b/lib/features/yojna/screens/apy_screen.dart new file mode 100644 index 0000000..4c81836 --- /dev/null +++ b/lib/features/yojna/screens/apy_screen.dart @@ -0,0 +1,113 @@ +import 'package:flutter/material.dart'; +import 'package:kmobile/data/models/user.dart'; +import 'package:kmobile/l10n/app_localizations.dart'; + +class APYScreen extends StatefulWidget { + final List users; + final int selectedIndex; + const APYScreen({ + super.key, + required this.users, + required this.selectedIndex, + }); + + @override + State createState() => _APYScreenState(); +} + +class _APYScreenState extends State { + User? _selectedAccount; + List _filteredUsers = []; + + @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; + } + } + } + + @override + Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context); + return Scaffold( + appBar: AppBar( + title: Text(l10n.apyRegistration), + centerTitle: false, + ), + body: SingleChildScrollView( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Card( + elevation: 2, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + l10n.apyDescription, + style: Theme.of(context).textTheme.titleMedium, + ), + ), + ), + const SizedBox(height: 16), + Card( + elevation: 2, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + DropdownButtonFormField( + value: _selectedAccount, + decoration: InputDecoration( + labelText: l10n.accountNumber, + border: const OutlineInputBorder(), + contentPadding: const EdgeInsets.symmetric( + vertical: 20, horizontal: 12), + ), + items: _filteredUsers.map((user) { + return DropdownMenuItem( + value: user, + child: Text(user.accountNo.toString()), + ); + }).toList(), + onChanged: (User? newUser) { + setState(() { + _selectedAccount = newUser; + }); + }, + validator: (value) { + if (value == null) { + return l10n.accountNumberRequired; + } + return null; + }, + ), + ], + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/yojna/screens/gov_scheme_screen.dart b/lib/features/yojna/screens/gov_scheme_screen.dart new file mode 100644 index 0000000..30340f6 --- /dev/null +++ b/lib/features/yojna/screens/gov_scheme_screen.dart @@ -0,0 +1,165 @@ +import 'package:flutter/material.dart'; +import 'package:kmobile/data/models/user.dart'; +import 'package:kmobile/features/yojna/screens/apy_screen.dart'; +import 'package:kmobile/features/yojna/screens/pm_main_screen.dart'; +import '../../../l10n/app_localizations.dart'; + +class GovSchemeScreen extends StatefulWidget { + final List users; + final int selectedIndex; + const GovSchemeScreen({ + super.key, + required this.users, + required this.selectedIndex, + }); + + @override + State createState() => _GovSchemeScreenState(); +} + +class _GovSchemeScreenState extends State { + @override + Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context); + return Scaffold( + appBar: AppBar( + title: Text(l10n.governmentSchemes), + ), + body: Stack( + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded( + child: GovSchemeTile( + logoText: "PMJJBY/PMSBY", + label: l10n.pradhanMantriYojana, + subtitle: l10n.enrollPMJJBYPMSBY, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => PMMainScreen( + users: widget.users, + selectedIndex: widget.selectedIndex, + ), + ), + ); + }, + ), + ), + Expanded( + child: GovSchemeTile( + logoText: "APY", + label: l10n.registerForAtalPensionYojana, + subtitle: l10n.secureYourFutureAPY, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => APYScreen( + users: widget.users, + selectedIndex: widget.selectedIndex, + ), + ), + );// Action for APY will be added later + }, + ), + ), + ], + ), + ), + IgnorePointer( + child: Center( + child: Opacity( + opacity: 0.07, + child: ClipOval( + child: Image.asset( + 'assets/images/logo.png', + width: 200, + height: 200, + ), + ), + ), + ), + ), + ], + ), + ); + } +} + +class GovSchemeTile extends StatelessWidget { + final String logoText; + final String label; + final String? subtitle; + final VoidCallback onTap; + final bool disable; + + const GovSchemeTile({ + super.key, + required this.logoText, + required this.label, + this.subtitle, + required this.onTap, + this.disable = false, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + return Card( + margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12.0), + ), + elevation: 4, + child: InkWell( + onTap: disable ? null : onTap, + borderRadius: BorderRadius.circular(12.0), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 36.0, horizontal: 16.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + logoText, + style: TextStyle( + fontSize: logoText.length > 5 ? 28 : 40, + fontWeight: FontWeight.bold, + color: theme.colorScheme.primary, + ), + ), + const SizedBox(height: 16), + Text( + label, + textAlign: TextAlign.center, + style: theme.textTheme.headlineSmall?.copyWith( + fontWeight: FontWeight.bold, + color: disable + ? theme.disabledColor + : theme.colorScheme.onSurface, + ), + ), + if (subtitle != null) + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + subtitle!, + textAlign: TextAlign.center, + style: theme.textTheme.bodyMedium?.copyWith( + color: disable + ? theme.disabledColor + : theme.colorScheme.onSurfaceVariant, + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/features/yojna/screens/pm_main_screen.dart b/lib/features/yojna/screens/pm_main_screen.dart new file mode 100644 index 0000000..3f5c146 --- /dev/null +++ b/lib/features/yojna/screens/pm_main_screen.dart @@ -0,0 +1,339 @@ +import 'package:flutter/material.dart'; +import 'package:kmobile/api/services/yojna_service.dart'; +import 'package:kmobile/data/models/user.dart'; +import 'package:kmobile/di/injection.dart'; +import 'package:kmobile/features/yojna/screens/pmjjby_screen.dart'; +import 'package:kmobile/features/yojna/screens/pmsby_screen.dart'; +import 'package:kmobile/features/yojna/screens/pmjjby_enquiry_screen.dart'; +import 'package:kmobile/features/yojna/screens/pmsby_enquiry_screen.dart'; +import 'package:kmobile/l10n/app_localizations.dart'; + +class PMMainScreen extends StatefulWidget { + final List users; + final int selectedIndex; + const PMMainScreen({ + super.key, + required this.users, + required this.selectedIndex, + }); + + @override + State createState() => _PMMainScreenState(); +} + +class _PMMainScreenState extends State { + User? _selectedAccount; + List _filteredUsers = []; + String? _selectedScheme; + + List _getSchemes(AppLocalizations l10n) => [ + l10n.pmjjbyFull, + l10n.pmsbyFull, + ]; + + @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; + } + } + } + + @override + void dispose() { + super.dispose(); + } + + Future _handleCreate() async { + final l10n = AppLocalizations.of(context); + if (_selectedAccount == null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(l10n.pleaseSelectAccountNumber)), + ); + return; + } + + if (_selectedScheme == null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(l10n.pleaseSelectSchemeFirst)), + ); + return; + } + + final String schemeCode = (_selectedScheme == l10n.pmjjbyFull) ? '02' : '01'; + + // Show loading + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Row( + children: [ + const CircularProgressIndicator(), + const SizedBox(width: 16), + Text(l10n.fetchingDetails), + ], + ), + duration: const Duration(seconds: 2), + ), + ); + + try { + final response = await getIt().fetchpmydetails( + scheme: schemeCode, + action: 'C', + accountno: _selectedAccount!.accountNo!, + ); + + if (mounted) { + Map? data; + if (response is Map) { + data = response; + } else if (response is List && response.isNotEmpty && response[0] is Map) { + data = response[0] as Map; + } + + if (data != null && data.isNotEmpty) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + if (_selectedScheme == l10n.pmjjbyFull) { + return PMJJBYScreen(initialData: data!); + } else { + return PMSBYScreen(initialData: data!); + } + }, + ), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(l10n.failedToFetchDetails)), + ); + } + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(l10n.genericError(e.toString()))), + ); + } + } + } + + void _handleEnquiry() { + final l10n = AppLocalizations.of(context); + if (_selectedAccount == null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(l10n.pleaseSelectAccountNumber)), + ); + return; + } + + if (_selectedScheme == null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(l10n.pleaseSelectSchemeFirst)), + ); + return; + } + + Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + if (_selectedScheme == l10n.pmjjbyFull) { + return PMJJBYEnquiryScreen(cifNumber: _selectedAccount!.cifNumber); + } else { + return PMSBYEnquiryScreen(cifNumber: _selectedAccount!.cifNumber); + } + }, + ), + ); + } + + @override + Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context); + final schemes = _getSchemes(l10n); + + // Ensure _selectedScheme is valid if it was set in a different language + if (_selectedScheme != null && !schemes.contains(_selectedScheme)) { + // Try to find the corresponding scheme in the new language + // This is a bit tricky, but since we only have two: + // If it doesn't match, it might be from the other language. + // For simplicity, we can just reset it or try to guess. + // Better to use non-localized values for the state, but let's just reset for now if it doesn't match + _selectedScheme = null; + } + + return Scaffold( + appBar: AppBar( + title: Text(l10n.pradhanMantriYojana), + centerTitle: false, + ), + body: SingleChildScrollView( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Card( + elevation: 2, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + l10n.pmjjbyPmsbyDescription, + style: Theme.of(context).textTheme.titleMedium, + ), + ), + ), + const SizedBox(height: 16), + Card( + elevation: 2, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + DropdownButtonFormField( + value: _selectedAccount, + decoration: InputDecoration( + labelText: l10n.accountNumber, + border: const OutlineInputBorder(), + contentPadding: const EdgeInsets.symmetric( + vertical: 20, horizontal: 12), + ), + items: _filteredUsers.map((user) { + return DropdownMenuItem( + value: user, + child: Text(user.accountNo.toString()), + ); + }).toList(), + onChanged: (User? newUser) { + setState(() { + _selectedAccount = newUser; + }); + }, + validator: (value) { + if (value == null) { + return l10n.accountNumberRequired; + } + return null; + }, + ), + const SizedBox(height: 16), + DropdownButtonFormField( + value: _selectedScheme, + isExpanded: true, + isDense: false, + itemHeight: null, + decoration: InputDecoration( + labelText: l10n.selectScheme, + border: const OutlineInputBorder(), + contentPadding: + const EdgeInsets.symmetric(vertical: 12, horizontal: 12), + ), + selectedItemBuilder: (BuildContext context) { + return schemes.map((String scheme) { + return Container( + alignment: Alignment.centerLeft, + child: Text( + scheme, + style: const TextStyle(fontSize: 14), + softWrap: true, + maxLines: 2, + overflow: TextOverflow.visible, + ), + ); + }).toList(); + }, + items: schemes.map((String scheme) { + return DropdownMenuItem( + value: scheme, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: Text( + scheme, + style: const TextStyle(fontSize: 15), + + softWrap: true, + ), + ), + ); + }).toList(), + onChanged: (String? newValue) { + setState(() { + _selectedScheme = newValue; + }); + }, + ), + ], + ), + ), + ), + const SizedBox(height: 24), + Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: _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: Text( + l10n.create, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + ), + ), + const SizedBox(width: 16), + Expanded( + child: ElevatedButton( + onPressed: _handleEnquiry, + 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.enquiry, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + ), + ), + ], + ), + ], + ), + ), + ); + } +} + diff --git a/lib/features/yojna/screens/pmjjby_enquiry_screen.dart b/lib/features/yojna/screens/pmjjby_enquiry_screen.dart new file mode 100644 index 0000000..0f54809 --- /dev/null +++ b/lib/features/yojna/screens/pmjjby_enquiry_screen.dart @@ -0,0 +1,218 @@ +import 'package:flutter/material.dart'; +import 'package:kmobile/api/services/yojna_service.dart'; +import 'package:kmobile/di/injection.dart'; +import 'package:kmobile/l10n/app_localizations.dart'; + +class PMJJBYEnquiryScreen extends StatefulWidget { + final String? cifNumber; + + const PMJJBYEnquiryScreen({ + super.key, + required this.cifNumber, + }); + + @override + State createState() => _PMJJBYEnquiryScreenState(); +} + +class _PMJJBYEnquiryScreenState extends State { + String? _selectedFinancialYear; + bool _isLoading = false; + Map? _enquiryData; + String? _errorMessage; + + final List _financialYears = [ + '2021-2022', + '2022-2023', + '2023-2024', + '2024-2025', + '2025-2026', + ]; + + @override + void initState() { + super.initState(); + _selectedFinancialYear = null; + } + + Future _fetchEnquiryData() async { + final l10n = AppLocalizations.of(context); + if (_selectedFinancialYear == null || widget.cifNumber == null) return; + + setState(() { + _isLoading = true; + _enquiryData = null; + _errorMessage = null; + }); + + final formattedYear = _selectedFinancialYear!.replaceAll('-', ''); + + try { + final response = await getIt().enquiry( + scheme: '02', + action: 'E', + financialyear: formattedYear, + customerno: widget.cifNumber, + ); + + setState(() { + if (response is Map) { + if (response['status'] == 'FAILED') { + _errorMessage = response['message'] ?? l10n.noRecordFound; + } else { + _enquiryData = response; + } + } else if (response is List && response.isNotEmpty) { + _enquiryData = response[0] as Map; + } else { + _errorMessage = l10n.noDataFoundYear; + } + }); + } catch (e) { + setState(() { + _errorMessage = l10n.genericError(e.toString()); + }); + } finally { + setState(() { + _isLoading = false; + }); + } + } + + @override + Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context); + return Scaffold( + appBar: AppBar( + title: Text(l10n.pmjjbyDetails), + ), + body: SingleChildScrollView( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Card( + elevation: 4, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + ListTile( + contentPadding: EdgeInsets.zero, + leading: const Icon(Icons.person, color: Colors.blue), + title: Text(l10n.cifNumber), + subtitle: Text( + widget.cifNumber ?? l10n.notApplicable, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + ), + const SizedBox(height: 16), + DropdownButtonFormField( + value: _selectedFinancialYear, + decoration: InputDecoration( + labelText: l10n.selectFinancialYear, + border: const OutlineInputBorder(), + prefixIcon: const Icon(Icons.calendar_today), + contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + ), + items: _financialYears.map((String year) { + return DropdownMenuItem( + value: year, + child: Text(year), + ); + }).toList(), + onChanged: (String? newValue) { + setState(() { + _selectedFinancialYear = newValue; + }); + _fetchEnquiryData(); + }, + ), + ], + ), + ), + ), + const SizedBox(height: 20), + if (_isLoading) + const Center( + child: Padding( + padding: EdgeInsets.all(20.0), + child: CircularProgressIndicator(), + ), + ) + else if (_errorMessage != null) + Card( + color: Colors.red.shade50, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + _errorMessage!, + style: TextStyle(color: Colors.red.shade700, fontWeight: FontWeight.bold), + //textAlign: Center, + ), + ), + ) + else if (_enquiryData != null) + Card( + elevation: 4, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + l10n.schemeDetails, + style: Theme.of(context).textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.primary, + ), + ), + const Divider(), + _buildDetailRow(l10n.customerName, _enquiryData!['customername']), + _buildDetailRow(l10n.policyNumber, _enquiryData!['policynumber']), + _buildDetailRow(l10n.accountNumber, _enquiryData!['accountno']), + _buildDetailRow(l10n.premiumAmount, _enquiryData!['preimiumamount']), + _buildDetailRow(l10n.nomineeName, _enquiryData!['nomineename']), + _buildDetailRow(l10n.date, _enquiryData!['transactiondate']), + _buildDetailRow(l10n.journalNo, _enquiryData!['journalno']), + ], + ), + ), + ), + ], + ), + ), + ); + } + + Widget _buildDetailRow(String label, dynamic value) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + label, + style: const TextStyle(fontWeight: FontWeight.w500, color: Colors.grey), + ), + Flexible( + child: Text( + value?.toString() ?? AppLocalizations.of(context).notApplicable, + style: const TextStyle(fontWeight: FontWeight.bold), + textAlign: TextAlign.end, + ), + ), + ], + ), + ); + } +} diff --git a/lib/features/yojna/screens/pmjjby_screen.dart b/lib/features/yojna/screens/pmjjby_screen.dart new file mode 100644 index 0000000..6049326 --- /dev/null +++ b/lib/features/yojna/screens/pmjjby_screen.dart @@ -0,0 +1,333 @@ +import 'package:flutter/material.dart'; +import 'package:kmobile/api/services/yojna_service.dart'; +import 'package:kmobile/di/injection.dart'; +import 'package:kmobile/l10n/app_localizations.dart'; + +class PMJJBYScreen extends StatefulWidget { + final Map? initialData; + const PMJJBYScreen({super.key, this.initialData}); + + @override + State createState() => _PMJJBYScreenState(); +} + +class _PMJJBYScreenState extends State { + final _formKey = GlobalKey(); + + // Controllers for all requested fields + late final _aadhaarController = TextEditingController(text: widget.initialData?['aadharno']?.toString()); + late final _accountNoController = TextEditingController(text: widget.initialData?['accountno']?.toString()); + late final _balanceController = TextEditingController(text: widget.initialData?['availablebalance']?.toString()); + late final _countryController = TextEditingController(text: widget.initialData?['country']?.toString() ?? 'IN'); + late final _dobController = TextEditingController(text: widget.initialData?['customerdob']?.toString()); + late final _nameController = TextEditingController(text: widget.initialData?['customername']?.toString()); + late final _customerNoController = TextEditingController(text: widget.initialData?['customerno']?.toString()); + late final _acctOpeningDateController = TextEditingController(text: widget.initialData?['dateofacctopening']?.toString()); + late final _emailController = TextEditingController(text: widget.initialData?['emailid']?.toString()); + late final _financialYearController = TextEditingController(text: widget.initialData?['financialyear']?.toString()); + late final _genderController = TextEditingController(text: widget.initialData?['gender']?.toString()); + late final _ifscController = TextEditingController(text: widget.initialData?['ifsccode']?.toString()); + late final _marriedController = TextEditingController(text: widget.initialData?['married']?.toString()); + late final _mobileController = TextEditingController(text: widget.initialData?['mobileno']?.toString()); + late final _panController = TextEditingController(text: widget.initialData?['pan']?.toString()); + late final _pincodeController = TextEditingController(text: widget.initialData?['pincode']?.toString()); + late final _policyNumberController = TextEditingController(text: widget.initialData?['policynumber']?.toString()); + late final _premiumAmountController = TextEditingController(text: widget.initialData?['premiumamount']?.toString()); + late final _stateController = TextEditingController(text: widget.initialData?['state']?.toString()); + + // Mapping options + final Map _healthStatusOptions = { + '1': 'Excellent', + '2': 'Good', + '3': 'Bad', + }; + + final Map _relationshipOptions = { + '01': 'Self', + '02': 'Wife', + '03': 'Father', + '04': 'Mother', + '05': 'Son', + '06': 'Daughter', + '07': 'Brother', + '08': 'Sister', + '09': 'Father-in-law', + '10': 'Mother-in-law', + '11': 'Grandson', + '12': 'Granddaughter', + '13': 'Grandfather', + '14': 'Grandmother', + '15': 'Brother-in-law', + '16': 'Sister-in-law', + '17': 'Husband', + '18': 'Guardian', + '99': 'Others', + }; + + final Map _minorOptions = { + 'Y': 'Yes', + 'N': 'No', + }; + + final Map _ruralOptions = { + 'R': 'Rural', + 'U': 'Urban', + 'S': 'Semi-Urban', + 'M': 'Metro', + }; + + final _healthStatusController = TextEditingController(); + final _collectionChannelController = TextEditingController(); + final _nomineeNameController = TextEditingController(); + final _nomineeAddressController = TextEditingController(); + final _nomineeRelationshipController = TextEditingController(); + final _nomineeMinorController = TextEditingController(); + final _ruralCategoryController = TextEditingController(); + + @override + void initState() { + super.initState(); + // Initialize dropdown controllers if data exists in initialData + if (widget.initialData != null) { + if (widget.initialData!.containsKey('ruralcategory')) { + _ruralCategoryController.text = widget.initialData!['ruralcategory'].toString(); + } + if (widget.initialData!.containsKey('healthstatus')) { + _healthStatusController.text = widget.initialData!['healthstatus'].toString(); + } + if (widget.initialData!.containsKey('nomineerelationship')) { + _nomineeRelationshipController.text = widget.initialData!['nomineerelationship'].toString(); + } + if (widget.initialData!.containsKey('nomineeminor')) { + _nomineeMinorController.text = widget.initialData!['nomineeminor'].toString(); + } + } + } + + @override + void dispose() { + _aadhaarController.dispose(); + _accountNoController.dispose(); + _balanceController.dispose(); + _countryController.dispose(); + _dobController.dispose(); + _nameController.dispose(); + _customerNoController.dispose(); + _acctOpeningDateController.dispose(); + _emailController.dispose(); + _financialYearController.dispose(); + _genderController.dispose(); + _ifscController.dispose(); + _marriedController.dispose(); + _mobileController.dispose(); + _panController.dispose(); + _pincodeController.dispose(); + _policyNumberController.dispose(); + _premiumAmountController.dispose(); + _stateController.dispose(); + _healthStatusController.dispose(); + _collectionChannelController.dispose(); + _nomineeNameController.dispose(); + _nomineeAddressController.dispose(); + _nomineeRelationshipController.dispose(); + _nomineeMinorController.dispose(); + _ruralCategoryController.dispose(); + super.dispose(); + } + + bool _isFetched(String key) { + return widget.initialData != null && + widget.initialData!.containsKey(key) && + widget.initialData![key]?.toString().isNotEmpty == true; + } + + Future _handleRegister() async { + final l10n = AppLocalizations.of(context); + // Show loading spinner + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => const Center(child: CircularProgressIndicator()), + ); + + try { + final response = await getIt().secondvalidationPMJJBY( + aadharno: _aadhaarController.text, + accountno: _accountNoController.text, + availablebalance: _balanceController.text, + country: _countryController.text, + customerdob: _dobController.text, + customername: _nameController.text, + customerno: _customerNoController.text, + dateofacctopening: _acctOpeningDateController.text, + emailid: _emailController.text, + financialyear: _financialYearController.text, + gender: _genderController.text, + ifsccode: _ifscController.text, + married: _marriedController.text, + mobileno: _mobileController.text, + pan: _panController.text, + pincode: _pincodeController.text, + policynumber: _policyNumberController.text, + premiumamount: _premiumAmountController.text, + state: _stateController.text, + healthstatus: _healthStatusController.text, + collectionchannel: _collectionChannelController.text, + nomineename: _nomineeNameController.text, + nomineeaddress: _nomineeAddressController.text, + nomineerelationship: _nomineeRelationshipController.text, + nomineeminor: _nomineeMinorController.text, + ruralcategory: _ruralCategoryController.text, + ); + String x = response.toString(); + if(x.contains('RECORD ALREADY EXISTS')){ + x= l10n.recordAlreadyExists; + } + + if (mounted) { + // Close loading spinner + Navigator.pop(context); + // Show response dialog + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => AlertDialog( + title: Text(l10n.response), + content: SingleChildScrollView(child: Text(x)), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).popUntil((route) => route.isFirst); + }, + child: Text(l10n.close), + ), + ], + ), + ); + } + } catch (e) { + if (mounted) { + Navigator.pop(context); // Close loading spinner + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(l10n.genericError(e.toString()))), + ); + } + } + } + + @override + Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context); + return Scaffold( + appBar: AppBar( + title: Text(l10n.pmjjbyRegistration), + ), + body: SingleChildScrollView( + padding: const EdgeInsets.all(16.0), + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _buildTextField(_nameController, l10n.customerName, readOnly: _isFetched('customername')), + _buildTextField(_customerNoController, l10n.customerNo, readOnly: _isFetched('customerno')), + _buildTextField(_accountNoController, l10n.accountNumber, keyboardType: TextInputType.number, readOnly: _isFetched('accountno')), + _buildTextField(_balanceController, l10n.availableBalance, keyboardType: TextInputType.number, readOnly: _isFetched('availablebalance')), + _buildTextField(_aadhaarController, l10n.aadhaarNo, keyboardType: TextInputType.number, readOnly: _isFetched('aadharno')), + _buildTextField(_dobController, l10n.customerDobFormat, readOnly: _isFetched('customerdob')), + _buildTextField(_genderController, l10n.gender, readOnly: _isFetched('gender')), + _buildTextField(_marriedController, l10n.marriedYesNo, readOnly: _isFetched('married')), + _buildTextField(_mobileController, l10n.mobileNumber, keyboardType: TextInputType.phone, readOnly: _isFetched('mobileno')), + _buildTextField(_emailController, 'Email ID', keyboardType: TextInputType.emailAddress, readOnly: _isFetched('emailid')), + _buildTextField(_panController, l10n.pan, readOnly: _isFetched('pan')), + _buildTextField(_ifscController, l10n.ifscCode, readOnly: _isFetched('ifsccode')), + _buildTextField(_acctOpeningDateController, l10n.dateOfAcctOpening, readOnly: _isFetched('dateofacctopening')), + _buildTextField(_pincodeController, l10n.pincode, keyboardType: TextInputType.number, readOnly: _isFetched('pincode')), + _buildTextField(_stateController, l10n.state, readOnly: _isFetched('state')), + _buildTextField(_countryController, l10n.country, readOnly: _isFetched('country')), + _buildDropdownField(_ruralCategoryController, l10n.ruralCategory, _ruralOptions, readOnly: _isFetched('ruralcategory')), + const Divider(height: 32), + Text(l10n.policyDetails, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), + const SizedBox(height: 16), + _buildTextField(_policyNumberController, l10n.policyNumber, readOnly: _isFetched('policynumber')), + _buildTextField(_premiumAmountController, l10n.premiumAmount, keyboardType: TextInputType.number, readOnly: _isFetched('premiumamount')), + _buildTextField(_financialYearController, l10n.financialYear, readOnly: _isFetched('financialyear')), + _buildDropdownField(_healthStatusController, l10n.healthStatus, _healthStatusOptions), + _buildTextField(_collectionChannelController, l10n.collectionChannel), + const Divider(height: 32), + Text(l10n.nomineeDetails, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), + const SizedBox(height: 16), + _buildTextField(_nomineeNameController, l10n.nomineeName), + _buildTextField(_nomineeAddressController, l10n.nomineeAddress), + _buildDropdownField(_nomineeRelationshipController, l10n.nomineeRelationship, _relationshipOptions, readOnly: _isFetched('nomineerelationship')), + _buildDropdownField(_nomineeMinorController, l10n.nomineeMinor, _minorOptions, readOnly: _isFetched('nomineeminor')), + const SizedBox(height: 24), + ElevatedButton( + onPressed: _handleRegister, + style: ElevatedButton.styleFrom( + backgroundColor: Theme.of(context).colorScheme.primary, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 16), + 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 options, + {bool readOnly = false}) { + // Determine current value + String? currentValue = options.containsKey(controller.text) ? controller.text : null; + + return Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: DropdownButtonFormField( + value: currentValue, + onChanged: readOnly ? null : (newValue) { + setState(() { + controller.text = newValue ?? ''; + }); + }, + decoration: InputDecoration( + labelText: label, + border: const OutlineInputBorder(), + contentPadding: const EdgeInsets.symmetric(vertical: 16, horizontal: 12), + ), + items: options.entries.map((entry) { + return DropdownMenuItem( + value: entry.key, + child: Text("${entry.key} - ${entry.value}"), + ); + }).toList(), + ), + ); + } + + Widget _buildTextField(TextEditingController controller, String label, {TextInputType keyboardType = TextInputType.text, bool readOnly = false}) { + return Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: TextFormField( + controller: controller, + readOnly: readOnly, + decoration: InputDecoration( + labelText: label, + border: const OutlineInputBorder(), + contentPadding: const EdgeInsets.symmetric(vertical: 16, horizontal: 12), + ), + keyboardType: keyboardType, + ), + ); + } +} diff --git a/lib/features/yojna/screens/pmsby_enquiry_screen.dart b/lib/features/yojna/screens/pmsby_enquiry_screen.dart new file mode 100644 index 0000000..33961cc --- /dev/null +++ b/lib/features/yojna/screens/pmsby_enquiry_screen.dart @@ -0,0 +1,218 @@ +import 'package:flutter/material.dart'; +import 'package:kmobile/api/services/yojna_service.dart'; +import 'package:kmobile/di/injection.dart'; +import 'package:kmobile/l10n/app_localizations.dart'; + +class PMSBYEnquiryScreen extends StatefulWidget { + final String? cifNumber; + + const PMSBYEnquiryScreen({ + super.key, + required this.cifNumber, + }); + + @override + State createState() => _PMSBYEnquiryScreenState(); +} + +class _PMSBYEnquiryScreenState extends State { + String? _selectedFinancialYear; + bool _isLoading = false; + Map? _enquiryData; + String? _errorMessage; + + final List _financialYears = [ + '2021-2022', + '2022-2023', + '2023-2024', + '2024-2025', + '2025-2026', + ]; + + @override + void initState() { + super.initState(); + _selectedFinancialYear = null; + } + + Future _fetchEnquiryData() async { + final l10n = AppLocalizations.of(context); + if (_selectedFinancialYear == null || widget.cifNumber == null) return; + + setState(() { + _isLoading = true; + _enquiryData = null; + _errorMessage = null; + }); + + final formattedYear = _selectedFinancialYear!.replaceAll('-', ''); + + try { + final response = await getIt().enquiry( + scheme: '01', + action: 'E', + financialyear: formattedYear, + customerno: widget.cifNumber, + ); + + setState(() { + if (response is Map) { + if (response['status'] == 'FAILED') { + _errorMessage = response['message'] ?? l10n.noRecordFound; + } else { + _enquiryData = response; + } + } else if (response is List && response.isNotEmpty) { + _enquiryData = response[0] as Map; + } else { + _errorMessage = l10n.noDataFoundYear; + } + }); + } catch (e) { + setState(() { + _errorMessage = l10n.genericError(e.toString()); + }); + } finally { + setState(() { + _isLoading = false; + }); + } + } + + @override + Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context); + return Scaffold( + appBar: AppBar( + title: Text(l10n.pmsbyDetails), + ), + body: SingleChildScrollView( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Card( + elevation: 4, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + ListTile( + contentPadding: EdgeInsets.zero, + leading: const Icon(Icons.person, color: Colors.blue), + title: Text(l10n.cifNumber), + subtitle: Text( + widget.cifNumber ?? l10n.notApplicable, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + ), + const SizedBox(height: 16), + DropdownButtonFormField( + value: _selectedFinancialYear, + decoration: InputDecoration( + labelText: l10n.selectFinancialYear, + border: const OutlineInputBorder(), + prefixIcon: const Icon(Icons.calendar_today), + contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + ), + items: _financialYears.map((String year) { + return DropdownMenuItem( + value: year, + child: Text(year), + ); + }).toList(), + onChanged: (String? newValue) { + setState(() { + _selectedFinancialYear = newValue; + }); + _fetchEnquiryData(); + }, + ), + ], + ), + ), + ), + const SizedBox(height: 20), + if (_isLoading) + const Center( + child: Padding( + padding: EdgeInsets.all(20.0), + child: CircularProgressIndicator(), + ), + ) + else if (_errorMessage != null) + Card( + color: Colors.red.shade50, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + _errorMessage!, + style: TextStyle(color: Colors.red.shade700, fontWeight: FontWeight.bold), + //textAlign: Center, + ), + ), + ) + else if (_enquiryData != null) + Card( + elevation: 4, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + l10n.schemeDetails, + style: Theme.of(context).textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.primary, + ), + ), + const Divider(), + _buildDetailRow(l10n.customerName, _enquiryData!['customername']), + _buildDetailRow(l10n.policyNumber, _enquiryData!['policynumber'] ?? _enquiryData!['policyno']), + _buildDetailRow(l10n.accountNumber, _enquiryData!['accountno']), + _buildDetailRow(l10n.premiumAmount, _enquiryData!['preimiumamount'] ?? _enquiryData!['premiumamount']), + _buildDetailRow(l10n.nomineeName, _enquiryData!['nomineename']), + _buildDetailRow(l10n.date, _enquiryData!['transactiondate']), + _buildDetailRow(l10n.journalNo, _enquiryData!['journalno']), + ], + ), + ), + ), + ], + ), + ), + ); + } + + Widget _buildDetailRow(String label, dynamic value) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + label, + style: const TextStyle(fontWeight: FontWeight.w500, color: Colors.grey), + ), + Flexible( + child: Text( + value?.toString() ?? AppLocalizations.of(context).notApplicable, + style: const TextStyle(fontWeight: FontWeight.bold), + textAlign: TextAlign.end, + ), + ), + ], + ), + ); + } +} diff --git a/lib/features/yojna/screens/pmsby_screen.dart b/lib/features/yojna/screens/pmsby_screen.dart new file mode 100644 index 0000000..23f0023 --- /dev/null +++ b/lib/features/yojna/screens/pmsby_screen.dart @@ -0,0 +1,350 @@ +import 'package:flutter/material.dart'; +import 'package:kmobile/api/services/yojna_service.dart'; +import 'package:kmobile/di/injection.dart'; +import 'package:kmobile/l10n/app_localizations.dart'; + +class PMSBYScreen extends StatefulWidget { + final Map? initialData; + const PMSBYScreen({super.key, this.initialData}); + + @override + State createState() => _PMSBYScreenState(); +} + +class _PMSBYScreenState extends State { + final _formKey = GlobalKey(); + + // Controllers for all requested fields + late final _aadhaarController = TextEditingController(text: widget.initialData?['aadharno']?.toString()); + late final _accountNoController = TextEditingController(text: widget.initialData?['accountno']?.toString()); + late final _balanceController = TextEditingController(text: widget.initialData?['availablebalance']?.toString()); + late final _countryController = TextEditingController(text: widget.initialData?['country']?.toString() ?? 'IN'); + late final _dobController = TextEditingController(text: widget.initialData?['customerdob']?.toString()); + late final _nameController = TextEditingController(text: widget.initialData?['customername']?.toString()); + late final _customerNoController = TextEditingController(text: widget.initialData?['customerno']?.toString()); + late final _acctOpeningDateController = TextEditingController(text: widget.initialData?['dateofacctopening']?.toString()); + late final _emailController = TextEditingController(text: widget.initialData?['emailid']?.toString()); + late final _financialYearController = TextEditingController(text: widget.initialData?['financialyear']?.toString()); + late final _genderController = TextEditingController(text: widget.initialData?['gender']?.toString()); + late final _ifscController = TextEditingController(text: widget.initialData?['ifsccode']?.toString()); + late final _marriedController = TextEditingController(text: widget.initialData?['married']?.toString()); + late final _mobileController = TextEditingController(text: widget.initialData?['mobileno']?.toString()); + late final _panController = TextEditingController(text: widget.initialData?['pan']?.toString()); + late final _pincodeController = TextEditingController(text: widget.initialData?['pincode']?.toString()); + late final _policyNumberController = TextEditingController(text: widget.initialData?['policyno']?.toString()); + late final _premiumAmountController = TextEditingController(text: widget.initialData?['premiumamount']?.toString()); + late final _stateController = TextEditingController(text: widget.initialData?['state']?.toString()); + late final _address1Controller = TextEditingController(text: widget.initialData?['address1']?.toString()); + late final _address2Controller = TextEditingController(text: widget.initialData?['address2']?.toString()); + late final _cityController = TextEditingController(text: widget.initialData?['city']?.toString()); + late final _relationWithNomineeController = TextEditingController(text: widget.initialData?['relationwithnominee']?.toString()); + late final _policyStatusController = TextEditingController(text: widget.initialData?['policystatus']?.toString()); + + // Mapping options + final Map _healthStatusOptions = { + '1': 'Excellent', + '2': 'Good', + '3': 'Bad', + }; + + final Map _relationshipOptions = { + '01': 'Self', + '02': 'Wife', + '03': 'Father', + '04': 'Mother', + '05': 'Son', + '06': 'Daughter', + '07': 'Brother', + '08': 'Sister', + '09': 'Father-in-law', + '10': 'Mother-in-law', + '11': 'Grandson', + '12': 'Granddaughter', + '13': 'Grandfather', + '14': 'Grandmother', + '15': 'Brother-in-law', + '16': 'Sister-in-law', + '17': 'Husband', + '18': 'Guardian', + '99': 'Others', + }; + + final Map _minorOptions = { + 'Y': 'Yes', + 'N': 'No', + }; + + final Map _ruralOptions = { + 'R': 'Rural', + 'U': 'Urban', + 'S': 'Semi-Urban', + 'M': 'Metro', + }; + + final _healthStatusController = TextEditingController(); + final _collectionChannelController = TextEditingController(); + final _nomineeNameController = TextEditingController(); + final _nomineeAddressController = TextEditingController(); + final _nomineeRelationshipController = TextEditingController(); + final _nomineeMinorController = TextEditingController(); + final _ruralCategoryController = TextEditingController(); + + @override + void initState() { + super.initState(); + // Initialize dropdown controllers if data exists in initialData + if (widget.initialData != null) { + if (widget.initialData!.containsKey('ruralcategory')) { + _ruralCategoryController.text = widget.initialData!['ruralcategory'].toString(); + } + if (widget.initialData!.containsKey('healthstatus')) { + _healthStatusController.text = widget.initialData!['healthstatus'].toString(); + } + if (widget.initialData!.containsKey('relationwithnominee')) { + _nomineeRelationshipController.text = widget.initialData!['relationwithnominee'].toString(); + } + if (widget.initialData!.containsKey('nomineeminor')) { + _nomineeMinorController.text = widget.initialData!['nomineeminor'].toString(); + } + } + } + + @override + void dispose() { + _aadhaarController.dispose(); + _accountNoController.dispose(); + _balanceController.dispose(); + _countryController.dispose(); + _dobController.dispose(); + _nameController.dispose(); + _customerNoController.dispose(); + _acctOpeningDateController.dispose(); + _emailController.dispose(); + _financialYearController.dispose(); + _genderController.dispose(); + _ifscController.dispose(); + _marriedController.dispose(); + _mobileController.dispose(); + _panController.dispose(); + _pincodeController.dispose(); + _policyNumberController.dispose(); + _premiumAmountController.dispose(); + _stateController.dispose(); + _address1Controller.dispose(); + _address2Controller.dispose(); + _cityController.dispose(); + _relationWithNomineeController.dispose(); + _policyStatusController.dispose(); + _healthStatusController.dispose(); + _collectionChannelController.dispose(); + _nomineeNameController.dispose(); + _nomineeAddressController.dispose(); + _nomineeRelationshipController.dispose(); + _nomineeMinorController.dispose(); + _ruralCategoryController.dispose(); + super.dispose(); + } + + bool _isFetched(String key) { + return widget.initialData != null && + widget.initialData!.containsKey(key) && + widget.initialData![key]?.toString().isNotEmpty == true; + } + + Future _handleRegister() async { + final l10n = AppLocalizations.of(context); + // Show loading spinner + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => const Center(child: CircularProgressIndicator()), + ); + + try { + final response = await getIt().secondvalidationPMSBY( + aadharno: _aadhaarController.text, + accountno: _accountNoController.text, + address1: _address1Controller.text, + address2: _address2Controller.text, + availablebalance: _balanceController.text, + city: _cityController.text, + country: _countryController.text, + customerdob: _dobController.text, + customername: _nameController.text, + customerno: _customerNoController.text, + emailid: _emailController.text, + financialyear: _financialYearController.text, + gender: _genderController.text, + married: _marriedController.text, + mobileno: _mobileController.text, + pan: _panController.text, + pincode: _pincodeController.text, + policyno: _policyNumberController.text, + premiumamount: _premiumAmountController.text, + state: _stateController.text, + healthstatus: _healthStatusController.text, + nomineename: _nomineeNameController.text, + nomineeadress: _nomineeAddressController.text, + relationwithnominee: _relationWithNomineeController.text, + nomineeminor: _nomineeMinorController.text, + collectionchannel: _collectionChannelController.text, + ruralcategory: _ruralCategoryController.text, + policystatus: _policyStatusController.text, + ); + String x = response.toString(); + if(x.contains('RECORD ALREADY EXISTS')){ + x= l10n.recordAlreadyExists; + } + + if (mounted) { + // Close loading spinner + Navigator.pop(context); + // Show response dialog + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => AlertDialog( + title: Text(l10n.response), + content: SingleChildScrollView(child: Text(x)), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).popUntil((route) => route.isFirst); + }, + child: Text(l10n.close), + ), + ], + ), + ); + } + } catch (e) { + if (mounted) { + Navigator.pop(context); // Close loading spinner + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(l10n.genericError(e.toString()))), + ); + } + } + } + + @override + Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context); + return Scaffold( + appBar: AppBar( + title: Text(l10n.pmsbyRegistration), + ), + body: SingleChildScrollView( + padding: const EdgeInsets.all(16.0), + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _buildTextField(_nameController, l10n.customerName, readOnly: _isFetched('customername')), + _buildTextField(_customerNoController, l10n.customerNo, readOnly: _isFetched('customerno')), + _buildTextField(_accountNoController, l10n.accountNumber, keyboardType: TextInputType.number, readOnly: _isFetched('accountno')), + _buildTextField(_balanceController, l10n.availableBalance, keyboardType: TextInputType.number, readOnly: _isFetched('availablebalance')), + _buildTextField(_aadhaarController, l10n.aadhaarNo, keyboardType: TextInputType.number, readOnly: _isFetched('aadharno')), + _buildTextField(_dobController, l10n.customerDobFormat, readOnly: _isFetched('customerdob')), + _buildTextField(_genderController, l10n.gender, readOnly: _isFetched('gender')), + _buildTextField(_marriedController, l10n.marriedYesNo, readOnly: _isFetched('married')), + _buildTextField(_mobileController, l10n.mobileNumber, keyboardType: TextInputType.phone, readOnly: _isFetched('mobileno')), + _buildTextField(_emailController, 'Email ID', keyboardType: TextInputType.emailAddress, readOnly: _isFetched('emailid')), + _buildTextField(_address1Controller, l10n.address1, readOnly: _isFetched('address1')), + _buildTextField(_address2Controller, l10n.address2, readOnly: _isFetched('address2')), + _buildTextField(_cityController, l10n.city, readOnly: _isFetched('city')), + _buildTextField(_panController, l10n.pan, readOnly: _isFetched('pan')), + _buildTextField(_ifscController, l10n.ifscCode, readOnly: _isFetched('ifsccode')), + _buildTextField(_acctOpeningDateController, l10n.dateOfAcctOpening, readOnly: _isFetched('dateofacctopening')), + _buildTextField(_pincodeController, l10n.pincode, keyboardType: TextInputType.number, readOnly: _isFetched('pincode')), + _buildTextField(_stateController, l10n.state, readOnly: _isFetched('state')), + _buildTextField(_countryController, l10n.country, readOnly: _isFetched('country')), + _buildDropdownField(_ruralCategoryController, l10n.ruralCategory, _ruralOptions, readOnly: _isFetched('ruralcategory')), + const Divider(height: 32), + Text(l10n.policyDetails, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), + const SizedBox(height: 16), + _buildTextField(_policyNumberController, l10n.policyNumber, readOnly: _isFetched('policyno')), + _buildTextField(_premiumAmountController, l10n.premiumAmount, keyboardType: TextInputType.number, readOnly: _isFetched('premiumamount')), + _buildTextField(_financialYearController, l10n.financialYear, readOnly: _isFetched('financialyear')), + _buildTextField(_policyStatusController, l10n.policyStatus, readOnly: _isFetched('policystatus')), + _buildDropdownField(_healthStatusController, l10n.healthStatus, _healthStatusOptions), + _buildTextField(_collectionChannelController, l10n.collectionChannel), + const Divider(height: 32), + Text(l10n.nomineeDetails, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), + const SizedBox(height: 16), + _buildTextField(_nomineeNameController, l10n.nomineeName), + _buildTextField(_nomineeAddressController, l10n.nomineeAddress), + _buildDropdownField(_relationWithNomineeController, l10n.relationWithNominee, _relationshipOptions, readOnly: _isFetched('relationwithnominee')), + _buildTextField(_nomineeRelationshipController, l10n.nomineeRelationship), + _buildDropdownField(_nomineeMinorController, l10n.nomineeMinor, _minorOptions, readOnly: _isFetched('nomineeminor')), + const SizedBox(height: 24), + ElevatedButton( + onPressed: _handleRegister, + style: ElevatedButton.styleFrom( + backgroundColor: Theme.of(context).colorScheme.primary, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 16), + 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 options, + {bool readOnly = false}) { + // Determine current value + String? currentValue = options.containsKey(controller.text) ? controller.text : null; + + return Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: DropdownButtonFormField( + value: currentValue, + onChanged: readOnly ? null : (newValue) { + setState(() { + controller.text = newValue ?? ''; + }); + }, + decoration: InputDecoration( + labelText: label, + border: const OutlineInputBorder(), + contentPadding: const EdgeInsets.symmetric(vertical: 16, horizontal: 12), + ), + items: options.entries.map((entry) { + return DropdownMenuItem( + value: entry.key, + child: Text("${entry.key} - ${entry.value}"), + ); + }).toList(), + ), + ); + } + + Widget _buildTextField(TextEditingController controller, String label, {TextInputType keyboardType = TextInputType.text, bool readOnly = false}) { + return Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: TextFormField( + controller: controller, + readOnly: readOnly, + decoration: InputDecoration( + labelText: label, + border: const OutlineInputBorder(), + contentPadding: const EdgeInsets.symmetric(vertical: 16, horizontal: 12), + ), + keyboardType: keyboardType, + ), + ); + } +}