From 212df37f8a0dd9ecbd7f694afd36ac57630ee70e Mon Sep 17 00:00:00 2001 From: risnaman Date: Sat, 19 Sep 2026 16:21:37 +0530 Subject: [PATCH] Code updated --- android/app/src/main/AndroidManifest.xml | 4 +- lib/screens/sign_up_screen/CreateAccount.dart | 72 ++- lib/screens/sign_up_screen/SignUpService.dart | 72 ++- .../sign_up_screen/sign_up_step_1.dart | 561 ++++++++---------- 4 files changed, 352 insertions(+), 357 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index b09befd..9578846 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,8 +1,10 @@ + + android:icon="@mipmap/ic_launcher" + android:usesCleartextTraffic="true"> { onTap: () async { if (_formKey.currentState!.validate()) { _formKey.currentState!.save(); - { - try { - print('form data is $formData'); + try { + print('form data is $formData'); - final response = - await userService.createAccount(formData); + final response = + await userService.createAccount(formData); - account_id = response['account_id'].toString(); - print( - 'after create account account id is $account_id'); - // ignore: use_build_context_synchronously - Navigator.pop( - _context, account_id); // Pop with account_id - - // Navigator.pop(context); - } catch (e) { - // ignore: use_build_context_synchronously - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('Error'), - content: Text('Account Creation Failed: $e'), - actions: [ - TextButton( - child: const Text('OK'), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ], - ); - }, - ); + var parsedId; + if (response is Map) { + parsedId = response['account_id'] ?? + response['id'] ?? + response['sysAccountId'] ?? + response['accountId'] ?? + response['item']?['account_id'] ?? + response['item']?['id']; + } else { + parsedId = response; } + + account_id = parsedId?.toString(); + print( + 'after create account account id is $account_id'); + + if (!mounted) return; + Navigator.pop(context, account_id); + } catch (e) { + if (!mounted) return; + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Error'), + content: Text('Account Creation Failed: $e'), + actions: [ + TextButton( + child: const Text('OK'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); } } }, diff --git a/lib/screens/sign_up_screen/SignUpService.dart b/lib/screens/sign_up_screen/SignUpService.dart index 8a1537e..2b17dc8 100644 --- a/lib/screens/sign_up_screen/SignUpService.dart +++ b/lib/screens/sign_up_screen/SignUpService.dart @@ -4,7 +4,42 @@ import '../../resources/api_constants.dart'; class SignUpApiService { final String baseUrl = ApiConstants.baseUrl; - final Dio dio = Dio(); + late final Dio dio; + + SignUpApiService() { + dio = Dio(BaseOptions( + connectTimeout: const Duration(seconds: 10), + receiveTimeout: const Duration(seconds: 10), + headers: { + 'Content-Type': 'application/json', + }, + )); + } + + String _formatError(dynamic e, String defaultMessage) { + if (e is DioException) { + if (e.response != null && e.response?.data != null) { + final data = e.response!.data; + if (data is Map) { + if (data['message'] != null) return data['message'].toString(); + if (data['operationMessage'] != null) return data['operationMessage'].toString(); + if (data['error'] != null) return data['error'].toString(); + } else if (data is String && data.isNotEmpty) { + return data; + } + } + if (e.type == DioExceptionType.connectionTimeout || + e.type == DioExceptionType.sendTimeout || + e.type == DioExceptionType.receiveTimeout) { + return 'Connection timed out. Please check your network and backend.'; + } + if (e.type == DioExceptionType.connectionError) { + return 'Cannot connect to server at $baseUrl. Please verify the backend is running.'; + } + return e.message ?? defaultMessage; + } + return defaultMessage.isNotEmpty ? '$defaultMessage: $e' : e.toString(); + } // get all account Future>> getallAccount(String token) async { @@ -17,18 +52,15 @@ class SignUpApiService { print('response data is ... $responseData'); if (responseData is List) { - // If the response is a list, cast it to the expected type final entities = responseData.cast>(); return entities; } else if (responseData is Map) { - // If the response is a single object, wrap it in a list return [responseData]; } else { - // Handle other unexpected response types here throw Exception('Unexpected response type'); } } catch (e) { - throw Exception('Failed to Account: $e'); + throw Exception(_formatError(e, 'Failed to fetch Account')); } } @@ -36,14 +68,18 @@ class SignUpApiService { Future> createAccount( Map entity) async { try { - // dio.options.headers['Authorization'] = 'Bearer $token'; final response = await dio .post('$baseUrl/token/users/sysaccount/savesysaccount', data: entity); - print(' created account is $response'); - return response.data; + print('created account is $response'); + if (response.data is Map) { + return response.data; + } else if (response.data is Map) { + return Map.from(response.data); + } + return {'account_id': response.data.toString()}; } catch (e) { - throw Exception('Failed To Create Account: $e'); + throw Exception(_formatError(e, 'Failed to Create Account')); } } @@ -51,41 +87,39 @@ class SignUpApiService { Future sendEmail(Map entity) async { try { print("in post api...$entity"); - // dio.options.headers['Authorization'] = 'Bearer $token'; await dio.post('$baseUrl/token/user/send_email', data: entity); print(entity); } catch (e) { - throw Exception('Failed to Send Email: $e'); + throw Exception(_formatError(e, 'Failed to Send Email')); } } // RESEND EMAIL FOR OTP Future resendEmail(String email) async { try { - // dio.options.headers['Authorization'] = 'Bearer $token'; await dio.post('$baseUrl/token/user/resend_otp?email=$email'); } catch (e) { - throw Exception('Failed to ReSend Email: $e'); + throw Exception(_formatError(e, 'Failed to Resend OTP')); } } - // OTP VEFICATION + // OTP VERIFICATION Future otpverification(String email, String otp) async { try { - // dio.options.headers['Authorization'] = 'Bearer $token'; await dio .post('$baseUrl/token/user/otp_verification?email=$email&otp=$otp'); } catch (e) { - throw Exception('Failed to Verify Otp: $e'); + throw Exception(_formatError(e, 'Failed to Verify OTP')); } } + Future createuser(Map entity) async { try { print("in post api...$entity"); await dio.post('$baseUrl/token/addOneAppUser', data: entity); print(entity); } catch (e) { - throw Exception('Failed to create User: $e'); + throw Exception(_formatError(e, 'Failed to Create User')); } } @@ -96,7 +130,7 @@ class SignUpApiService { await dio.put('$baseUrl/api/updateAppUserDto/$entityId', data: entity); print(entity); } catch (e) { - throw Exception('Failed to update Backend: $e'); + throw Exception(_formatError(e, 'Failed to update User')); } } @@ -105,7 +139,7 @@ class SignUpApiService { dio.options.headers['Authorization'] = 'Bearer $token'; await dio.delete('$baseUrl/api/delete_usr/$entityId'); } catch (e) { - throw Exception('Failed to delete User: $e'); + throw Exception(_formatError(e, 'Failed to delete User')); } } } diff --git a/lib/screens/sign_up_screen/sign_up_step_1.dart b/lib/screens/sign_up_screen/sign_up_step_1.dart index 424120b..829fdd4 100644 --- a/lib/screens/sign_up_screen/sign_up_step_1.dart +++ b/lib/screens/sign_up_screen/sign_up_step_1.dart @@ -37,22 +37,18 @@ class _SignUpUserScreenState extends State { final Map formData = {}; final _formKey = GlobalKey(); - var selectedAccount; - TextEditingController emailcontroller = TextEditingController(); - var email; - var otp; - var confirmPassword; - - bool _passwordVisible = false; - bool _isPasswordValid = true; - void _validatePassword(String password) { - setState(() { - _isPasswordValid = password.isNotEmpty; - }); - } + final TextEditingController emailcontroller = TextEditingController(); + String? otp; + bool _isLoading = false; RegistrationStep currentStep = RegistrationStep.SendOTP; + @override + void dispose() { + emailcontroller.dispose(); + super.dispose(); + } + void moveToNextStep() { setState(() { if (currentStep == RegistrationStep.SendOTP) { @@ -66,25 +62,96 @@ class _SignUpUserScreenState extends State { } void showSuccessMessage(String message) { + if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), - duration: const Duration(seconds: 2), + duration: const Duration(seconds: 3), backgroundColor: Colors.green, ), ); } void showErrorMessage(String error) { + if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(error), - duration: const Duration(seconds: 2), + duration: const Duration(seconds: 4), backgroundColor: Colors.red, ), ); } + Future _handleSendOtp() async { + FocusManager.instance.primaryFocus?.unfocus(); + if (_formKey.currentState!.validate()) { + _formKey.currentState!.save(); + + final email = emailcontroller.text.trim(); + formData['usrGrpId'] = 41; + formData['email'] = email; + + setState(() => _isLoading = true); + try { + print('send email data is $formData'); + await userService.sendEmail(formData); + if (!mounted) return; + showSuccessMessage('OTP sent to $email'); + moveToNextStep(); + } catch (e) { + if (!mounted) return; + showErrorMessage('Failed to send OTP: $e'); + } finally { + if (mounted) setState(() => _isLoading = false); + } + } + } + + Future _handleVerifyOtp() async { + FocusManager.instance.primaryFocus?.unfocus(); + final enteredOtp = otp?.trim(); + if (enteredOtp == null || enteredOtp.length < 6) { + showErrorMessage('Please enter the complete 6-digit OTP'); + return; + } + + final email = emailcontroller.text.trim(); + setState(() => _isLoading = true); + try { + print('Verifying email: $email with OTP: $enteredOtp'); + await userService.otpverification(email, enteredOtp); + if (!mounted) return; + showSuccessMessage('Email verified successfully'); + moveToNextStep(); + } catch (e) { + if (!mounted) return; + showErrorMessage('Failed to verify OTP: $e'); + } finally { + if (mounted) setState(() => _isLoading = false); + } + } + + Future _handleResendOtp() async { + final email = emailcontroller.text.trim(); + if (email.isEmpty) { + showErrorMessage('Email address is missing'); + return; + } + + setState(() => _isLoading = true); + try { + await userService.resendEmail(email); + if (!mounted) return; + showSuccessMessage('OTP resent to $email'); + } catch (e) { + if (!mounted) return; + showErrorMessage('Failed to resend OTP: $e'); + } finally { + if (mounted) setState(() => _isLoading = false); + } + } + @override Widget build(BuildContext context) { return Scaffold( @@ -98,301 +165,185 @@ class _SignUpUserScreenState extends State { svgPath: ImageConstant.imgArrowleft, margin: getMargin(left: 16, top: 13, bottom: 17), onTap: () { - Navigator.pop(context); + if (currentStep == RegistrationStep.VerifyOTP) { + setState(() => currentStep = RegistrationStep.SendOTP); + } else if (currentStep == RegistrationStep.EnterUserInfo) { + setState(() => currentStep = RegistrationStep.VerifyOTP); + } else { + Navigator.pop(context); + } }), centerTitle: true, title: AppbarTitle(text: "Create User")), - body: Builder( - builder: (BuildContext context) { - return SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(16), - child: Form( - key: _formKey, - child: SizedBox( - height: MediaQuery.of(context).size.height * - 0.5, // Use 50% of the screen height - child: ListView.builder( - itemCount: 1, - itemBuilder: (BuildContext context, int index) { - if(currentStep == RegistrationStep.SendOTP) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text("Email", - overflow: TextOverflow.ellipsis, - textAlign: TextAlign.left, - style: AppStyle.txtGilroyMedium16Bluegray900), - CustomTextFormField( - focusNode: FocusNode(), - controller: emailcontroller, - validator: (value) { - if (value == null || value.isEmpty) { - return 'Please enter Email'; - } - return null; - }, - hintText: "Enter Your Email", - margin: getMargin(top: 7), - textInputType: TextInputType.emailAddress), - CustomButton( - height: getVerticalSize(50), - width: getHorizontalSize(396), - text: "Send OTP", - margin: getMargin(top: 25), - onTap: () async { - - if (_formKey.currentState!.validate()) { - _formKey.currentState!.save(); - - formData['usrGrpId'] = 41; - formData['email'] = emailcontroller.text; - try { - print('send email data is $formData'); - - await userService.sendEmail(formData); - - await Future.delayed( - const Duration(seconds: 2)); - - moveToNextStep(); - } catch (e) { - showErrorMessage( - 'Failed to send OTP: $e'); - } - } - - }, - ), - Align( - alignment: Alignment.center, - child: Padding( - padding: getPadding(top: 28, bottom: 5), - child: RichText( - text: TextSpan(children: [ - TextSpan( - text: "", - style: TextStyle( - color: ColorConstant.fromHex( - "#ff12282a"), - fontSize: getFontSize(16), - fontFamily: 'Gilroy', - fontWeight: FontWeight.w400)), - TextSpan( - text: " ", - style: TextStyle( - color: ColorConstant.fromHex( - "#ff12282a"), - fontSize: getFontSize(16), - fontFamily: 'Gilroy', - fontWeight: FontWeight.w700)), - TextSpan( - text: "", - style: TextStyle( - color: ColorConstant.fromHex( - "#ff0061ff"), - fontSize: getFontSize(16), - fontFamily: 'Gilroy', - fontWeight: FontWeight.w700, - decoration: - TextDecoration.underline)) - ]), - textAlign: TextAlign.left))) - ]); - } - else if(currentStep == RegistrationStep.VerifyOTP) { - return Container( - width: double.maxFinite, - padding: getPadding( - left: 16, - top: 76, - right: 16, - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - CustomImageView( - svgPath: ImageConstant.imgMobile, - height: getVerticalSize( - 82, - ), - width: getHorizontalSize( - 51, - ), - ), - Padding( - padding: getPadding( - top: 29, - ), - child: Text( - "Email Verification", - overflow: TextOverflow.ellipsis, - textAlign: TextAlign.left, - style: AppStyle.txtGilroySemiBold24, - ), - ), - Container( - width: getHorizontalSize( - 302, - ), - margin: getMargin( - left: 46, - top: 19, - right: 46, - ), - child: Text( - "A mail with a 6-digit verification code was just sent to ${emailcontroller.text}", - maxLines: null, - textAlign: TextAlign.center, - style: AppStyle.txtGilroyMedium16, - ), - ), - Pinput( - length: 6, - showCursor: true, - defaultPinTheme: PinTheme( - width: 50, - height: 50, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - border: Border.all( - color: ColorConstant.blue50, - ), - ), - textStyle: const TextStyle( - fontSize: 20, - fontWeight: FontWeight.w600, - ), - ), - onCompleted: (value) { - setState(() { - otp = value; - }); - }, - ), - - CustomButton( - height: getVerticalSize( - 50, - ), - text: "Next", - margin: getMargin( - top: 40, - ), - onTap: () async { - try { - print('email is $email and otp is $otp'); - await userService.otpverification(emailcontroller.text, otp); - - moveToNextStep(); - showSuccessMessage( - 'Email verified successfully'); - } catch (e) { - showErrorMessage('Failed to verify OTP: $e'); - } - }, - ), - GestureDetector( - onTap: () async { - try { - await userService.resendEmail(email); - - await Future.delayed( - const Duration(seconds: 5)); - showSuccessMessage('OTP RESEND'); - } catch (e) { - showErrorMessage( - 'Failed to resend OTP: $e'); - } - }, - child: Padding( - padding: getPadding(top: 3), - child: Text("Forgot Password?", - overflow: TextOverflow.ellipsis, - textAlign: TextAlign.left, - style: - AppStyle.txtGilroyMedium14BlueA700)), - ), - - - Align( - alignment: Alignment.centerLeft, - child: Padding( - padding: getPadding( - top: 18, - bottom: 5, - ), - child: Row( - children: [ - Padding( - padding: getPadding( - top: 2, - ), - child: Text( - "Didn’t get the code?", - overflow: TextOverflow.ellipsis, - textAlign: TextAlign.left, - style: AppStyle.txtGilroyMedium16, - ), - ), - Padding( - padding: getPadding( - left: 12, - bottom: 1, - ), - child: Text( - "Resend", - overflow: TextOverflow.ellipsis, - textAlign: TextAlign.left, - style: AppStyle.txtGilroySemiBold16BlueA700, - ), - ), - ], - ), - ), - ), - ], - ), - ); - } - else if (currentStep == - RegistrationStep.EnterUserInfo) { - return Column( - children: [ - CustomButton( - height: getVerticalSize( - 50, - ), - text: "Go to Registration Details", - margin: getMargin( - top: 40, - ), - onTap: () async { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - RegistrationDetailsScreen(email: emailcontroller.text), - ), - ); - }, - ), - ], - ); - } - else{ - return Container(); - } - }, - ), - ), - ), - ), - ); - }, + body: SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Form( + key: _formKey, + child: _buildStepContent(), + ), ), ); } + Widget _buildStepContent() { + if (currentStep == RegistrationStep.SendOTP) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text("Email", + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.left, + style: AppStyle.txtGilroyMedium16Bluegray900), + CustomTextFormField( + controller: emailcontroller, + validator: (value) { + if (value == null || value.trim().isEmpty) { + return 'Please enter Email'; + } + final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); + if (!emailRegex.hasMatch(value.trim())) { + return 'Please enter a valid email address'; + } + return null; + }, + hintText: "Enter Your Email", + margin: getMargin(top: 7), + textInputType: TextInputType.emailAddress), + CustomButton( + height: getVerticalSize(50), + width: getHorizontalSize(396), + text: _isLoading ? "Sending OTP..." : "Send OTP", + margin: getMargin(top: 25), + onTap: _isLoading ? null : _handleSendOtp, + ), + ], + ); + } else if (currentStep == RegistrationStep.VerifyOTP) { + return Container( + width: double.maxFinite, + padding: getPadding(left: 16, top: 30, right: 16), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + CustomImageView( + svgPath: ImageConstant.imgMobile, + height: getVerticalSize(82), + width: getHorizontalSize(51), + ), + Padding( + padding: getPadding(top: 24), + child: Text( + "Email Verification", + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.left, + style: AppStyle.txtGilroySemiBold24, + ), + ), + Container( + width: getHorizontalSize(302), + margin: getMargin(left: 16, top: 16, right: 16), + child: Text( + "A mail with a 6-digit verification code was just sent to ${emailcontroller.text.trim()}", + maxLines: null, + textAlign: TextAlign.center, + style: AppStyle.txtGilroyMedium16, + ), + ), + const SizedBox(height: 24), + Pinput( + length: 6, + showCursor: true, + defaultPinTheme: PinTheme( + width: 50, + height: 50, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: ColorConstant.blue50, + ), + ), + textStyle: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.w600, + ), + ), + onChanged: (value) { + otp = value; + }, + onCompleted: (value) { + setState(() { + otp = value; + }); + }, + ), + CustomButton( + height: getVerticalSize(50), + text: _isLoading ? "Verifying..." : "Next", + margin: getMargin(top: 30), + onTap: _isLoading ? null : _handleVerifyOtp, + ), + Padding( + padding: getPadding(top: 24, bottom: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "Didn’t get the code?", + style: AppStyle.txtGilroyMedium16, + ), + const SizedBox(width: 8), + InkWell( + onTap: _isLoading ? null : _handleResendOtp, + child: Text( + "Resend", + style: AppStyle.txtGilroySemiBold16BlueA700, + ), + ), + ], + ), + ), + ], + ), + ); + } else if (currentStep == RegistrationStep.EnterUserInfo) { + return Column( + children: [ + const SizedBox(height: 40), + CustomImageView( + svgPath: ImageConstant.imgMobile, + height: getVerticalSize(82), + width: getHorizontalSize(51), + ), + const SizedBox(height: 20), + Text( + "Email Verified!", + style: AppStyle.txtGilroySemiBold24, + ), + const SizedBox(height: 10), + Text( + "Please proceed to enter your registration details.", + textAlign: TextAlign.center, + style: AppStyle.txtGilroyMedium16, + ), + CustomButton( + height: getVerticalSize(50), + text: "Go to Registration Details", + margin: getMargin(top: 40), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + RegistrationDetailsScreen(email: emailcontroller.text.trim()), + ), + ); + }, + ), + ], + ); + } else { + return Container(); + } + } }