diff --git a/lib/config/my_router.dart b/lib/config/my_router.dart index b3cad353..cdfe3b13 100644 --- a/lib/config/my_router.dart +++ b/lib/config/my_router.dart @@ -301,7 +301,6 @@ import '../presentation/Screens/auth_verification/changepassword.dart'; import '../presentation/Screens/auth_verification/confirm_password.dart'; import '../presentation/Screens/auth_verification/create_new_pw.dart'; import '../presentation/Screens/auth_verification/otp_verification.dart'; -import '../presentation/Screens/auth_verification/registration.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'; @@ -312,21 +311,13 @@ final GoRouter router = GoRouter( routes: [ GoRoute( path: '/', -builder: (context, state) => LoginRoute(), - //builder: (context, state) => MyHomePage(), +//builder: (context, state) => LoginRoute(), + builder: (context, state) => MyHomePage(), ), GoRoute( path: '/myhomepage', builder: (context, state) => MyHomePage(), ), - GoRoute( - path: '/login', - builder: (context, state) => LoginRoute(), - ), - GoRoute( - path: '/registration', - builder: (context, state) => RegisterScreen(), - ), GoRoute( path: '/mailverification', builder: (context, state) => EmailVerificationScreen( 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 94158760..97204028 100644 --- a/lib/presentation/Screens/auth_verification/registration.dart +++ b/lib/presentation/Screens/auth_verification/registration.dart @@ -582,8 +582,8 @@ class _RegisterScreenState extends State { fontSize: 16, color: Colors.white), ), SizedBox(width: 8), - Icon(Icons.arrow_forward_ios, - color: Colors.white,size: 16,), + Icon(Icons.arrow_forward, + color: Colors.white), ], ), ), @@ -599,7 +599,6 @@ class _RegisterScreenState extends State { width: screenwidth / 1.3, child: ElevatedButton( onPressed: () { - context.go('/login'); // Add your login logic here }, style: ElevatedButton.styleFrom( @@ -626,20 +625,31 @@ class _RegisterScreenState extends State { // ], // ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - "Login", - style: TextStyle( - fontSize: 16, - color: Colors.white, + child: GestureDetector( + onTap: () { + // Navigate to LoginRoute + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => LoginRoute()), + // MaterialPageRoute(builder: (context) => LoginRoute(userId: userID)), + ); + }, + 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: 16,), - ], + SizedBox(width: 8), + Icon(Icons.arrow_forward, + color: Colors.white), + ], + ), ), ), ), 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 49e86082..cbf28f0a 100644 --- a/lib/presentation/routes/auth_routes/login_route.dart +++ b/lib/presentation/routes/auth_routes/login_route.dart @@ -740,205 +740,206 @@ class _LoginRouteState extends State { ), ), ); - -// 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 79c919c0..deab917f 100644 --- a/lib/presentation/routes/drawer_routes/Drawer Items/manage_users.dart +++ b/lib/presentation/routes/drawer_routes/Drawer Items/manage_users.dart @@ -43,9 +43,7 @@ class _ManageUserRouterState extends State { try { await _pb.admins .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com'); - final result = await _pb.collection('users').getFullList( - filter: 'verified = false', - ); + final result = await _pb.collection('users').getFullList(); setState(() { userData = result.map((record) { @@ -178,6 +176,8 @@ class _ManageUserRouterState extends State { user.id, // User's unique ID 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 cdd32697..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,11 +243,11 @@ 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 '/myhomepage': @@ -150,7 +263,7 @@ 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: 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