From b5b6c6ed49aab2aa28f0067e23c0790d78bf3483 Mon Sep 17 00:00:00 2001 From: asif Date: Sat, 8 Nov 2025 20:07:04 +0530 Subject: [PATCH] Change TPIn #4 --- lib/features/profile/profile_screen.dart | 1 + .../profile/tpin/change_tpin_otp_screen.dart | 63 ++-- .../profile/tpin/change_tpin_screen.dart | 296 ++++++++---------- 3 files changed, 171 insertions(+), 189 deletions(-) diff --git a/lib/features/profile/profile_screen.dart b/lib/features/profile/profile_screen.dart index 82ee5eb..f3b9d38 100644 --- a/lib/features/profile/profile_screen.dart +++ b/lib/features/profile/profile_screen.dart @@ -194,6 +194,7 @@ class _ProfileScreenState extends State { }, ), ListTile( + leading: const Icon(Icons.password), title: Text('Change TPIN'), onTap: () async { // 1. Get the AuthService instance diff --git a/lib/features/profile/tpin/change_tpin_otp_screen.dart b/lib/features/profile/tpin/change_tpin_otp_screen.dart index 6e65b03..c99759a 100644 --- a/lib/features/profile/tpin/change_tpin_otp_screen.dart +++ b/lib/features/profile/tpin/change_tpin_otp_screen.dart @@ -1,17 +1,16 @@ - import 'dart:async'; - import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:kmobile/di/injection.dart'; - import 'package:kmobile/features/dashboard/screens/dashboard_screen.dart'; import 'package:kmobile/widgets/pin_input_field.dart'; import '../../../api/services/change_password_service.dart'; class ChangeTpinOtpScreen extends StatefulWidget { + final String oldTpin; final String newTpin; - final String mobileNumber; // Receive mobile number + final String mobileNumber; const ChangeTpinOtpScreen({ super.key, + required this.oldTpin, required this.newTpin, required this.mobileNumber, }); @@ -22,7 +21,8 @@ class _ChangeTpinOtpScreenState extends State { final _otpController = TextEditingController(); - final ChangePasswordService _changePasswordService = getIt(); + final ChangePasswordService _changePasswordService = + getIt(); bool _isLoading = false; void _handleVerifyOtp() async { @@ -37,44 +37,43 @@ _isLoading = true; }); - String message = 'An unknown error occurred.'; - bool success = false; - try { - // 1. Validate the OTP - final responseString = await _changePasswordService.validateOtp( + // 1. Validate the OTP first. + await _changePasswordService.validateOtp( otp: _otpController.text, - mobileNumber: widget.mobileNumber, + mobileNumber: '8981274001', ); - final response = jsonDecode(responseString); - // 2. Check status and set message - if (response['statusCode'] == 200 || response['statusCode'] == 2000) { - message = 'TPIN changed successfully!'; - success = true; - } else { - message = response['message'] ?? 'Invalid OTP. Please try again.'; + // 2. If OTP is valid, then call validateChangeTpin. + await _changePasswordService.validateChangeTpin( + oldTpin: widget.oldTpin, + newTpin: widget.newTpin, + ); + + // 3. Show success message. + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('TPIN changed successfully!'), + backgroundColor: Colors.green, + ), + ); + // 4. Navigate back to the profile screen or home. + Navigator.of(context).popUntil((route) => route.isFirst); } } catch (e) { - message = 'Failed to verify OTP: $e'; - } finally { - // 3. Show feedback if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text(message), - backgroundColor: success ? Colors.green : Colors.red, + content: Text('An error occurred: $e'), + backgroundColor: Colors.red, ), ); - - // 4. Navigate to dashboard after 5 seconds - Timer(const Duration(seconds: 5), () { - if (mounted) { - Navigator.of(context).pushAndRemoveUntil( - MaterialPageRoute(builder: (context) => const DashboardScreen()), - (Route route) => false, - ); - } + } + } finally { + if (mounted) { + setState(() { + _isLoading = false; }); } } diff --git a/lib/features/profile/tpin/change_tpin_screen.dart b/lib/features/profile/tpin/change_tpin_screen.dart index 4ecf038..f53ebea 100644 --- a/lib/features/profile/tpin/change_tpin_screen.dart +++ b/lib/features/profile/tpin/change_tpin_screen.dart @@ -1,164 +1,146 @@ - import 'package:flutter/material.dart'; - import 'package:kmobile/di/injection.dart'; - import 'package:kmobile/features/profile/tpin/change_tpin_otp_screen.dart'; - import 'package:kmobile/widgets/pin_input_field.dart'; - import '../../../api/services/change_password_service.dart'; - import 'dart:convert'; +import 'package:flutter/material.dart'; +import 'package:kmobile/di/injection.dart'; +import 'package:kmobile/features/profile/tpin/change_tpin_otp_screen.dart'; +import 'package:kmobile/widgets/pin_input_field.dart'; +import '../../../api/services/change_password_service.dart'; - class ChangeTpinScreen extends StatefulWidget { - final String mobileNumber; - const ChangeTpinScreen({super.key, required this.mobileNumber}); +class ChangeTpinScreen extends StatefulWidget { + final String mobileNumber; + const ChangeTpinScreen({super.key, required this.mobileNumber}); - @override - State createState() => _ChangeTpinScreenState(); - } + @override + State createState() => _ChangeTpinScreenState(); +} - class _ChangeTpinScreenState extends State { - final _formKey = GlobalKey(); - final _oldTpinController = TextEditingController(); - final _newTpinController = TextEditingController(); - final _confirmTpinController = TextEditingController(); - final ChangePasswordService _changePasswordService = getIt(); - bool _isLoading = false; +class _ChangeTpinScreenState extends State { + final _formKey = GlobalKey(); + final _oldTpinController = TextEditingController(); + final _newTpinController = TextEditingController(); + final _confirmTpinController = TextEditingController(); + final ChangePasswordService _changePasswordService = + getIt(); + bool _isLoading = false; - @override - void dispose() { - _oldTpinController.dispose(); - _newTpinController.dispose(); - _confirmTpinController.dispose(); - super.dispose(); - } + @override + void dispose() { + _oldTpinController.dispose(); + _newTpinController.dispose(); + _confirmTpinController.dispose(); + super.dispose(); + } - void _handleChangeTpin() async { - if (_formKey.currentState!.validate()) { - setState(() { - _isLoading = true; - }); - try { - // 1. Validate the current TPIN and new TPIN - final responseString = await _changePasswordService.validateChangeTpin( - oldTpin: _oldTpinController.text, - newTpin: _newTpinController.text, - ); + void _handleChangeTpin() async { + if (_formKey.currentState!.validate()) { + setState(() { + _isLoading = true; + }); + try { + // 1. Get OTP for TPIN change. + await _changePasswordService.getOtpTpin(mobileNumber: '8981274001'); - // The service throws an exception for non-200 HTTP status, - // so we assume HTTP 200 here and check the body. - final response = jsonDecode(responseString); + // 2. Navigate to the OTP screen on success. + if (mounted) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => ChangeTpinOtpScreen( + oldTpin: _oldTpinController.text, + newTpin: _newTpinController.text, + mobileNumber: '8981274001', + ), + ), + ); + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Failed to send OTP: $e')), + ); + } + } finally { + if (mounted) { + setState(() { + _isLoading = false; + }); + } + } + } + } - // 2. Check the status code from the response body - if (response.statusCode == 200) { - // 3. Get OTP for TPIN change - await _changePasswordService.getOtpTpin(mobileNumber: '8981274001'); - - // 4. Navigate to OTP screen - if (mounted) { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => ChangeTpinOtpScreen( - newTpin: _newTpinController.text, - mobileNumber: '8981274001', // Pass mobile number - ), - ), - ); - } - } else { - if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(response['message'] ?? 'Invalid TPIN details') - )); - } - } - } catch (e) { - if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('An error occurred: $e')), - ); - } - } finally { - if (mounted) { - setState(() { - _isLoading = false; - }); - } - } - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('Change TPIN'), - ), - body: SingleChildScrollView( - padding: const EdgeInsets.all(16.0), - child: Form( - key: _formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text('Current TPIN'), - const SizedBox(height: 8), - PinInputField( - controller: _oldTpinController, - validator: (value) { - if (value == null || value.length != 6) { - return 'Please enter your 6-digit old TPIN'; - } - return null; - }, - ), - const SizedBox(height: 24), - const Text('New TPIN'), - const SizedBox(height: 8), - PinInputField( - controller: _newTpinController, - validator: (value) { - if (value == null || value.length != 6) { - return 'Please enter a 6-digit new TPIN'; - } - if (value == _oldTpinController.text) { - return 'New TPIN must be different from the old one.'; - } - return null; - }, - ), - const SizedBox(height: 24), - const Text('Confirm New TPIN'), - const SizedBox(height: 8), - PinInputField( - controller: _confirmTpinController, - validator: (value) { - if (value == null || value.length != 6) { - return 'Please confirm your new TPIN'; - } - if (value != _newTpinController.text) { - return 'TPINs do not match'; - } - return null; - }, - ), - const SizedBox(height: 32), - SizedBox( - width: double.infinity, - child: ElevatedButton( - onPressed: _isLoading ? null : _handleChangeTpin, - child: _isLoading - ? const SizedBox( - height: 24, - width: 24, - child: CircularProgressIndicator( - color: Colors.white, - strokeWidth: 2.5, - ), - ) - : const Text('Proceed'), - ), - ), - ], - ), - ), - ), - ); - } - } \ No newline at end of file + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Change TPIN'), + ), + body: SingleChildScrollView( + padding: const EdgeInsets.all(16.0), + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text('Current TPIN'), + const SizedBox(height: 8), + PinInputField( + controller: _oldTpinController, + validator: (value) { + if (value == null || value.length != 6) { + return 'Please enter your 6-digit old TPIN'; + } + return null; + }, + ), + const SizedBox(height: 24), + const Text('New TPIN'), + const SizedBox(height: 8), + PinInputField( + controller: _newTpinController, + validator: (value) { + if (value == null || value.length != 6) { + return 'Please enter a 6-digit new TPIN'; + } + if (value == _oldTpinController.text) { + return 'New TPIN must be different from the old one.'; + } + return null; + }, + ), + const SizedBox(height: 24), + const Text('Confirm New TPIN'), + const SizedBox(height: 8), + PinInputField( + controller: _confirmTpinController, + validator: (value) { + if (value == null || value.length != 6) { + return 'Please confirm your new TPIN'; + } + if (value != _newTpinController.text) { + return 'TPINs do not match'; + } + return null; + }, + ), + const SizedBox(height: 32), + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: _isLoading ? null : _handleChangeTpin, + child: _isLoading + ? const SizedBox( + height: 24, + width: 24, + child: CircularProgressIndicator( + color: Colors.white, + strokeWidth: 2.5, + ), + ) + : const Text('Proceed'), + ), + ), + ], + ), + ), + ), + ); + } +} \ No newline at end of file