From 95127914943f0a2f8fe85a4afd6a7bdcc1661718 Mon Sep 17 00:00:00 2001 From: venbaittech Date: Thu, 19 Dec 2024 14:02:34 +0530 Subject: [PATCH] bug fix (manage user,profile,login) --- lib/config/my_router.dart | 31 +- .../auth_verification/changepassword.dart | 28 +- .../auth_verification/confirm_password.dart | 8 +- .../auth_verification/create_new_pw.dart | 53 +- .../auth_verification/registration.dart | 120 +- lib/presentation/Screens/profilepage.dart | 23 +- .../components/themed_text_field.dart | 5 +- .../routes/auth_routes/login_route.dart | 1035 +++++------------ .../Drawer Items/edit_profile.dart | 108 +- .../Drawer Items/manage_users.dart | 1 + .../drawer_routes/custom_drawer_routes.dart | 187 ++- .../drawer_routes/profile/reset_pw_route.dart | 6 +- pubspec.lock | 16 + pubspec.yaml | 1 + 14 files changed, 732 insertions(+), 890 deletions(-) diff --git a/lib/config/my_router.dart b/lib/config/my_router.dart index cdfe3b13..edcd04f0 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/manage_users.dart'; @@ -312,12 +314,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( @@ -329,10 +335,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', @@ -356,10 +363,11 @@ final GoRouter router = GoRouter( builder: (context, state) => FeedbackForm(), ), 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: '/manageuser', @@ -369,10 +377,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 cbf28f0a..7e56df9f 100644 --- a/lib/presentation/routes/auth_routes/login_route.dart +++ b/lib/presentation/routes/auth_routes/login_route.dart @@ -1,10 +1,9 @@ -import 'package:excel/excel.dart'; import 'package:external_repos/external_repos.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:the_validator/the_validator.dart'; import 'package:uae_stat/config/my_theme.dart'; import 'package:uae_stat/domain/use_cases/auth_use_case.dart'; @@ -19,729 +18,329 @@ import 'package:uae_stat/presentation/components/space.dart'; import 'package:uae_stat/presentation/components/themed_text_field.dart'; import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart'; -import '../../Screens/profilepage.dart'; import '../../Screens/auth_verification/registration.dart'; import 'package:pocketbase/pocketbase.dart'; -// class LoginRoute extends HookConsumerWidget { -// final pb = PocketBase('https://pb.venbait.in'); -// // final _pb = PocketBase('http://127.0.0.1:8090'); -// LoginRoute({super.key}); -// -// static final formKey = GlobalKey(); -// -// Future profileStatus(String userId) async { -// try { -// // Authenticate admin -// final adminAuth = await pb.admins.authWithPassword( -// 'pb@venbainfotech.com', -// 'pb@venbainfotech.com', -// ); -// final String adminToken = adminAuth.token; -// print('adminToken: $adminToken'); -// -// // Fetch user details -// final userDetailsResponse = await pb.collection('users').getOne( -// userId, -// headers: { -// 'Authorization': 'Bearer $adminToken', -// }, -// ); -// print('userDetailsLogin: $userDetailsResponse'); -// -// // Check if 'is_profile_completed' is true or false -// if (userDetailsResponse != null && -// userDetailsResponse.data['is_profile_completed'] != null) { -// return userDetailsResponse.data['is_profile_completed']; -// } else { -// return false; // Default to false if the field is missing or response is null -// } -// } catch (e) { -// print('Error fetching user details: $e'); -// return false; // Return false on error -// } -// } -// -// @override -// Widget build(BuildContext context, WidgetRef ref) { -// final emailCtl = useTextEditingController(); -// final pwCtl = useTextEditingController(); -// final forgotPwBtn = TextButton( -// onPressed: () async { -// final forgotPwFormKey = GlobalKey(); -// final email = await showDialog( -// context: context, -// builder: (dialogCtx) => AlertDialog( -// title: Text( -// context.translate( -// 'Reset Password', -// 'إعادة تعيين كلمة المرور', -// ), -// ), -// content: Form( -// key: forgotPwFormKey, -// child: TextFormField( -// validator: FieldValidator.email(), -// controller: emailCtl, -// decoration: InputDecoration( -// labelText: context.translate( -// 'Email address', -// 'عنوان البريد الإلكتروني', -// ), -// ), -// ), -// ), -// actions: [ -// TextButton( -// onPressed: Navigator.of(dialogCtx, rootNavigator: true).pop, -// child: Text( -// context.translate( -// 'Return', -// 'يعود', -// ), -// ), -// ), -// ElevatedButton( -// onPressed: () { -// final isValid = forgotPwFormKey.currentState!.validate(); -// if (!isValid) return; -// Navigator.of(dialogCtx, rootNavigator: true).pop( -// emailCtl.text, -// ); -// }, -// child: Text( -// context.translate( -// 'Send Reset Email', -// 'إرسال إعادة تعيين البريد الإلكتروني', -// ), -// ), -// ), -// ], -// ), -// ); -// if (email == null) return; -// if (!context.mounted) return; -// await context.loaderWithErrorDialog( -// () => ref -// .read( -// authUseCaseProvider.notifier, -// ) -// .requestPwReset(email), -// ); -// if (!context.mounted) return; -// context.simpleDialog( -// title: context.translate( -// 'Success!', -// 'نجاح', -// ), -// content: context.translate( -// 'An email has been sent to $email with further details.', -// 'تم إرسال بريد إلكتروني إلى $email يتضمن المزيد من التفاصيل.', -// ), -// ); -// }, -// child: Text( -// context.translate( -// 'Forgot Password?', -// 'هل نسيت كلمة السر؟', -// ), -// style: TextStyle( -// fontFamily: context.translate( -// 'Roboto', -// 'NotoKufi', -// ), -// fontWeight: FontWeight.bold, -// fontSize: 14, -// color: MyTheme.topicColor(IndicatorTopic.economy).shade600, -// ), -// ), -// ); -// final loginBtn = SizedBox( -// width: double.infinity, -// child: ElevatedButton( -// onPressed: () { -// context.go('/profile'); -// }, -// // async { -// // final isValid = formKey.currentState!.validate(); -// // if (!isValid) return; -// // final session = await context.loaderWithErrorDialog( -// // () => ref -// // .read( -// // authUseCaseProvider.notifier, -// // ) -// // .login( -// // emailCtl.text, -// // pwCtl.text, -// // ), -// // errorDialogBuilder: ( -// // error, [ -// // StackTrace? stackTrace, -// // ]) { -// // if (error == LoginError.invalidEmailPw) { -// // return context.simpleDialog( -// // title: context.translate( -// // 'Incorrect credentials', -// // 'أوراق غير صحيحة', -// // ), -// // content: context.translate( -// // 'Your email or password is invalid. Please try again.', -// // 'بريدك الإلكتروني أو كلمة المرور غير صالحة. حاول مرة اخرى.', -// // ), -// // ); -// // } -// // if (error == LoginError.emailAddressNotVerified) { -// // return context.simpleDialog( -// // title: context.translate( -// // 'Verification Error', -// // 'خطأ التحقق', -// // ), -// // content: context.translate( -// // '${emailCtl.text} is not a verified email address. Please check your email for a verification link.', -// // '${emailCtl.text} ليس عنوان بريد إلكتروني تم التحقق منه. يرجى التحقق من بريدك الإلكتروني للحصول على رابط التحقق.', -// // ), -// // extraAction: ElevatedButton( -// // onPressed: () { -// // context.go('/profile'); -// // }, -// // // async { -// // // Navigator.of( -// // // context, -// // // rootNavigator: true, -// // // ).pop(); -// // // await context.loaderWithErrorDialog( -// // // () => ref -// // // .read(authUseCaseProvider.notifier) -// // // .requestVerificationEmail(emailCtl.text), -// // // ); -// // // if (!context.mounted) return; -// // // context.simpleDialog( -// // // title: 'Email Re-sent', -// // // content: -// // // 'We\'ve sent you the verification email at ${emailCtl.text} again.', -// // // ); -// // // }, -// // child: Text( -// // context.translate( -// // 'I did not receive an email', -// // 'لم أتلق بريدًا إلكترونيًا', -// // ), -// // ), -// // ), -// // ); -// // } -// // return context.simpleDialog(); -// // }, -// // ); -// // // if (!context.mounted || session == null) return; -// // // Extract userId from the session -// // // final userId = session.id; -// // // -// // // final bool isUpdate = await profileStatus(userId); -// // // -// // // print('Is profile completed: $isUpdate'); -// // -// // // if (isUpdate) { -// // // Navigator.pushReplacement( -// // // context, -// // // MaterialPageRoute(builder: (context) => DemoHome()), -// // // ); -// // // } -// // // else { -// // -// // // Navigator.pushReplacement( -// // // context, -// // // MaterialPageRoute( -// // // builder: (context) => ProfileScreen(userId: userId)), -// // // ); -// // //} -// // -// // // 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.social), -// ), -// // surfaceTintColor: MaterialStatePropertyAll( -// // MyTheme.economy[800], -// // ), -// foregroundColor: const WidgetStatePropertyAll( -// Colors.white, -// ), -// ), -// child: Row( -// mainAxisAlignment: MainAxisAlignment.center, -// children: [ -// Text( -// context.translate( -// 'Login', -// 'تسجيل الدخول', -// ), -// ), -// 6.horizontalSpace, -// const Icon(Icons.chevron_right_outlined), -// ], -// ), -// ), -// ); -// 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 < 10) { -// return 'The password must be at least 10 characters'; -// } -// return FieldValidator.password(minLength: 10)(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; -// } -// } +class LoginRoute extends HookConsumerWidget { + final pb = PocketBase('https://pb.venbait.in'); + // final _pb = PocketBase('http://127.0.0.1:8090'); + LoginRoute({super.key}); + dynamic userData; + String? role; -class LoginRoute extends StatefulWidget { - const LoginRoute({super.key}); + static final formKey = GlobalKey(); + + Future profileStatus(String userId) async { + try { + // Authenticate admin + final adminAuth = await pb.admins.authWithPassword( + 'pb@venbainfotech.com', + 'pb@venbainfotech.com', + ); + final String adminToken = adminAuth.token; + print('adminToken: $adminToken'); + + // Fetch user details + final userDetailsResponse = await pb.collection('users').getOne( + userId, + headers: { + 'Authorization': 'Bearer $adminToken', + }, + ); + + print('userDetailsLogin: $userDetailsResponse'); + + // Check if user is verified + role = userDetailsResponse.data['role']; + final bool? isVerified = userDetailsResponse.data['verified']; + final bool? isUserMailVerified = + userDetailsResponse.data['user_mail_verify']; + + // Check admin and email verification statuses + if (isVerified == false) { + throw Exception('Admin not approved'); + } + if (isUserMailVerified == false) { + throw Exception('Email not verified'); + } + userData = userDetailsResponse; + // Check if 'is_profile_completed' is true + final bool isProfileCompleted = + userDetailsResponse.data['is_profile_completed'] ?? false; + + return isProfileCompleted; + } catch (e) { + print('Error fetching user details: $e'); + rethrow; + } + } + + Future saveUserId(String userId) async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setString('userId', userId); // Save userId locally + } @override - State createState() => _LoginRouteState(); -} - -class _LoginRouteState extends State { - final _formKey = GlobalKey(); - bool _obscurePassword = true; - @override - Widget build(BuildContext context) { - double screenheight = MediaQuery.of(context).size.height; - double screenwidth = MediaQuery.of(context).size.width; - return Scaffold( - body: Padding( - padding: const EdgeInsets.all(20.0), - child: Form( - key: _formKey, - child: SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox(height: screenheight / 6), - Text( - 'Hello Again !', - style: TextStyle( - fontSize: 32, fontWeight: FontWeight.w400), - ), - SizedBox(height: 5), - Text('Please login to access',style: TextStyle( - fontFamily: context.translate( - 'Roboto', - 'NotoKufi', - ), - color: Color(0xFF898C81), - fontSize: 18, fontWeight: FontWeight.bold),), - Text("UAE's key official statistics",style: TextStyle( - fontFamily: context.translate( - 'Roboto', - 'NotoKufi', - ), - color: Color(0xFF898C81), - fontSize: 16, fontWeight: FontWeight.bold),), - - SizedBox(height: 10), - // Display this if registration is pending approval - TextFormField( - // controller: _usernameController, - // focusNode: _focusNodes[0], - decoration: InputDecoration( - hintText: "Username", - //hintText: _showHints[0] ? 'Username' : null, - prefixIcon: Image.asset("assets/icons/misc/person.png",scale: 2,color: Colors.lightBlueAccent,), - border: OutlineInputBorder(borderRadius: BorderRadius.circular(15)), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Colors.blue.shade900,) - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), // Keep rounded corners - borderSide: BorderSide( - color: Colors.blue,// Default border color - width: 1.0, // Border width when not focused - ), - ), - counterText: '', - ), - //validator: _validateUsername, - maxLength: - 40, // Set the maximum length to 20 characters - //maxLengthEnforcement: MaxLengthEnforcement.enforced, - ), - SizedBox(height: 20), - TextFormField( - // controller: _passwordController, - // focusNode: _focusNodes[2], - // obscureText: _obscurePassword, - decoration: InputDecoration( - hintText: "Password", - // hintText: _showHints[2] ? 'Enter your password' : null, - prefixIcon: Image.asset("assets/icons/misc/lock.png",scale: 2,color: Colors.lightBlueAccent,), - suffixIcon: IconButton( - icon: Icon( - _obscurePassword - ? Icons.visibility - : Icons.visibility_off, - color: Colors.grey, - ), - onPressed: () { - setState(() { - _obscurePassword = !_obscurePassword; - }); - }, - ), - border: OutlineInputBorder(borderRadius: BorderRadius.circular(15)), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Colors.blue.shade900,), - - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), // Keep rounded corners - borderSide: BorderSide( - color: Colors.blue,// Default border color - width: 1.0, // Border width when not focused - ), - ), - counterText: '', - ), - //validator: _validatePassword, - maxLength: 40, - maxLengthEnforcement: MaxLengthEnforcement.enforced, - ), - - SizedBox(height: 10), - Row(mainAxisAlignment: MainAxisAlignment.end, - children: [Text("Forgot Password?", - style: TextStyle(color: Color(0xFF985400),fontWeight: FontWeight.bold - ),)],), - SizedBox(height: 20), - SizedBox( - width: screenwidth, - child: ElevatedButton( - onPressed: (){ - context.go('/profile'); - }, - //_registerUser, - style: ElevatedButton.styleFrom( - backgroundColor: Color( - 0xFFA7887A), // Brownish color for Register - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - "Login", - style: TextStyle( - fontSize: 16, color: Colors.white), - ), - SizedBox(width: 8), - Icon(Icons.arrow_forward_ios, - color: Colors.white, - size: 10, - ), - ], - ), - ), - ), - SizedBox(height: screenheight/20), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - "Don't have an account? ", - style: TextStyle( - fontFamily: context.translate( - 'Roboto', - 'NotoKufi', - ), - color: Color(0xFF898C81), - fontSize: 18, fontWeight: FontWeight.bold), - ), - InkWell( - onTap: (){ - context.go('/registration'); - }, - child: Text( - " Register Now", - style: TextStyle( - fontFamily: context.translate( - 'Roboto', - 'NotoKufi', - ), - color: Color(0xFF985400), - fontSize: 18, fontWeight: FontWeight.bold), - ), - ), - ],), - - SizedBox( - height: screenheight/20, - ), - SizedBox( - width: screenwidth, - child: ElevatedButton( - onPressed: () { - context.go('/myhomepage'); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Color( - 0xFF82AFCB), // Blueish color for Login - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - "Continue as guest", - style: TextStyle( - fontSize: 16, - color: Colors.white, - ), - ), - SizedBox(width: 8), - Icon(Icons.arrow_forward_ios, - color: Colors.white,size: 10,), - ], - ), - ), - ), - SizedBox( - height: screenheight/15, - ), - Center( - child: Container( - height: screenheight / 13, - width: screenwidth / 3, - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage( - "assets/splash_screen/logo.png"), // Background image asset - fit: BoxFit.fill, - ), - ), - )) - ], + Widget build(BuildContext context, WidgetRef ref) { + final emailCtl = useTextEditingController(); + final pwCtl = useTextEditingController(); + final forgotPwBtn = TextButton( + onPressed: () async { + final forgotPwFormKey = GlobalKey(); + final email = await showDialog( + context: context, + builder: (dialogCtx) => AlertDialog( + title: Text( + context.translate( + 'Reset Password', + 'إعادة تعيين كلمة المرور', + ), ), + content: Form( + key: forgotPwFormKey, + child: TextFormField( + validator: FieldValidator.email(), + controller: emailCtl, + decoration: InputDecoration( + labelText: context.translate( + 'Email address', + 'عنوان البريد الإلكتروني', + ), + ), + ), + ), + actions: [ + TextButton( + onPressed: Navigator.of(dialogCtx, rootNavigator: true).pop, + child: Text( + context.translate( + 'Return', + 'يعود', + ), + ), + ), + ElevatedButton( + onPressed: () { + final isValid = forgotPwFormKey.currentState!.validate(); + if (!isValid) return; + Navigator.of(dialogCtx, rootNavigator: true).pop( + emailCtl.text, + ); + }, + child: Text( + context.translate( + 'Send Reset Email', + 'إرسال إعادة تعيين البريد الإلكتروني', + ), + ), + ), + ], ), + ); + if (email == null) return; + if (!context.mounted) return; + await context.loaderWithErrorDialog( + () => ref + .read( + authUseCaseProvider.notifier, + ) + .requestPwReset(email), + ); + if (!context.mounted) return; + context.simpleDialog( + title: context.translate( + 'Success!', + 'نجاح', + ), + content: context.translate( + 'An email has been sent to $email with further details.', + 'تم إرسال بريد إلكتروني إلى $email يتضمن المزيد من التفاصيل.', + ), + ); + }, + child: Text( + context.translate( + 'Forgot Password?', + 'هل نسيت كلمة السر؟', + ), + style: TextStyle( + fontFamily: context.translate( + 'Roboto', + 'NotoKufi', + ), + fontWeight: FontWeight.bold, + fontSize: 14, + color: MyTheme.topicColor(IndicatorTopic.economy).shade600, + ), + ), + ); + final loginBtn = SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: () async { + final isValid = formKey.currentState!.validate(); + if (!isValid) return; + final session = await context.loaderWithErrorDialog( + () => ref + .read( + authUseCaseProvider.notifier, + ) + .login( + emailCtl.text, + pwCtl.text, + ), + errorDialogBuilder: ( + error, [ + StackTrace? stackTrace, + ]) { + if (error == LoginError.invalidEmailPw) { + return context.simpleDialog( + title: context.translate( + 'Incorrect credentials', + 'أوراق غير صحيحة', + ), + content: context.translate( + 'Your email or password is invalid. Please try again.', + 'بريدك الإلكتروني أو كلمة المرور غير صالحة. حاول مرة اخرى.', + ), + ); + } + // if (error == LoginError.emailAddressNotVerified) { + // return context.simpleDialog( + // title: context.translate( + // 'Verification Error', + // 'خطأ التحقق', + // ), + // content: context.translate( + // '${emailCtl.text} is not a verified email address. Please check your email for a verification link.', + // '${emailCtl.text} ليس عنوان بريد إلكتروني تم التحقق منه. يرجى التحقق من بريدك الإلكتروني للحصول على رابط التحقق.', + // ), + // extraAction: ElevatedButton( + // onPressed: () async { + // Navigator.of( + // context, + // rootNavigator: true, + // ).pop(); + // await context.loaderWithErrorDialog( + // () => ref + // .read(authUseCaseProvider.notifier) + // .requestVerificationEmail(emailCtl.text), + // ); + // if (!context.mounted) return; + // context.simpleDialog( + // title: 'Email Re-sent', + // content: + // 'We\'ve sent you the verification email at ${emailCtl.text} again.', + // ); + // }, + // child: Text( + // context.translate( + // 'I did not receive an email', + // 'لم أتلق بريدًا إلكترونيًا', + // ), + // ), + // ), + // ); + // } + return context.simpleDialog(); + }, + ); + + if (!context.mounted || session == null) return; + final userId = session.id; + if (userId.isNotEmpty) { + await saveUserId(userId); + } + try { + final bool isProfileComplete = await profileStatus(userId); + print(isProfileComplete); + if (isProfileComplete) { + print('home'); + if (role == 'admin') { + context.go('/manageuser'); + } else { + context.go('/myhomepage'); + } + // context.go('/myhomepage'); + } else { + print('profile'); + if (userId != null && userId.isNotEmpty) { + context.go('/profile/$userId'); + } else { + print('Error: userId is null or empty.'); + } + } + } catch (e) { + // Handle specific errors based on their message + if (e.toString().contains('Admin not approved')) { + context.simpleDialog( + title: context.translate( + 'Admin Approval Required', 'موافقة المسؤول مطلوبة'), + content: context.translate( + 'Your account has not been approved by the admin.', + 'لم تتم الموافقة على حسابك من قبل المسؤول.', + ), + ); + } else if (e.toString().contains('Email not verified')) { + context.simpleDialog( + title: context.translate( + 'Email Not Verified', 'البريد الإلكتروني غير مُحقق'), + content: context.translate( + 'Your email address is not verified. Please check your email.', + 'عنوان بريدك الإلكتروني غير مُحقق. يرجى التحقق من بريدك الإلكتروني.', + ), + ); + } else { + print('Unexpected error: $e'); + } + } + + // 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.social), + ), + // surfaceTintColor: MaterialStatePropertyAll( + // MyTheme.economy[800], + // ), + foregroundColor: const WidgetStatePropertyAll( + Colors.white, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + context.translate( + 'Login', + 'تسجيل الدخول', + ), + ), + 6.horizontalSpace, + const Icon(Icons.chevron_right_outlined), + ], ), ), ); -<<<<<<< HEAD -======= final form = Form( key: formKey, child: Column( @@ -817,12 +416,7 @@ class _LoginRouteState extends State { ], ); final dontHaveAnAccountRegisterBtn = TextButton( - onPressed: () => { - Navigator.push( - context, - MaterialPageRoute(builder: (context) => RegisterScreen()), - ), - }, + onPressed: () => {context.go('/register')}, child: Text.rich( textAlign: TextAlign.center, TextSpan( @@ -940,6 +534,5 @@ class _LoginRouteState extends State { 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 ba1d5bd7..981a32fe 100644 --- a/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart +++ b/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart @@ -1,7 +1,10 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; +import 'package:pocketbase/pocketbase.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:shared_preferences/shared_preferences.dart'; -class BaseScaffold extends StatelessWidget { +class BaseScaffold extends StatefulWidget { final Widget body; final Widget title; final List? actions; @@ -12,9 +15,81 @@ class BaseScaffold extends StatelessWidget { required this.title, this.actions, }) : super(key: key); - // final Widget body; - // - // const BaseScaffold({required this.body}); + + @override + _BaseScaffoldState createState() => _BaseScaffoldState(); +} + +class _BaseScaffoldState extends State { + final _pb = PocketBase('https://pb.venbait.in'); + String _avatarUrl = ''; + dynamic userId; + String? userName; + String? userEmail; + String? userAvatar; + String? role; + + @override + void initState() { + super.initState(); + _checkUserId(); + } + + // Method to retrieve userId from SharedPreferences + Future getUserId() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getString('userId'); // Retrieve the userId + } + + // Method to check if userId exists and update state + Future _checkUserId() async { + String? fetchedUserId = await getUserId(); + if (fetchedUserId != null && fetchedUserId.isNotEmpty) { + setState(() { + userId = fetchedUserId; + }); + //print('NAVUser ID: $userId'); + _fetchUserData(); + } else { + print('No userId found'); + // Handle the case where userId is not available + } + } + + Future _fetchUserData() async { + try { + final adminAuth = await _pb.admins + .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com'); + final adminToken = adminAuth.token; + //print('adminToken- ${adminToken}'); + final userDetailsResponse = await _pb.collection('users').getOne( + userId!, + headers: { + 'Authorization': 'Bearer $adminToken', + }, + ); + print('NAVuserDetails: $userDetailsResponse'); + + setState(() { + userName = userDetailsResponse.data['username'] ?? ''; + userEmail = userDetailsResponse.data['email'] ?? ''; + userAvatar = userDetailsResponse.data['avatar'] ?? ''; + role = userDetailsResponse.data['role'] ?? ''; + String recordId = userId; + String collectionId = + userDetailsResponse.data['collectionId'] ?? '_pb_users_auth_'; + + if (userAvatar!.isNotEmpty && recordId.isNotEmpty) { + _avatarUrl = + 'https://pb.venbait.in/api/files/$collectionId/$recordId/$userAvatar'; + } else { + _avatarUrl = ''; // Reset to default or empty + } + }); + } catch (e) { + print('Error fetching user details: $e'); + } + } @override Widget build(BuildContext context) { @@ -22,15 +97,29 @@ class BaseScaffold extends StatelessWidget { String currentRoute = GoRouterState.of(context).matchedLocation; double myheight = MediaQuery.of(context).size.height; double mywidth = MediaQuery.of(context).size.width; + return Scaffold( - appBar: AppBar(title: title, actions: [ - IconButton(onPressed: () {}, icon: Icon(Icons.toggle_off_outlined)), - ]), + appBar: AppBar( + title: widget.title, + actions: [ + // IconButton( + // onPressed: () { + // // Share logic here + // print("Share icon pressed"); + // Share.share( + // 'Open the app: fcscapp://home\n\n' + // 'If you don’t have the app installed, ' + // 'visit: http://localhost:65493'); + // }, + // icon: Icon(Icons.share), + // ), + IconButton(onPressed: () {}, icon: Icon(Icons.toggle_off_outlined)), + ], + ), drawer: Drawer( child: ListView( children: [ DrawerHeader( - //decoration: BoxDecoration(color: Colors.blue), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ @@ -48,18 +137,39 @@ class BaseScaffold extends StatelessWidget { child: Row( children: [ SizedBox( - width: mywidth / 8, - child: Image( - image: AssetImage( - 'assets/edit_profile/profile.png'))), + width: mywidth / 8, + height: mywidth / 8, + child: ClipOval( + child: _avatarUrl.isNotEmpty + ? Image( + image: NetworkImage(_avatarUrl), + fit: BoxFit.cover, + width: double.infinity, + height: double.infinity, + ) + : Image( + image: AssetImage( + 'assets/edit_profile/profile.png'), + fit: BoxFit.cover, + width: double.infinity, + height: double.infinity, + ), + ), + //child: Image(image: AssetImage('assets/edit_profile/profile.png')) + ), SizedBox( width: mywidth / 20, ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('Mohammad'), - Text('Mohammad@fcsc.com') + // Text(userId ?? 'Loading user...'), // Display userId here + // Text('Mohammad@fcsc.com') + Text(userName ?? 'Loading...'), + Text( + userEmail ?? 'Loading...', + style: TextStyle(fontSize: 12), + ), ], ) ], @@ -68,24 +178,27 @@ class BaseScaffold extends StatelessWidget { ], ), ), - ListTile( - leading: SizedBox( - height: myheight / 15, - width: mywidth / 15, - child: Image( - image: AssetImage('assets/icons/drawer/message.png'))), - title: Text('Feedback'), - onTap: () => context.go('/feedback'), - ), - ListTile( - leading: SizedBox( - height: myheight / 15, - width: mywidth / 15, - child: Image( - image: AssetImage('assets/icons/drawer/manageuser.png'))), - title: Text('Manage User'), - onTap: () => context.go('/manageuser'), - ), + if (role != 'admin') + ListTile( + leading: SizedBox( + height: myheight / 15, + width: mywidth / 15, + child: Image( + image: AssetImage('assets/icons/drawer/message.png'))), + title: Text('Feedback'), + onTap: () => context.go('/feedback'), + ), + if (role == 'admin') + ListTile( + leading: SizedBox( + height: myheight / 15, + width: mywidth / 15, + child: Image( + image: + AssetImage('assets/icons/drawer/manageuser.png'))), + title: Text('Manage User'), + onTap: () => context.go('/manageuser'), + ), ], ), ), @@ -130,14 +243,14 @@ class BaseScaffold extends StatelessWidget { unselectedItemColor: Colors.grey, showUnselectedLabels: true, ), - body: body, + body: widget.body, ); } - //Map the current route to the selected index + // Map the current route to the selected index int _getSelectedIndex(String route) { switch (route) { - case '/': + case '/myhomepage': return 0; case '/about': return 1; @@ -150,11 +263,11 @@ class BaseScaffold extends StatelessWidget { } } - //Handle navigation when an item is tapped + // Handle navigation when an item is tapped void _onItemTapped(BuildContext context, int index) { switch (index) { case 0: - context.go('/'); + context.go('/myhomepage'); break; case 1: context.go('/about'); 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