diff --git a/lib/config/my_router.dart b/lib/config/my_router.dart index 4402c031..b8d290e6 100644 --- a/lib/config/my_router.dart +++ b/lib/config/my_router.dart @@ -294,6 +294,7 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; +import 'package:uae_stat/presentation/Screens/auth_verification/registration.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; import '../domain/use_cases/preferences_use_case.dart'; @@ -304,6 +305,7 @@ import '../presentation/Screens/auth_verification/otp_verification.dart'; import '../presentation/Screens/profilepage.dart'; import '../presentation/routes/auth_routes/login_route.dart'; import '../presentation/routes/bottom_bar_routes/tab_routes/home_route.dart'; +import '../presentation/routes/drawer_routes/Drawer Items/edit_profile.dart'; import '../presentation/routes/drawer_routes/Drawer Items/feedback.dart'; import '../presentation/routes/drawer_routes/Drawer Items/user_guide/features.dart'; import '../presentation/routes/drawer_routes/Drawer Items/user_guide/user_guide.dart'; @@ -314,12 +316,16 @@ final GoRouter router = GoRouter( GoRoute( path: '/', //builder: (context, state) => LoginRoute(), - builder: (context, state) => MyHomePage(), + builder: (context, state) => LoginRoute(), ), GoRoute( path: '/myhomepage', builder: (context, state) => MyHomePage(), ), + GoRoute( + path: '/register', + builder: (context, state) => RegisterScreen(), + ), GoRoute( path: '/mailverification', builder: (context, state) => EmailVerificationScreen( @@ -331,10 +337,11 @@ final GoRouter router = GoRouter( ), ), GoRoute( - path: '/changepassword', - builder: (context, state) => Changepassword( - userId: '', - ), + path: '/changepassword/:userId', + builder: (context, state) { + final userId = state.pathParameters['userId']!; + return Changepassword(userId: userId); + }, ), GoRoute( path: '/createNewPw/:userId/:email', @@ -359,10 +366,11 @@ final GoRouter router = GoRouter( ), //Sub route GoRoute( - path: '/profile', - builder: (context, state) => ProfileScreen( - userId: '', - ), + path: '/profile/:userId', + builder: (context, state) { + final userId = state.pathParameters['userId']!; + return ProfileScreen(userId: userId); + }, ), GoRoute( path: '/user-guide', @@ -382,10 +390,13 @@ final GoRouter router = GoRouter( // return ManageUserRouter(title: title); // }, // ), + GoRoute( + path: '/editProfile', + builder: (context, state) => EditProfile(), + ), GoRoute( path: '/manageuser', builder: (context, state) => ManageUserRouter(), ), ], ); - diff --git a/lib/presentation/Screens/auth_verification/changepassword.dart b/lib/presentation/Screens/auth_verification/changepassword.dart index 702da9a2..9b86c2e1 100644 --- a/lib/presentation/Screens/auth_verification/changepassword.dart +++ b/lib/presentation/Screens/auth_verification/changepassword.dart @@ -58,7 +58,7 @@ class _ResetPasswordScreenState extends State { //print('userDetails ->: $userData'); if (userData['items'].isNotEmpty) { // Email exists; retrieve user ID - final userId = userData['items'][0]['id']; + // final userId = userData['items'][0]['id']; // Proceed with OTP request final otpResponse = await http.post( @@ -87,17 +87,15 @@ class _ResetPasswordScreenState extends State { ); // Navigate to the Email Verification screen - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => EmailVerificationScreen( - email: email, - userId: widget.userId, // Pass user ID to the next screen - otp: otp, - otpId: otpId, - sendVerificationCode: sendVerificationCode, - ), - ), + context.go( + '/mailverification', + extra: { + 'email': email, + 'userId': widget.userId, // Pass user ID to the next screen + 'otp': otp, + 'otpId': otpId, + 'sendVerificationCode': sendVerificationCode, + }, ); } else { final error = @@ -132,6 +130,12 @@ class _ResetPasswordScreenState extends State { } } + @override + void initState() { + super.initState(); + print(widget.userId); + } + @override void dispose() { _emailController.dispose(); diff --git a/lib/presentation/Screens/auth_verification/confirm_password.dart b/lib/presentation/Screens/auth_verification/confirm_password.dart index 41cb7c4c..cc04fa28 100644 --- a/lib/presentation/Screens/auth_verification/confirm_password.dart +++ b/lib/presentation/Screens/auth_verification/confirm_password.dart @@ -156,8 +156,8 @@ class _ConfirmPasswordState extends State { suffixIcon: IconButton( icon: Icon( _obscurePassword - ? Icons.visibility - : Icons.visibility_off, + ? Icons.visibility_off + : Icons.visibility, color: Colors.blue, ), onPressed: () { @@ -184,8 +184,8 @@ class _ConfirmPasswordState extends State { suffixIcon: IconButton( icon: Icon( _obscureConfirmPassword - ? Icons.visibility - : Icons.visibility_off, + ? Icons.visibility_off + : Icons.visibility, color: Colors.blue, ), onPressed: () { diff --git a/lib/presentation/Screens/auth_verification/create_new_pw.dart b/lib/presentation/Screens/auth_verification/create_new_pw.dart index 196b760c..1d0ddbda 100644 --- a/lib/presentation/Screens/auth_verification/create_new_pw.dart +++ b/lib/presentation/Screens/auth_verification/create_new_pw.dart @@ -38,13 +38,50 @@ class _CreateNewPwState extends State { } String? _validateNewPassword(String? value) { + // Define the regular expression for allowed characters + final regex = RegExp( + r'^[A-Za-z0-9~!@#$%^&*()_+=[\]{}|;:,.<>?/-àèìòùáéíóúÀÈÌÒÙÁÉÍÓÚ]*$'); + + // Define regular expressions for password complexity requirements + final hasUppercase = RegExp(r'[A-Z]'); + final hasLowercase = RegExp(r'[a-z]'); + final hasDigit = RegExp(r'\d'); + final hasSpecialCharacter = RegExp(r'[~!@#$%^&*()_+=[\]{}|;:,.<>?/-]'); + if (value == null || value.isEmpty) { return 'New password is required'; - } else if (value.length < 6) { - return 'Password must be at least 6 characters'; + } else if (value.length < 8 || value.length > 64) { + return 'Password must be between 8 and 64 characters'; } else if (value == _oldPassword) { return 'New password must not be the same as the old password'; } + + // Check the regular expression for allowed characters + if (!regex.hasMatch(value)) { + return 'Password contains invalid characters'; + } + + // Track missing constraints + List missingConstraints = []; + + if (!hasUppercase.hasMatch(value)) { + missingConstraints.add('uppercase letter'); + } + if (!hasLowercase.hasMatch(value)) { + missingConstraints.add('lowercase letter'); + } + if (!hasDigit.hasMatch(value)) { + missingConstraints.add('numeric digit'); + } + if (!hasSpecialCharacter.hasMatch(value)) { + missingConstraints.add('special character'); + } + + // If there are missing constraints, return a consolidated message + if (missingConstraints.isNotEmpty) { + return 'At least one ${missingConstraints.join(', ')}'; + } + _newPassword = value; // Store for validation return null; } @@ -184,8 +221,8 @@ class _CreateNewPwState extends State { suffixIcon: IconButton( icon: Icon( _obscureOldPassword - ? Icons.visibility - : Icons.visibility_off, + ? Icons.visibility_off + : Icons.visibility, color: Colors.blue, ), onPressed: () { @@ -208,8 +245,8 @@ class _CreateNewPwState extends State { suffixIcon: IconButton( icon: Icon( _obscureNewPassword - ? Icons.visibility - : Icons.visibility_off, + ? Icons.visibility_off + : Icons.visibility, color: Colors.blue, ), onPressed: () { @@ -231,8 +268,8 @@ class _CreateNewPwState extends State { suffixIcon: IconButton( icon: Icon( _obscureConfirmPassword - ? Icons.visibility - : Icons.visibility_off, + ? Icons.visibility_off + : Icons.visibility, color: Colors.blue, ), onPressed: () { diff --git a/lib/presentation/Screens/auth_verification/registration.dart b/lib/presentation/Screens/auth_verification/registration.dart index 97204028..d83f2db4 100644 --- a/lib/presentation/Screens/auth_verification/registration.dart +++ b/lib/presentation/Screens/auth_verification/registration.dart @@ -6,6 +6,7 @@ import 'package:pocketbase/pocketbase.dart'; import 'package:uae_stat/domain/use_cases/language.dart'; import 'package:uae_stat/presentation/Screens/profilepage.dart'; +import '../../../infrastructure/services/pocketbase_service.dart'; import '../../routes/auth_routes/login_route.dart'; class RegisterScreen extends StatefulWidget { @@ -135,27 +136,77 @@ class _RegisterScreenState extends State { return null; } + // String? _validatePassword(String? value) { + // // Define the regular expression for allowed characters + // final regex = RegExp( + // r'^[A-Za-z0-9~!@#$%^&*()_+=[\]{}|;:,.<>?/-àèìòùáéíóúÀÈÌÒÙÁÉÍÓÚ]*$'); + // + // if (value == null || value.isEmpty) { + // return 'Required'; + // } else if (value.length < 8) { + // return 'Password must be at least 8characters'; + // } + // + // // Check the length constraint + // if (value.length < 8 || value.length > 40) { + // return 'Password must be between 8 and 64 characters'; + // } + // + // // Check the regular expression + // if (!regex.hasMatch(value)) { + // return 'Password contains invalid characters'; + // } + // + // _password = value; // Store the password for confirm password validation + // return null; + // } + String? _validatePassword(String? value) { // Define the regular expression for allowed characters final regex = RegExp( r'^[A-Za-z0-9~!@#$%^&*()_+=[\]{}|;:,.<>?/-àèìòùáéíóúÀÈÌÒÙÁÉÍÓÚ]*$'); + // Define regular expressions for password complexity requirements + final hasUppercase = RegExp(r'[A-Z]'); + final hasLowercase = RegExp(r'[a-z]'); + final hasDigit = RegExp(r'\d'); + final hasSpecialCharacter = RegExp(r'[~!@#$%^&*()_+=[\]{}|;:,.<>?/-]'); + if (value == null || value.isEmpty) { return 'Required'; - } else if (value.length < 8) { - return 'Password must be at least 8characters'; } // Check the length constraint - if (value.length < 8 || value.length > 40) { + if (value.length < 8 || value.length > 64) { return 'Password must be between 8 and 64 characters'; } - // Check the regular expression + // Check the regular expression for allowed characters if (!regex.hasMatch(value)) { return 'Password contains invalid characters'; } + // Track missing constraints + List missingConstraints = []; + + if (!hasUppercase.hasMatch(value)) { + missingConstraints.add('uppercase letter'); + } + if (!hasLowercase.hasMatch(value)) { + missingConstraints.add('lowercase letter'); + } + if (!hasDigit.hasMatch(value)) { + missingConstraints.add('numeric digit'); + } + if (!hasSpecialCharacter.hasMatch(value)) { + missingConstraints.add('special character'); + } + + // If there are missing constraints, return a consolidated message + if (missingConstraints.isNotEmpty) { + return 'At least one ${missingConstraints.join(', ')}'; + } + _password = value; // Store the password for confirm password validation return null; } @@ -182,6 +233,30 @@ class _RegisterScreenState extends State { if (_formKey.currentState?.validate() ?? false) { if (isChecked) { try { + final existingUsers = await pb.collection('users').getList( + filter: 'email="${_emailController.text}"', + ); + + if (existingUsers.items.isNotEmpty) { + // Email already exists + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text('Email Exists'), + content: Text( + 'Email ID already exists. Please use a different email.'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: Text('OK'), + ), + ], + ); + }, + ); + return; // Stop registration process + } final adminAuth = await pb.admins .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com'); @@ -194,6 +269,7 @@ class _RegisterScreenState extends State { 'password': _passwordController.text, 'passwordConfirm': _passwordController.text, 'status': 'Pending', + 'role': 'user', }, headers: { 'Authorization': adminToken }); @@ -207,13 +283,6 @@ class _RegisterScreenState extends State { registrationSuccess = true; registrationFailed = false; // Show success message on success }); - - // Navigate to ProfileScreen after successful registration - // Navigator.pushReplacement( - // context, - // MaterialPageRoute( - // builder: (context) => ProfileScreen(userId: response.id)), - // ); } else { throw Exception('User registration failed: missing user ID'); } @@ -275,7 +344,7 @@ class _RegisterScreenState extends State { buildIconContainer(Icons.report, Color(0xFF7DAFBC)), SizedBox(height: 20), Text( - "Your registration is pending for Admin Approval.", + "Your registration is pending for verification", textAlign: TextAlign.center, style: TextStyle( fontSize: 16, @@ -284,7 +353,7 @@ class _RegisterScreenState extends State { ), SizedBox(height: 10), Text( - "Access will be granted once your account is approved.", + "Kindly verify your mail to proceed further.", textAlign: TextAlign.center, style: TextStyle( fontSize: 14, @@ -293,14 +362,8 @@ class _RegisterScreenState extends State { ), SizedBox(height: 10), ElevatedButton( - onPressed: () => { - Navigator.pushReplacement( - context, - MaterialPageRoute(builder: (context) => LoginRoute() - - //ProfileScreen(userId: userID) - ), - ), + onPressed: () { + context.go('/'); }, child: Text( 'Go to Login', @@ -455,8 +518,8 @@ class _RegisterScreenState extends State { suffixIcon: IconButton( icon: Icon( _obscurePassword - ? Icons.visibility - : Icons.visibility_off, + ? Icons.visibility_off + : Icons.visibility, color: Colors.blue, ), onPressed: () { @@ -487,8 +550,8 @@ class _RegisterScreenState extends State { suffixIcon: IconButton( icon: Icon( _obscureConfirmPassword - ? Icons.visibility - : Icons.visibility_off, + ? Icons.visibility_off + : Icons.visibility, color: Colors.blue, ), onPressed: () { @@ -628,12 +691,7 @@ class _RegisterScreenState extends State { child: GestureDetector( onTap: () { // Navigate to LoginRoute - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => LoginRoute()), - // MaterialPageRoute(builder: (context) => LoginRoute(userId: userID)), - ); + context.go('/'); }, child: Row( mainAxisSize: MainAxisSize.min, diff --git a/lib/presentation/Screens/profilepage.dart b/lib/presentation/Screens/profilepage.dart index c155b517..9c94e6ee 100644 --- a/lib/presentation/Screens/profilepage.dart +++ b/lib/presentation/Screens/profilepage.dart @@ -53,6 +53,7 @@ class _ProfileScreenState extends State { final TextEditingController _emailController = TextEditingController(); // To keep track of the selected date DateTime? _selectedDate; + String? role; // Date format for the display final DateFormat _dateFormat = DateFormat('yyyy-MM-dd'); @@ -148,6 +149,7 @@ class _ProfileScreenState extends State { setState(() { _usernameController.text = userDetailsResponse.data['username'] ?? ''; _emailController.text = userDetailsResponse.data['email'] ?? ''; + role = userDetailsResponse.data['role'] ?? ''; }); } catch (e) { print('Error fetching user details: $e'); @@ -271,7 +273,11 @@ class _ProfileScreenState extends State { _resetFormFields(); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Profile updated successfully!"))); - context.go('/myhomepage'); + if (role == 'admin') { + context.go('/manageuser'); + } else { + context.go('/myhomepage'); + } } catch (error) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Failed to update profile: $error"))); @@ -306,7 +312,7 @@ class _ProfileScreenState extends State { leading: IconButton( icon: Icon(Icons.arrow_back_ios_new, color: Colors.black), onPressed: () { - context.go('/myhomepage'); + context.go('/'); }, ), title: Text( @@ -315,7 +321,9 @@ class _ProfileScreenState extends State { ), actions: [ TextButton( - onPressed: () {}, + onPressed: () { + context.go('/'); + }, child: Text( 'Logout', style: TextStyle(color: Colors.orange), @@ -425,8 +433,9 @@ class _ProfileScreenState extends State { } //RegExp(r"^[a-zA-Z\s]+$"); - final nameRegex = - RegExp(r"^[a-zA-Z0-9'\-\u0627\u0639~\u00C0-\u00FF]+$"); + final nameRegex = RegExp( + r"^[a-zA-Z0-9'\-\u0627\u0639~\u00C0-\u00FF\s]+$"); + if (!nameRegex.hasMatch(value)) { return 'Invalid Characters'; } @@ -698,7 +707,7 @@ class ConfirmationDialog extends StatelessWidget { ), children: [ TextSpan( - text: "name", + text: "Name", style: TextStyle(fontWeight: FontWeight.w700), // Bold for "name" ), @@ -706,7 +715,7 @@ class ConfirmationDialog extends StatelessWidget { text: " or ", ), TextSpan( - text: "date of birth", + text: "Date of birth", style: TextStyle( fontWeight: FontWeight.w700), // Bold for "date of birth" ), diff --git a/lib/presentation/components/themed_text_field.dart b/lib/presentation/components/themed_text_field.dart index bc023b2e..27a526ec 100644 --- a/lib/presentation/components/themed_text_field.dart +++ b/lib/presentation/components/themed_text_field.dart @@ -1,4 +1,3 @@ - import 'package:external_repos/external_repos.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; @@ -38,8 +37,8 @@ class ThemedFormField extends HookWidget { onPressed: () => isObscured.value = !isObscured.value, icon: Icon( isObscured.value - ? Icons.visibility_outlined - : Icons.visibility_off_outlined, + ? Icons.visibility_off_outlined + : Icons.visibility_outlined, color: const Color(0xff9EA2A9), ), ); diff --git a/lib/presentation/routes/auth_routes/login_route.dart b/lib/presentation/routes/auth_routes/login_route.dart index b67e1dc1..cbf28f0a 100644 --- a/lib/presentation/routes/auth_routes/login_route.dart +++ b/lib/presentation/routes/auth_routes/login_route.dart @@ -740,206 +740,206 @@ class _LoginRouteState extends State { ), ), ); -// <<<<<<< HEAD -// ======= -// final form = Form( -// key: formKey, -// child: Column( -// children: [ -// ThemedFormField( -// hintText: context.translate( -// 'Email', -// 'بريد إلكتروني', -// ), -// validator: FieldValidator.email(), -// imgPath: MiscIconAssetPath.person, -// controller: emailCtl, -// ), -// 15.verticalSpace, -// ThemedFormField( -// validator: (text) { -// if (text!.length < 8) { -// return 'The password must be at least 8 characters'; -// } -// return FieldValidator.password(minLength: 8)(text); -// }, -// hintText: context.translate( -// 'Password', -// 'كلمة المرور', -// ), -// imgPath: MiscIconAssetPath.lock, -// controller: pwCtl, -// isObscurable: true, -// ), -// // 6.verticalSpace, -// Align( -// alignment: AlignmentDirectional.topEnd, -// child: forgotPwBtn, -// ), -// 10.verticalSpace, -// loginBtn, -// ], -// ), -// ); -// final helloAndPleaseLoginTexts = Column( -// children: [ -// Text( -// context.translate( -// 'Hello Again!', -// 'مرحبا مجددا!', -// ), -// style: TextStyle( -// fontFamily: context.translate( -// 'Roboto', -// 'NotoKufi', -// ), -// fontSize: 40, -// fontWeight: FontWeight.w300, -// ), -// ), -// 10.verticalSpace, -// Text( -// context.translate( -// 'Please login to access UAE’s key official statistics', -// 'يرجى تسجيل الدخول للوصول إلى الإحصاءات الرسمية الرئيسية لدولة الإمارات العربية المتحدة', -// ), -// textAlign: TextAlign.center, -// style: TextStyle( -// fontFamily: context.translate( -// 'Roboto', -// 'NotoKufi', -// ), -// fontSize: 18, -// color: const Color(0xff898C81), -// fontWeight: FontWeight.w700, -// ), -// ), -// ], -// ); -// final dontHaveAnAccountRegisterBtn = TextButton( -// onPressed: () => { -// Navigator.push( -// context, -// MaterialPageRoute(builder: (context) => RegisterScreen()), -// ), -// }, -// child: Text.rich( -// textAlign: TextAlign.center, -// TextSpan( -// style: TextStyle( -// fontFamily: context.translate( -// 'Roboto', -// 'NotoKufi', -// ), -// fontSize: 18, -// fontWeight: FontWeight.bold, -// ), -// children: [ -// TextSpan( -// text: context.translate( -// 'Don\'t have an account? ', -// 'ليس لديك حساب؟', -// ), -// style: const TextStyle( -// color: Color(0xff898C81), -// ), -// ), -// const TextSpan( -// text: ' ', -// ), -// TextSpan( -// text: context.translate( -// 'Register Now', -// 'سجل الان', -// ), -// style: TextStyle( -// color: MyTheme.topicColor(IndicatorTopic.economy).shade600, -// ), -// ), -// ], -// ), -// ), -// ); -// final continueAsGuestBtn = SizedBox( -// width: double.infinity, -// child: ElevatedButton( -// onPressed: () => context -// .go('/${context.language}/${BottomNavBarItem.home.routePath}'), -// style: ButtonStyle( -// shape: WidgetStatePropertyAll( -// RoundedRectangleBorder( -// borderRadius: BorderRadius.circular(10), -// ), -// ), -// padding: const WidgetStatePropertyAll( -// EdgeInsets.symmetric(vertical: 10.5), -// ), -// textStyle: WidgetStatePropertyAll( -// TextStyle( -// fontFamily: context.translate( -// 'Roboto', -// 'NotoKufi', -// ), -// fontSize: 16, -// fontWeight: FontWeight.w600, -// ), -// ), -// backgroundColor: WidgetStatePropertyAll( -// MyTheme.topicColor(IndicatorTopic.environment), -// ), -// foregroundColor: const WidgetStatePropertyAll( -// Colors.white, -// ), -// ), -// child: Row( -// mainAxisAlignment: MainAxisAlignment.center, -// children: [ -// Text( -// context.translate( -// 'Continue as Guest', -// 'استمر كضيف', -// ), -// ), -// 6.horizontalSpace, -// const Icon(Icons.chevron_right_outlined), -// ], -// ), -// ), -// ); -// final fcscBanner = Image.asset( -// BannerAssetPath.fcsc, -// height: 56, -// ); -// final screenWidth = MediaQuery.of(context).size.width; -// final listViewHorizontalPadding = -// screenWidth < 700 ? 30 : 30 + (screenWidth - 700) / 2; -// final scaffoldBody = ListView( -// padding: EdgeInsets.symmetric( -// horizontal: listViewHorizontalPadding.toDouble(), -// ), -// children: [ -// 36.verticalSpace, -// const Align( -// alignment: AlignmentDirectional.topEnd, -// child: LangToggle(), -// ), -// 16.verticalSpace, -// helloAndPleaseLoginTexts, -// 42.verticalSpace, -// form, -// 20.verticalSpace, -// dontHaveAnAccountRegisterBtn, -// 36.verticalSpace, -// continueAsGuestBtn, -// 72.verticalSpace, -// fcscBanner, -// ], -// ); -// final bgScaffold = Scaffold( -// backgroundColor: Colors.white, -// body: SafeArea(child: scaffoldBody), -// ); -// return bgScaffold; -// >>>>>>> b0c5ebce3deb7da2aa24b0ea5ffe684df7dfb7bf +<<<<<<< HEAD +======= + final form = Form( + key: formKey, + child: Column( + children: [ + ThemedFormField( + hintText: context.translate( + 'Email', + 'بريد إلكتروني', + ), + validator: FieldValidator.email(), + imgPath: MiscIconAssetPath.person, + controller: emailCtl, + ), + 15.verticalSpace, + ThemedFormField( + validator: (text) { + if (text!.length < 8) { + return 'The password must be at least 8 characters'; + } + return FieldValidator.password(minLength: 8)(text); + }, + hintText: context.translate( + 'Password', + 'كلمة المرور', + ), + imgPath: MiscIconAssetPath.lock, + controller: pwCtl, + isObscurable: true, + ), + // 6.verticalSpace, + Align( + alignment: AlignmentDirectional.topEnd, + child: forgotPwBtn, + ), + 10.verticalSpace, + loginBtn, + ], + ), + ); + final helloAndPleaseLoginTexts = Column( + children: [ + Text( + context.translate( + 'Hello Again!', + 'مرحبا مجددا!', + ), + style: TextStyle( + fontFamily: context.translate( + 'Roboto', + 'NotoKufi', + ), + fontSize: 40, + fontWeight: FontWeight.w300, + ), + ), + 10.verticalSpace, + Text( + context.translate( + 'Please login to access UAE’s key official statistics', + 'يرجى تسجيل الدخول للوصول إلى الإحصاءات الرسمية الرئيسية لدولة الإمارات العربية المتحدة', + ), + textAlign: TextAlign.center, + style: TextStyle( + fontFamily: context.translate( + 'Roboto', + 'NotoKufi', + ), + fontSize: 18, + color: const Color(0xff898C81), + fontWeight: FontWeight.w700, + ), + ), + ], + ); + final dontHaveAnAccountRegisterBtn = TextButton( + onPressed: () => { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => RegisterScreen()), + ), + }, + child: Text.rich( + textAlign: TextAlign.center, + TextSpan( + style: TextStyle( + fontFamily: context.translate( + 'Roboto', + 'NotoKufi', + ), + fontSize: 18, + fontWeight: FontWeight.bold, + ), + children: [ + TextSpan( + text: context.translate( + 'Don\'t have an account? ', + 'ليس لديك حساب؟', + ), + style: const TextStyle( + color: Color(0xff898C81), + ), + ), + const TextSpan( + text: ' ', + ), + TextSpan( + text: context.translate( + 'Register Now', + 'سجل الان', + ), + style: TextStyle( + color: MyTheme.topicColor(IndicatorTopic.economy).shade600, + ), + ), + ], + ), + ), + ); + final continueAsGuestBtn = SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: () => context + .go('/${context.language}/${BottomNavBarItem.home.routePath}'), + style: ButtonStyle( + shape: WidgetStatePropertyAll( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + padding: const WidgetStatePropertyAll( + EdgeInsets.symmetric(vertical: 10.5), + ), + textStyle: WidgetStatePropertyAll( + TextStyle( + fontFamily: context.translate( + 'Roboto', + 'NotoKufi', + ), + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + backgroundColor: WidgetStatePropertyAll( + MyTheme.topicColor(IndicatorTopic.environment), + ), + foregroundColor: const WidgetStatePropertyAll( + Colors.white, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + context.translate( + 'Continue as Guest', + 'استمر كضيف', + ), + ), + 6.horizontalSpace, + const Icon(Icons.chevron_right_outlined), + ], + ), + ), + ); + final fcscBanner = Image.asset( + BannerAssetPath.fcsc, + height: 56, + ); + final screenWidth = MediaQuery.of(context).size.width; + final listViewHorizontalPadding = + screenWidth < 700 ? 30 : 30 + (screenWidth - 700) / 2; + final scaffoldBody = ListView( + padding: EdgeInsets.symmetric( + horizontal: listViewHorizontalPadding.toDouble(), + ), + children: [ + 36.verticalSpace, + const Align( + alignment: AlignmentDirectional.topEnd, + child: LangToggle(), + ), + 16.verticalSpace, + helloAndPleaseLoginTexts, + 42.verticalSpace, + form, + 20.verticalSpace, + dontHaveAnAccountRegisterBtn, + 36.verticalSpace, + continueAsGuestBtn, + 72.verticalSpace, + fcscBanner, + ], + ); + final bgScaffold = Scaffold( + backgroundColor: Colors.white, + body: SafeArea(child: scaffoldBody), + ); + return bgScaffold; +>>>>>>> b0c5ebce3deb7da2aa24b0ea5ffe684df7dfb7bf } } diff --git a/lib/presentation/routes/drawer_routes/Drawer Items/edit_profile.dart b/lib/presentation/routes/drawer_routes/Drawer Items/edit_profile.dart index 080be24f..00d0c486 100644 --- a/lib/presentation/routes/drawer_routes/Drawer Items/edit_profile.dart +++ b/lib/presentation/routes/drawer_routes/Drawer Items/edit_profile.dart @@ -262,72 +262,72 @@ class _EditProfileState extends State { } void showConfirmationDialog(BuildContext context) async { - final result = await showDialog( - context: context, - builder: (context) => const ConfirmationDialog(), - ); + // final result = await showDialog( + // context: context, + // builder: (context) => const ConfirmationDialog(), + // ); - if (result == true) { - // Validate only the country field - if (_validateDropdown(_selectedCountry) == null) { - try { - String userID = userId; + // if (result == true) { + // Validate only the country field + if (_validateDropdown(_selectedCountry) == null) { + try { + String userID = userId; - print("ShowConfirmationuserID - $userID "); - // Retrieve data from the country/region field - String countryRegion = - _selectedCountry ?? ''; // Ensure the country is selected + print("ShowConfirmationuserID - $userID "); + // Retrieve data from the country/region field + String countryRegion = + _selectedCountry ?? ''; // Ensure the country is selected - // Create a multipart request - final uri = - Uri.parse('${_pb.baseUrl}/api/collections/users/records/$userID'); - final request = http.MultipartRequest('PATCH', uri); + // Create a multipart request + final uri = + Uri.parse('${_pb.baseUrl}/api/collections/users/records/$userID'); + final request = http.MultipartRequest('PATCH', uri); - // Add fields to the request - request.fields['country_region'] = - countryRegion; // Only update country here + // Add fields to the request + request.fields['country_region'] = + countryRegion; // Only update country here - // If profile image exists, add it - if (_profileImage != null) { - request.files.add(await http.MultipartFile.fromPath( - 'avatar', - _profileImage!.path, - )); - } + // If profile image exists, add it + if (_profileImage != null) { + request.files.add(await http.MultipartFile.fromPath( + 'avatar', + _profileImage!.path, + )); + } - // Add headers (e.g., authorization) - request.headers['Authorization'] = 'Bearer ${_pb.authStore.token}'; + // Add headers (e.g., authorization) + request.headers['Authorization'] = 'Bearer ${_pb.authStore.token}'; - // Send the request - final response = await request.send(); - print(response); + // Send the request + final response = await request.send(); + print(response); - // Handle response - if (response.statusCode == 200) { - _resetFormFields(); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text("Profile updated successfully!")), - ); - context.go('/myhomepage'); - } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: - Text("Failed to update profile: ${response.statusCode}")), - ); - } - } catch (error) { + // Handle response + if (response.statusCode == 200) { + _resetFormFields(); ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text("Failed to update profile: $error")), + SnackBar(content: Text("Profile updated successfully!")), + ); + context.go('/myhomepage'); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: + Text("Failed to update profile: ${response.statusCode}")), ); } - } else { - // Show an error if the country is invalid - setState(() { - showError = true; - }); + } catch (error) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Failed to update profile: $error")), + ); } + } else { + // Show an error if the country is invalid + setState(() { + showError = true; + }); } + // } } void _resetFormFields() { @@ -462,7 +462,7 @@ class _EditProfileState extends State { //RegExp(r"^[a-zA-Z\s]+$"); final nameRegex = RegExp( - r"^[a-zA-Z0-9'\-\u0627\u0639~\u00C0-\u00FF]+$"); + r"^[a-zA-Z0-9'\-\u0627\u0639~\u00C0-\u00FF\s]+$"); if (!nameRegex.hasMatch(value)) { return 'Invalid Characters'; } diff --git a/lib/presentation/routes/drawer_routes/Drawer Items/manage_users.dart b/lib/presentation/routes/drawer_routes/Drawer Items/manage_users.dart index 583d0015..deab917f 100644 --- a/lib/presentation/routes/drawer_routes/Drawer Items/manage_users.dart +++ b/lib/presentation/routes/drawer_routes/Drawer Items/manage_users.dart @@ -177,6 +177,7 @@ class _ManageUserRouterState extends State { body: { 'status': newStatus, // Update status 'verified': newStatus == 'Approved' ? true : false, + 'reviewed': true, }, ); diff --git a/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart b/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart index 1e220431..ba1d5bd7 100644 --- a/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart +++ b/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart @@ -25,7 +25,7 @@ class BaseScaffold extends StatelessWidget { return Scaffold( appBar: AppBar(title: title, actions: [ IconButton(onPressed: () {}, icon: Icon(Icons.toggle_off_outlined)), - ],), + ]), drawer: Drawer( child: ListView( children: [ @@ -39,19 +39,19 @@ class BaseScaffold extends StatelessWidget { SizedBox( width: mywidth / 8, child: Image( - image: AssetImage('assets/logos/fcsc.png'),),), + image: AssetImage('assets/logos/fcsc.png'))), ], ), Divider(), InkWell( - onTap: () => context.go('/profile'), + onTap: () => context.go('/editProfile'), child: Row( children: [ SizedBox( width: mywidth / 8, child: Image( image: AssetImage( - 'assets/edit_profile/profile.png',),),), + 'assets/edit_profile/profile.png'))), SizedBox( width: mywidth / 20, ), @@ -59,12 +59,12 @@ class BaseScaffold extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Mohammad'), - Text('Mohammad@fcsc.com'), + Text('Mohammad@fcsc.com') ], - ), + ) ], ), - ), + ) ], ), ), @@ -73,20 +73,10 @@ class BaseScaffold extends StatelessWidget { height: myheight / 15, width: mywidth / 15, child: Image( - image: AssetImage('assets/icons/drawer/message.png'),),), + image: AssetImage('assets/icons/drawer/message.png'))), title: Text('Feedback'), onTap: () => context.go('/feedback'), ), - //user Guide - ListTile( - leading: SizedBox( - height: myheight / 15, - width: mywidth / 15, - child: Image( - image: AssetImage('assets/icons/drawer/book.png'),),), - title: Text('User Guide'), - onTap: () => context.go('/user-guide'), - ), ListTile( leading: SizedBox( height: myheight / 15, diff --git a/lib/presentation/routes/drawer_routes/profile/reset_pw_route.dart b/lib/presentation/routes/drawer_routes/profile/reset_pw_route.dart index b640d62c..c8e170b0 100644 --- a/lib/presentation/routes/drawer_routes/profile/reset_pw_route.dart +++ b/lib/presentation/routes/drawer_routes/profile/reset_pw_route.dart @@ -27,7 +27,7 @@ class ResetPwRoute extends HookConsumerWidget { suffixIcon: IconButton( onPressed: () => doObscureOld.value = !doObscureOld.value, icon: Icon( - doObscureOld.value ? Icons.visibility : Icons.visibility_off, + doObscureOld.value ? Icons.visibility_off : Icons.visibility, ), ), ), @@ -51,7 +51,7 @@ class ResetPwRoute extends HookConsumerWidget { suffixIcon: IconButton( onPressed: () => doObscureNew.value = !doObscureNew.value, icon: Icon( - doObscureNew.value ? Icons.visibility : Icons.visibility_off, + doObscureNew.value ? Icons.visibility_off : Icons.visibility, ), ), ), @@ -72,7 +72,7 @@ class ResetPwRoute extends HookConsumerWidget { onPressed: () => doObscureConfirmNew.value = !doObscureConfirmNew.value, icon: Icon( - doObscureConfirmNew.value ? Icons.visibility : Icons.visibility_off, + doObscureConfirmNew.value ? Icons.visibility_off : Icons.visibility, ), ), ), diff --git a/pubspec.lock b/pubspec.lock index 663307ad..67247bd5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1244,6 +1244,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + syncfusion_flutter_charts: + dependency: "direct main" + description: + name: syncfusion_flutter_charts + sha256: b2a9f0fd585ef96c081c37697b46d48d3b0f3fe6bddc5011a3542962814fafa8 + url: "https://pub.dev" + source: hosted + version: "28.1.33" + syncfusion_flutter_core: + dependency: transitive + description: + name: syncfusion_flutter_core + sha256: b1071c698b502e7d55f91352a8b82d42f49f4c96e523d43b6fade5d5af710048 + url: "https://pub.dev" + source: hosted + version: "28.1.33" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index d6542470..3fac9859 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -54,6 +54,7 @@ dependencies: provider: ^6.1.2 mailer: ^6.2.0 image_picker: ^1.1.2 + syncfusion_flutter_charts: ^28.1.33 dependency_overrides: fading_edge_scrollview: ^4.1.1