From 15187d30f413db7b27912de2c083caf083c40095 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Mon, 29 Jun 2026 16:47:51 +0300 Subject: [PATCH] ui improvements --- .../providers/api/user_provider.dart | 4 +- .../screens/demo_request_form_screen.dart | 147 +++++++++--------- lib/modules/signup/screens/signup_screen.dart | 91 +++++------ .../forget_passwod_verify_otp.dart | 18 +-- .../reset_password_view.dart | 24 +-- lib/new_views/pages/login_page.dart | 20 ++- lib/views/widgets/dialogs/dialog.dart | 16 +- 7 files changed, 159 insertions(+), 161 deletions(-) diff --git a/lib/controllers/providers/api/user_provider.dart b/lib/controllers/providers/api/user_provider.dart index c2d0d00f..5fc2cf07 100644 --- a/lib/controllers/providers/api/user_provider.dart +++ b/lib/controllers/providers/api/user_provider.dart @@ -276,7 +276,7 @@ class UserProvider extends ChangeNotifier { // } // return response.statusCode; // } - Future sendForgetPasswordOtp({required BuildContext context, required String employeeId}) async { + Future sendForgetPasswordOtp({required BuildContext context, required String employeeId, required String typeSend}) async { GeneralResponseModel generalResponseModel = GeneralResponseModel(responseCode: -1); if (_loading == true) return generalResponseModel; _loading = true; @@ -284,7 +284,7 @@ class UserProvider extends ChangeNotifier { Response response; try { showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); - response = await ApiManager.instance.post(URLs.sendForgetPasswordOtp + '?employeeId=$employeeId', showToast: false, body: {}); + response = await ApiManager.instance.post(URLs.sendForgetPasswordOtp, showToast: false, body: {"employeeId": employeeId, "typeSend": typeSend}); generalResponseModel = GeneralResponseModel.fromJson(json.decode(response.body)); _loading = false; if (response.statusCode >= 200 && response.statusCode < 300) { diff --git a/lib/modules/applicationDemo/screens/demo_request_form_screen.dart b/lib/modules/applicationDemo/screens/demo_request_form_screen.dart index 371c6fe3..3b5e33f9 100644 --- a/lib/modules/applicationDemo/screens/demo_request_form_screen.dart +++ b/lib/modules/applicationDemo/screens/demo_request_form_screen.dart @@ -72,81 +72,82 @@ class _ApplicationDemoRequestFormScreenState extends State( - context: context, - title: 'Select Roles *', - initialValue: _selectedRoles, - staticData: provider.roles, - loading: provider.roleLoading, - showAsFullScreenDialog: true, - onSelect: (selectedRoles) { - setState(() { - _selectedRoles = selectedRoles ?? []; - _rolesValidationError = null; // Clear error when roles are selected - }); - }, - ), - if (_rolesValidationError != null) - Padding( - padding: const EdgeInsets.only(top: 8, left: 16), - child: Text( - _rolesValidationError!, - style: AppTextStyles.tinyFont.copyWith( - color: AppColor.red30, + SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + spacing: 12, + children: [ + AppTextFormField( + controller: _fullNameController, + labelText: 'Full Name *', + textInputType: TextInputType.name, + validator: (value) { + if (!_formSubmitted && _fullNameController.text.isEmpty) return null; + if (_fullNameController.text.isEmpty) return 'Full Name is required'; + return null; + }, + ), + AppTextFormField( + controller: _emailController, + labelText: 'Email Address *', + textInputType: TextInputType.emailAddress, + validator: (value) { + if (!_formSubmitted && _emailController.text.isEmpty) return null; + if (_emailController.text.isEmpty) return 'Email is required'; + if (!Validator.isEmail(_emailController.text)) return 'Invalid email format'; + return null; + }, + ), + AppTextFormField( + controller: _mobileController, + labelText: 'Mobile Number *', + textInputType: TextInputType.phone, + validator: (value) { + if (!_formSubmitted && _mobileController.text.isEmpty) return null; + if (_mobileController.text.isEmpty) return 'Mobile Number is required'; + if (!Validator.isPhoneNumber(_mobileController.text)) return 'Invalid mobile number'; + return null; + }, + ), + AppTextFormField( + controller: _companyNameController, + labelText: 'Company Name *', + textInputType: TextInputType.text, + validator: (value) { + if (!_formSubmitted && _companyNameController.text.isEmpty) return null; + if (_companyNameController.text.isEmpty) return 'Company Name is required'; + return null; + }, + ), + MultipleItemDropDownMenu( + context: context, + title: 'Select Roles *', + initialValue: _selectedRoles, + staticData: provider.roles, + loading: provider.roleLoading, + showAsFullScreenDialog: true, + onSelect: (selectedRoles) { + setState(() { + _selectedRoles = selectedRoles ?? []; + _rolesValidationError = null; // Clear error when roles are selected + }); + }, + ), + if (_rolesValidationError != null) + Padding( + padding: const EdgeInsets.only(top: 8, left: 16), + child: Text( + _rolesValidationError!, + style: AppTextStyles.tinyFont.copyWith( + color: AppColor.red30, + ), ), ), - ), - ], - ).paddingAll(16).expanded, + ], + ).toShadowContainer(context, borderRadius: 20), + ).expanded, FooterActionButton.footerContainer( context: context, child: AppFilledButton( diff --git a/lib/modules/signup/screens/signup_screen.dart b/lib/modules/signup/screens/signup_screen.dart index 8743e636..330ca3a4 100644 --- a/lib/modules/signup/screens/signup_screen.dart +++ b/lib/modules/signup/screens/signup_screen.dart @@ -5,6 +5,7 @@ import 'package:test_sa/controllers/validator/validator.dart'; import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; import 'package:test_sa/modules/signup/models/company_model.dart'; import 'package:test_sa/modules/signup/models/group_model.dart'; import 'package:test_sa/modules/signup/models/signup_request_model.dart'; @@ -95,41 +96,35 @@ class _SignUpScreenState extends State { appBar: const DefaultAppBar(title: "Sign Up"), body: Column( children: [ - Expanded( + SingleChildScrollView( + padding: EdgeInsets.all(16.toScreenWidth), child: Form( key: _formKey, - child: SingleChildScrollView( - padding: EdgeInsets.all(16.toScreenWidth), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildAssetGroupDropdown(), - 16.height, - if (_selectedAssetGroup != null) ...[ - _buildCompanyDropdown(), - 16.height, - ], - if (_selectedCompany != null) ..._buildDynamicFields(), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 12, + children: [ + _buildAssetGroupDropdown(), + // 16.height, + if (_selectedAssetGroup != null) ...[ + _buildCompanyDropdown(), + // 16.height, ], - ), - ), + if (_selectedCompany != null) ..._buildDynamicFields(), + ], + ).toShadowContainer(context, borderRadius: 20), ), - ), + ).expanded, if (_selectedCompany != null) - Container( - padding: EdgeInsets.all(16.toScreenWidth), - decoration: BoxDecoration( - color: AppColor.background(context), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: 10, - offset: const Offset(0, -2), - ), - ], - ), - child: SafeArea( - child: _buildSubmitButton(), + FooterActionButton.footerContainer( + context: context, + child: AppFilledButton( + label: "Submit", + maxWidth: true, + loading: _isSubmitting, + disableButton: _isSubmitting, + onPressed: _submitForm, ), ), ], @@ -199,20 +194,20 @@ class _SignUpScreenState extends State { if (_isHMGGroup) { return [ _buildEmployeeIdField(), - 16.height, + // 16.height, if (_employeeFetched) ...[ _buildReadOnlyField("Full Name", _fullNameController), _buildReadOnlyField("Email Address", _emailController), _buildReadOnlyField("Mobile Number", _mobileController), // _buildReadOnlyField("Site", _siteController), // _buildReadOnlyField("Department", _departmentController), - 16.height, + // 16.height, _buildExtensionField(), - 16.height, + // 16.height, _buildSiteDropdown(), - 16.height, + // 16.height, _buildDepartmentDropdown(), - 16.height, + // 16.height, _buildRoleField(), ], ]; @@ -220,17 +215,17 @@ class _SignUpScreenState extends State { return [ // Employee ID is auto-generated for Non-HMG, so not shown during signup _buildFullNameField(), - 16.height, + // 16.height, _buildEmailField(), - 16.height, + // 16.height, _buildMobileField(), - 16.height, + // 16.height, _buildExtensionField(), - 16.height, + // 16.height, _buildSiteDropdown(), - 16.height, + // 16.height, _buildDepartmentDropdown(), - 16.height, + // 16.height, _buildRoleField(), ]; } @@ -285,7 +280,7 @@ class _SignUpScreenState extends State { } Widget _buildReadOnlyField(String label, TextEditingController controller) { - if(controller.text.isEmpty) { + if (controller.text.isEmpty) { return const SizedBox(); } return Column( @@ -403,16 +398,6 @@ class _SignUpScreenState extends State { ); } - Widget _buildSubmitButton() { - return AppFilledButton( - label: "Submit", - maxWidth: true, - loading: _isSubmitting, - disableButton: _isSubmitting, - onPressed: _submitForm, - ); - } - Future _fetchEmployeeDetails(String employeeId) async { final employee = await _signUpProvider.getEmployeeFromERP(employeeId); @@ -436,7 +421,7 @@ class _SignUpScreenState extends State { _emailController.text = employee.employeeEmailAddress ?? ''; _mobileController.text = employee.employeeMobileNumber ?? ''; //Todo need to map site and department from ERP data - _departmentController.text = ''; + _departmentController.text = ''; _siteController.text = employee.branchCode ?? ''; }); } else { diff --git a/lib/new_views/forget_password_module/forget_passwod_verify_otp.dart b/lib/new_views/forget_password_module/forget_passwod_verify_otp.dart index 955852a5..56ab8486 100644 --- a/lib/new_views/forget_password_module/forget_passwod_verify_otp.dart +++ b/lib/new_views/forget_password_module/forget_passwod_verify_otp.dart @@ -12,6 +12,7 @@ import 'package:test_sa/models/new_models/general_response_model.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; import 'package:test_sa/new_views/forget_password_module/reset_password_view.dart'; +import 'package:test_sa/views/widgets/item_views/info_text_widget.dart'; class ForgetPasswordVerifyOtpView extends StatefulWidget { Map data = {}; @@ -65,9 +66,9 @@ class _ForgetPasswordVerifyOtpViewState extends State(context, listen: false); String employeeId = widget.data['employeeId']; - GeneralResponseModel response = await _userProvider.sendForgetPasswordOtp( - context: context, - employeeId: employeeId, - ); + GeneralResponseModel response = await _userProvider.sendForgetPasswordOtp(context: context, employeeId: employeeId, typeSend: widget.data['typeSend']); // Restart the timer _startTimer(); diff --git a/lib/new_views/forget_password_module/reset_password_view.dart b/lib/new_views/forget_password_module/reset_password_view.dart index 13197c9e..abd175d2 100644 --- a/lib/new_views/forget_password_module/reset_password_view.dart +++ b/lib/new_views/forget_password_module/reset_password_view.dart @@ -11,6 +11,8 @@ import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart'; import 'package:test_sa/new_views/pages/login_page.dart'; +import 'package:test_sa/views/widgets/item_views/info_header_widget.dart'; +import 'package:test_sa/views/widgets/item_views/info_text_widget.dart'; import '../../controllers/providers/api/user_provider.dart'; import '../../controllers/validator/validator.dart'; @@ -45,9 +47,9 @@ class _ResetPasswordViewState extends State { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ - context.translation.resetPassword.heading2(context).custom(color: AppColor.white936, fontWeight: FontWeight.w500), + Text(context.translation.resetPassword, style: AppTextStyles.heading2.copyWith(color: AppColor.headingTextColor(context), fontWeight: FontWeight.w500)), 8.height, - context.translation.pleaseEnterTheNewPassword.bodyText2(context).custom(color: AppColor.neutral120, fontWeight: FontWeight.w500), + InfoTextLabelWidget(label: context.translation.pleaseEnterTheNewPassword), 32.height, AppTextFormField( initialValue: "", @@ -56,10 +58,11 @@ class _ResetPasswordViewState extends State { labelText: context.translation.password, obscureText: !_passwordVisible, suffixIcon: Icon( - // Based on passwordVisible state choose the icon - _passwordVisible ? Icons.visibility : Icons.visibility_off, - color: context.isDark ? AppColor.neutral10 : AppColor.neutral20, - ).paddingOnly(end: 12).onPress(() { + // Based on passwordVisible state choose the icon + _passwordVisible ? Icons.visibility : Icons.visibility_off, + color: AppColor.icon2Color(context)) + .paddingOnly(end: 12) + .onPress(() { setState(() { _passwordVisible = !_passwordVisible; }); @@ -82,10 +85,7 @@ class _ResetPasswordViewState extends State { onSaved: (value) { confirmPassword = value; }, - suffixIcon: Icon( - _confirmPasswordVisible ? Icons.visibility : Icons.visibility_off, - color: context.isDark ? AppColor.neutral10 : AppColor.neutral20, - ).paddingOnly(end: 12).onPress(() { + suffixIcon: Icon(_confirmPasswordVisible ? Icons.visibility : Icons.visibility_off, color: AppColor.icon2Color(context)).paddingOnly(end: 12).onPress(() { setState(() { _confirmPasswordVisible = !_confirmPasswordVisible; }); @@ -107,11 +107,11 @@ class _ResetPasswordViewState extends State { if (newPassword != confirmPassword) { Fluttertoast.showToast(msg: 'Password not matched try again'); } else { - UserProvider userProvider = Provider.of(context,listen: false); + UserProvider userProvider = Provider.of(context, listen: false); UpdatePasswordModel updatePasswordModel = UpdatePasswordModel(password: newPassword, confirmPassword: confirmPassword, userId: userProvider.verifyOtpModel.userId, token: userProvider.verifyOtpModel.token); GeneralResponseModel generalResponseModel = await userProvider.updateNewPassword(context: context, updatePasswordModel: updatePasswordModel); - if (generalResponseModel.isSuccess==true) { + if (generalResponseModel.isSuccess == true) { newPassword = ''; confirmPassword = ''; Navigator.of(context).pushNamedAndRemoveUntil(LoginPage.routeName, (routes) => true); diff --git a/lib/new_views/pages/login_page.dart b/lib/new_views/pages/login_page.dart index 30af7d0c..89ebabfd 100644 --- a/lib/new_views/pages/login_page.dart +++ b/lib/new_views/pages/login_page.dart @@ -20,6 +20,7 @@ import 'package:test_sa/modules/applicationDemo/screens/demo_request_form_screen import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/forget_password_module/forget_passwod_verify_otp.dart'; import 'package:test_sa/new_views/pages/land_page/land_page.dart'; +import 'package:test_sa/views/widgets/dialogs/dialog.dart'; import 'package:url_launcher/url_launcher.dart'; import '../../controllers/providers/api/user_provider.dart'; @@ -160,14 +161,29 @@ class _LoginPageState extends State { Align( alignment: AlignmentDirectional.centerEnd, child: context.translation.forgotPassword.bodyText(context).custom(color: AppColor.primary10, fontWeight: FontWeight.w500).onPress(() async { + String? methodType; + bool result = await showDialog( + context: context, + builder: (_) => + AAlertDialog(title: "OTP Verification".addTranslation, content: "Please choose the OTP verification method".addTranslation, button1Text: "SMS", button2Text: "Email"), + ); + if (result == true) { + methodType = "SMS"; + } else { + methodType = "EMAIL"; + } + if (methodType == null) { + return; + } + if (userNameController.text.isNotEmpty) { - GeneralResponseModel response = await _userProvider.sendForgetPasswordOtp(context: context, employeeId: userNameController.text); + GeneralResponseModel response = await _userProvider.sendForgetPasswordOtp(context: context, employeeId: userNameController.text, typeSend: methodType); if (response.isSuccess == true) { Navigator.push( context, MaterialPageRoute( builder: (context) => ForgetPasswordVerifyOtpView( - data: {'employeeId': userNameController.text, 'phoneNumber': response.data}, + data: {'employeeId': userNameController.text, 'phoneNumber': response.data, 'typeSend': methodType}, ))); // Navigator.push(context, ForgetPasswordVerifyOtpView.routeName); } else { diff --git a/lib/views/widgets/dialogs/dialog.dart b/lib/views/widgets/dialogs/dialog.dart index bf0fccb4..452dc3ad 100644 --- a/lib/views/widgets/dialogs/dialog.dart +++ b/lib/views/widgets/dialogs/dialog.dart @@ -9,8 +9,10 @@ import 'package:test_sa/new_views/app_style/app_color.dart'; class AAlertDialog extends StatelessWidget { final String? title; final String? content; + final String? button1Text; + final String? button2Text; - const AAlertDialog({Key? key, this.title, this.content}) : super(key: key); + const AAlertDialog({Key? key, this.title, this.content, this.button1Text, this.button2Text}) : super(key: key); @override Widget build(BuildContext context) { @@ -21,14 +23,14 @@ class AAlertDialog extends StatelessWidget { actions: [ TextButton( // child: Text(_subtitle.confirm), - child: Text(context.translation.confirm), + child: Text(button1Text ?? context.translation.confirm), onPressed: () { Navigator.of(context).pop(true); }, ), TextButton( // child: Text(_subtitle.cancel), - child: Text(context.translation.cancel), + child: Text(button2Text ?? context.translation.cancel), onPressed: () { Navigator.of(context).pop(false); }, @@ -43,14 +45,14 @@ class AAlertDialog extends StatelessWidget { actions: [ TextButton( // child: Text(_subtitle.confirm), - child: Text(context.translation.confirm, style: AppTextStyles.bodyText.copyWith(color: AppColor.loadingColor(context))), + child: Text(button1Text ?? context.translation.confirm, style: AppTextStyles.bodyText.copyWith(color: AppColor.loadingColor(context))), onPressed: () { Navigator.of(context).pop(true); }, ), TextButton( // child: Text(_subtitle.cancel), - child: Text(context.translation.cancel, style: AppTextStyles.bodyText.copyWith(color: AppColor.loadingColor(context))), + child: Text(button2Text ?? context.translation.cancel, style: AppTextStyles.bodyText.copyWith(color: AppColor.loadingColor(context))), onPressed: () { Navigator.of(context).pop(false); }, @@ -59,7 +61,3 @@ class AAlertDialog extends StatelessWidget { ); } } - - - -