diff --git a/lib/domain/use_cases/auth_use_case.dart b/lib/domain/use_cases/auth_use_case.dart index a6d27b8c..72fc6c01 100644 --- a/lib/domain/use_cases/auth_use_case.dart +++ b/lib/domain/use_cases/auth_use_case.dart @@ -56,7 +56,7 @@ class AuthUseCase extends _$AuthUseCase { ); } - Future login(String email, String pw) async { + Future login(String email, String pw) async { final session = await _repo.login(email, pw); final localPrefs = await getIt.call().get(); if (localPrefs != null) { @@ -66,6 +66,7 @@ class AuthUseCase extends _$AuthUseCase { ); } state = AsyncData(session); + return session; // Return the session with user details. } Future logout() async { diff --git a/lib/main.dart b/lib/main.dart index dab16a3b..df1ec5b7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -16,34 +16,25 @@ void main() async { // await (await SharedPreferences.getInstance()).clear(); configureDependencies(); runApp( - ProviderScope( + const ProviderScope( child: MainApp(), ), ); } class MainApp extends StatelessWidget { + const MainApp({super.key}); + @override Widget build(BuildContext context) { - return MaterialApp.router( - routerConfig: router, // Use the GoRouter configuration + return MaterialApp( + home: RegisterScreen(), + //home: ProfileScreen(), + debugShowCheckedModeBanner: false, ); } } -// class MainApp extends StatelessWidget { -// const MainApp({super.key}); -// -// @override -// Widget build(BuildContext context) { -// return MaterialApp( -// home: RegisterScreen(), -// //home: ProfileScreen(), -// debugShowCheckedModeBanner: false, -// ); -// } -// } - // class MainApp extends ConsumerWidget { // const MainApp({super.key}); @@ -103,5 +94,3 @@ class MainApp extends StatelessWidget { // return authFinalizedWidget; // } // } - - diff --git a/lib/presentation/Screens/changepassword.dart b/lib/presentation/Screens/changepassword.dart index 3a66ed5f..702da9a2 100644 --- a/lib/presentation/Screens/changepassword.dart +++ b/lib/presentation/Screens/changepassword.dart @@ -1,12 +1,16 @@ +import 'dart:math'; + import 'dart:convert'; import 'package:go_router/go_router.dart'; import 'package:http/http.dart' as http; import 'package:flutter/material.dart'; import 'package:pocketbase/pocketbase.dart'; -//import 'otp_verification.dart'; +import 'otp_verification.dart'; class Changepassword extends StatefulWidget { - const Changepassword({Key? key}) : super(key: key); + // final String email; + final String userId; + const Changepassword({Key? key, required this.userId}); @override State createState() => _ResetPasswordScreenState(); @@ -76,42 +80,39 @@ class _ResetPasswordScreenState extends State { final otp = otpData['otp']; // OTP value from the response final otpId = otpData['id']; _otpId = otpData['id']; - //print('OTP: $otp, ID: $otpId, '); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Verification code sent to $email")), ); + // Navigate to the Email Verification screen - // Navigator.push( - // context, - // MaterialPageRoute( - // builder: (context) => EmailVerificationScreen( - // email: email, - // userId: userId, // Pass user ID to the next screen - // otp: otp, - // otpId: otpId, - // sendVerificationCode: sendVerificationCode, - // ), - // ), - // ); + 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, + ), + ), + ); } else { - context.go('/Profile'); final error = jsonDecode(otpResponse.body)['error'] ?? "Unknown error"; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Error: $error")), ); } - } - else { + } else { // Email not found ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Please Enter Registered Mail ID")), ); } - } - else { + } else { // Error in user collection request final error = jsonDecode(userCheckResponse.body)['error'] ?? "Unknown error"; @@ -119,13 +120,11 @@ class _ResetPasswordScreenState extends State { SnackBar(content: Text("Error: $error")), ); } - } - catch (e) { + } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Failed to process request: $e")), ); - } - finally { + } finally { setState(() { _isLoading = false; }); @@ -238,15 +237,12 @@ class _ResetPasswordScreenState extends State { width: double.infinity, height: 48, child: ElevatedButton( - onPressed: (){ - context.go('/mailverification'); - }, - // _isLoading - // ? null - // : () { - // final email = _emailController.text.trim(); - // sendVerificationCode(email); - // }, + onPressed: _isLoading + ? null + : () { + final email = _emailController.text.trim(); + sendVerificationCode(email); + }, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF8B7355), foregroundColor: Colors.white, diff --git a/lib/presentation/Screens/confirm_password.dart b/lib/presentation/Screens/confirm_password.dart index 64a82993..496daef0 100644 --- a/lib/presentation/Screens/confirm_password.dart +++ b/lib/presentation/Screens/confirm_password.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; import 'package:pocketbase/pocketbase.dart'; import 'package:uae_stat/presentation/Screens/profilepage.dart'; @@ -72,10 +71,14 @@ class _ConfirmPasswordState extends State { } // Update password - await _pb.collection('users').update( - userId, // User ID - body: {'password': newPassword}, // Updated password - ); + await pb.collection('users').update( + userId, // User ID + body: { + 'password': newPassword, + 'passwordConfirm': newPassword, + }, // Updated password + headers: headers, + ); // Show success Snackbar ScaffoldMessenger.of(context).showSnackBar( @@ -202,22 +205,18 @@ class _ConfirmPasswordState extends State { SizedBox( width: screenwidth / 1.1, child: ElevatedButton( - onPressed: () - { - context.go('/myhomepage'); + onPressed: () async { + if ((_formKey.currentState?.validate() ?? false)) { + //String userId = '4hai9cbn4lg6jt4'; // Replace with the actual user ID + String newPassword = _password ?? + ''; // Replace with the new password + + //print("Confirm Passwd- $widget.userId, $newPassword"); + + await updatePassword(widget.userId, newPassword); + } + // Add verification logic here }, - // async { - // if ((_formKey.currentState?.validate() ?? false)) { - // //String userId = '4hai9cbn4lg6jt4'; // Replace with the actual user ID - // String newPassword = _password ?? - // ''; // Replace with the new password - // - // //print("Confirm Passwd- $widget.userId, $newPassword"); - // - // await updatePassword(widget.userId, newPassword); - // } - // // Add verification logic here - // }, style: ElevatedButton.styleFrom( backgroundColor: Colors.brown[300], padding: EdgeInsets.symmetric( diff --git a/lib/presentation/Screens/demo_home.dart b/lib/presentation/Screens/demo_home.dart new file mode 100644 index 00000000..402bc535 --- /dev/null +++ b/lib/presentation/Screens/demo_home.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import 'dart:async'; + +import 'package:uae_stat/domain/use_cases/language.dart'; + +import '../components/my_drawer.dart'; + +class DemoHome extends StatefulWidget { + @override + State createState() => _DemoHomeState(); +} + +class _DemoHomeState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + drawer: const MyDrawer(), + appBar: AppBar( + backgroundColor: const Color(0xFFf8f9ff), + title: Text(context.translate('Home', 'نموذج الملاحظات')), + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Center( + child: Text('Home Page'), + )), + ); + } +} diff --git a/lib/presentation/Screens/otp_verification.dart b/lib/presentation/Screens/otp_verification.dart index 61e329f5..f5c4c6c8 100644 --- a/lib/presentation/Screens/otp_verification.dart +++ b/lib/presentation/Screens/otp_verification.dart @@ -1,9 +1,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; import 'package:pocketbase/pocketbase.dart'; -import 'package:uae_stat/config/my_router.dart'; import 'confirm_password.dart'; @@ -216,20 +214,17 @@ class _EmailVerificationScreenState extends State { SizedBox( width: screenwidth / 1.2, child: ElevatedButton( - onPressed: () { - context.go('/confirmpasswd'); - }, - //async { + onPressed: () async { // Combine the entered OTP - // String enteredOTP = _otpControllers - // .map((controller) => controller.text) - // .join(); - // - // verify( - // context, widget.email, enteredOTP, widget.userId); - // - // // Add verification logic here - // }, + String enteredOTP = _otpControllers + .map((controller) => controller.text) + .join(); + + verify( + context, widget.email, enteredOTP, widget.userId); + + // Add verification logic here + }, style: ElevatedButton.styleFrom( backgroundColor: Colors.brown[300], padding: diff --git a/lib/presentation/Screens/profilepage.dart b/lib/presentation/Screens/profilepage.dart index 4208a0a1..f0ab349f 100644 --- a/lib/presentation/Screens/profilepage.dart +++ b/lib/presentation/Screens/profilepage.dart @@ -1,18 +1,14 @@ import 'dart:convert'; import 'dart:io'; -import 'dart:math'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:go_router/go_router.dart'; import 'package:http/http.dart' as http; import 'package:image_picker/image_picker.dart'; import 'package:intl/intl.dart'; -//import 'package:pocketbase/pocketbase.dart'; +import 'package:pocketbase/pocketbase.dart'; -import '../../domain/use_cases/preferences_use_case.dart'; -import '../routes/bottom_bar_routes/tab_routes/home_route.dart'; import 'changepassword.dart'; class ProfileScreen extends StatefulWidget { @@ -24,8 +20,7 @@ class ProfileScreen extends StatefulWidget { } class _ProfileScreenState extends State { - final defaultLanguage = PreferencesUseCase.defaultPrefs.language.name; - //final _pb = PocketBase('https://pb.venbait.in'); + final _pb = PocketBase('https://pb.venbait.in'); // final _pb = PocketBase('http://127.0.0.1:8090'); final _formKey = GlobalKey(); final TextEditingController _fullNameController = TextEditingController(); @@ -51,42 +46,42 @@ class _ProfileScreenState extends State { // Regular expression to validate Full Name (no special characters) final RegExp _nameRegExp = RegExp(r"^[a-zA-Z\s]+$"); - //late Future userDetails; + late Future userDetails; @override - // void initState() { - // super.initState(); - // print(widget.userId); - // _fetchUserData(); // Call the function to fetch user data - // } + void initState() { + super.initState(); + print(widget.userId); + _fetchUserData(); // Call the function to fetch user data + } @override - // void dispose() { - // _fullNameController.dispose(); - // _dateController.dispose(); - // super.dispose(); - // } + void dispose() { + _fullNameController.dispose(); + _dateController.dispose(); + super.dispose(); + } - // Future _fetchUserData() async { - // try { - // final adminAuth = await _pb.admins - // .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com'); - // final adminToken = adminAuth.token; - // final userDetailsResponse = await _pb.collection('users').getOne( - // widget.userId, - // headers: { - // 'Authorization': 'Bearer $adminToken', - // }, - // ); - // print('userDetails: $userDetailsResponse'); - // setState(() { - // _usernameController.text = userDetailsResponse.data['username'] ?? ''; - // _emailController.text = userDetailsResponse.data['email'] ?? ''; - // }); - // } catch (e) { - // print('Error fetching user details: $e'); - // } - // } + Future _fetchUserData() async { + try { + final adminAuth = await _pb.admins + .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com'); + final adminToken = adminAuth.token; + final userDetailsResponse = await _pb.collection('users').getOne( + widget.userId, + headers: { + 'Authorization': 'Bearer $adminToken', + }, + ); + print('userDetails: $userDetailsResponse'); + setState(() { + _usernameController.text = userDetailsResponse.data['username'] ?? ''; + _emailController.text = userDetailsResponse.data['email'] ?? ''; + }); + } catch (e) { + print('Error fetching user details: $e'); + } + } void _pickImage() async { final XFile? pickedFile = @@ -178,30 +173,30 @@ class _ProfileScreenState extends State { String formattedDob = DateFormat('yyyy-MM-dd').format(dob); // Create a multipart request - // final uri = - // Uri.parse('${_pb.baseUrl}/api/collections/users/records/$userID'); - // final request = http.MultipartRequest('PATCH', uri); + final uri = + Uri.parse('${_pb.baseUrl}/api/collections/users/records/$userID'); + final request = http.MultipartRequest('PATCH', uri); // Add other fields - // request.fields['full_name'] = fullName; - // request.fields['dob'] = formattedDob; - // request.fields['country_region'] = countryRegion; - // request.fields['is_profile_completed'] = 'True'; + request.fields['full_name'] = fullName; + request.fields['dob'] = formattedDob; + request.fields['country_region'] = countryRegion; + request.fields['is_profile_completed'] = 'True'; // Add the file, if available - // if (_profileImage != null) { - // request.files.add(await http.MultipartFile.fromPath( - // 'avatar', - // _profileImage!.path, - // )); - // } + if (_profileImage != null) { + request.files.add(await http.MultipartFile.fromPath( + 'avatar', + _profileImage!.path, + )); + } // Add headers (if required, e.g., authorization) - //request.headers['Authorization'] = 'Bearer ${_pb.authStore.token}'; + request.headers['Authorization'] = 'Bearer ${_pb.authStore.token}'; // Send the request - //final response = await request.send(); - //print(response); + final response = await request.send(); + print(response); _resetFormFields(); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Profile updated successfully!"))); @@ -520,7 +515,11 @@ class _ProfileScreenState extends State { Center( child: GestureDetector( onTap: () { - context.go('/changepass'); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => Changepassword()), + ); }, child: Text( "Change Password", @@ -533,18 +532,16 @@ class _ProfileScreenState extends State { SizedBox(height: 20), ElevatedButton.icon( onPressed: () { - context.go('/myhomepage'); - // if ((_formKey.currentState?.validate() ?? false) && - // (isChecked)) { - // _formKey.currentState?.save(); - // showConfirmationDialog(context); - // } else { - // setState(() { - // showError = - // !isChecked; // Show error if the checkbox is not checked - // }); - // } - }, + if ((_formKey.currentState?.validate() ?? false) && + (isChecked)) { + _formKey.currentState?.save(); + showConfirmationDialog(context); + } else { + setState(() { + showError = + !isChecked; // Show error if the checkbox is not checked + }); + } // if ((_formKey.currentState?.validate() ?? false) && (isChecked)) { // _formKey.currentState?.save(); // showConfirmationDialog(context); @@ -563,7 +560,7 @@ class _ProfileScreenState extends State { // ); //} //} - //}, + }, icon: Icon( Icons.save, color: Colors.white, @@ -668,8 +665,7 @@ class ConfirmationDialog extends StatelessWidget { ), ), ElevatedButton( - onPressed: () => context.go('/home'), - //Navigator.of(context).pop(true), + onPressed: () => Navigator.of(context).pop(true), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF92722A), // Brown color foregroundColor: Colors.white, diff --git a/lib/presentation/Screens/registration.dart b/lib/presentation/Screens/registration.dart index d87b0562..a4f18a07 100644 --- a/lib/presentation/Screens/registration.dart +++ b/lib/presentation/Screens/registration.dart @@ -1,3 +1,4 @@ +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:go_router/go_router.dart'; @@ -6,6 +7,7 @@ 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 { @override @@ -17,6 +19,8 @@ class _RegisterScreenState extends State { final _usernameController = TextEditingController(); final _emailController = TextEditingController(); final _passwordController = TextEditingController(); + final _confirmpasswordController = TextEditingController(); + bool _obscurePassword = true; bool _obscureConfirmPassword = true; bool isChecked = false; @@ -25,56 +29,153 @@ class _RegisterScreenState extends State { bool registrationSuccess = false; bool registrationFailed = false; dynamic userID; + final pb = PocketBase('https://pb.venbait.in'); // final pb = PocketBase('http://127.0.0.1:8090'); + // Add focus nodes and hint states + final List _focusNodes = List.generate(4, (_) => FocusNode()); + final List _showHints = [ + true, + true, + true, + true + ]; // One for each TextField + + String _getControllerText(int index) { + if (index == 0) return _usernameController.text; + if (index == 1) return _emailController.text; + if (index == 2) return _passwordController.text; + if (index == 3) return _confirmpasswordController.text; + return ''; + } + @override void initState() { super.initState(); + + // Add listeners for focus nodes + for (int i = 0; i < _focusNodes.length; i++) { + _focusNodes[i].addListener(() { + setState(() { + // Hide hint when focused and text is not empty + _showHints[i] = + !_focusNodes[i].hasFocus && (_getControllerText(i).isEmpty); + }); + }); + + // Listen to text changes + if (i == 0) { + _usernameController.addListener(() { + setState(() { + _showHints[i] = + !_focusNodes[i].hasFocus && (_getControllerText(i).isEmpty); + }); + }); + } else if (i == 1) { + _emailController.addListener(() { + setState(() { + _showHints[i] = + !_focusNodes[i].hasFocus && (_getControllerText(i).isEmpty); + }); + }); + } else if (i == 2) { + _passwordController.addListener(() { + setState(() { + _showHints[i] = + !_focusNodes[i].hasFocus && (_getControllerText(i).isEmpty); + }); + }); + } else if (i == 3) { + _confirmpasswordController.addListener(() { + setState(() { + _showHints[i] = + !_focusNodes[i].hasFocus && (_getControllerText(i).isEmpty); + }); + }); + } + } } @override void dispose() { + for (var focusNode in _focusNodes) { + focusNode.dispose(); + } _usernameController.dispose(); _emailController.dispose(); _passwordController.dispose(); + _confirmpasswordController.dispose(); super.dispose(); } String? _validateUsername(String? value) { if (value == null || value.isEmpty) { return 'Required'; - } else if (RegExp(r'[^a-zA-Z0-9]').hasMatch(value)) { + } else if (value.length > 40) { + return 'Must not exceed 40 characters'; + } else if (!RegExp(r"^[a-zA-Z0-9'\-\u0627\u0639~\u00C0-\u00FF]+$") + .hasMatch(value)) { return 'Invalid Characters'; } return null; } + //else if (RegExp(r'[^a-zA-Z0-9]').hasMatch(value)) + String? _validateEmail(String? value) { + // Regex to allow special characters, accented characters, and alphanumeric characters + final emailRegex = + r'^[a-zA-Z0-9àèìòùÀÈÌÒÙéàç~!#$%^&*()_+=-{}|;,.?<>]+@[a-zA-Z0-9àèìòùÀÈÌÒÙéàç~!#$%^&*()_+=-{}|;,.?<>]+\.[a-zA-Z]{2,}$'; + if (value == null || value.isEmpty) { return 'Required'; - } else if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) { + } else if (!RegExp(emailRegex).hasMatch(value)) { return 'Invalid Email'; } 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 < 6) { - return 'Password must be at least 6 characters'; + } 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? _validateConfirmPassword(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 || value.length > 40) { + return 'Password must be at least 8characters'; + } else if (!regex.hasMatch(value)) { + return 'Password contains invalid characters'; } else if (value != _password) { return 'Passwords do not match'; } + return null; } @@ -84,14 +185,16 @@ class _RegisterScreenState extends State { try { final adminAuth = await pb.admins .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com'); + final adminToken = adminAuth.token; + print('adminToken- ${adminToken}'); // Create user in PocketBase final response = await pb.collection('users').create(body: { 'username': _usernameController.text, 'email': _emailController.text, 'password': _passwordController.text, - 'passwordConfirm': _passwordController - .text, // PocketBase requires password confirmation + 'passwordConfirm': _passwordController.text, + // 'verified': true, }, headers: { 'Authorization': adminToken }); @@ -194,9 +297,10 @@ class _RegisterScreenState extends State { onPressed: () => { Navigator.pushReplacement( context, - MaterialPageRoute( - builder: (context) => - ProfileScreen(userId: userID)), + MaterialPageRoute(builder: (context) => LoginRoute() + + //ProfileScreen(userId: userID) + ), ), }, child: Text( @@ -247,9 +351,17 @@ class _RegisterScreenState extends State { ), SizedBox(height: 10), ElevatedButton( - onPressed: () => context.go( - '/${context.language}/login', - ), + onPressed: () => { + setState(() { + registrationFailed = false; + registrationSuccess = false; + _usernameController.clear(); + _emailController.clear(); + _passwordController.clear(); + _confirmpasswordController.clear(); + isChecked = false; + }) + }, child: Text( 'Retry', ), @@ -286,12 +398,14 @@ class _RegisterScreenState extends State { ), SizedBox(height: 10), Text('Please enter your details'), + SizedBox(height: 20), // Display this if registration is pending approval TextFormField( controller: _usernameController, + focusNode: _focusNodes[0], decoration: InputDecoration( - hintText: 'Username', + hintText: _showHints[0] ? 'Username' : null, prefixIcon: Icon( Icons.person, color: Colors.blue, @@ -307,22 +421,34 @@ class _RegisterScreenState extends State { SizedBox(height: 15), TextFormField( controller: _emailController, + focusNode: _focusNodes[1], decoration: InputDecoration( - hintText: 'Enter your email', + // hintText: 'Enter your email', + hintText: + _showHints[1] ? 'Enter your email' : null, prefixIcon: Icon( Icons.email, color: Colors.blue, ), border: OutlineInputBorder(), + counterText: '', ), validator: _validateEmail, + maxLength: 320, + maxLengthEnforcement: MaxLengthEnforcement.enforced, + inputFormatters: [ + LengthLimitingTextInputFormatter( + 320), // Limit to 320 characters + ], ), SizedBox(height: 15), TextFormField( controller: _passwordController, + focusNode: _focusNodes[2], obscureText: _obscurePassword, decoration: InputDecoration( - hintText: 'Enter your password', + hintText: + _showHints[2] ? 'Enter your password' : null, prefixIcon: Icon( Icons.lock, color: Colors.blue, @@ -341,14 +467,20 @@ class _RegisterScreenState extends State { }, ), border: OutlineInputBorder(), + counterText: '', ), validator: _validatePassword, + maxLength: 40, + maxLengthEnforcement: MaxLengthEnforcement.enforced, ), SizedBox(height: 15), TextFormField( + controller: _confirmpasswordController, + focusNode: _focusNodes[3], obscureText: _obscureConfirmPassword, decoration: InputDecoration( - hintText: 'Confirm password', + hintText: + _showHints[3] ? 'Confirm password' : null, prefixIcon: Icon( Icons.lock, color: Colors.blue, @@ -368,8 +500,15 @@ class _RegisterScreenState extends State { }, ), border: OutlineInputBorder(), + counterText: '', ), validator: _validateConfirmPassword, + maxLength: 40, + maxLengthEnforcement: MaxLengthEnforcement.enforced, + inputFormatters: [ + LengthLimitingTextInputFormatter( + 64), // Limit to 40 characters + ], ), SizedBox(height: 10), Row( @@ -414,7 +553,7 @@ class _RegisterScreenState extends State { Padding( padding: const EdgeInsets.only(left: 10.0), child: Text( - 'Required', + 'Please agree to terms and conditions.', style: TextStyle( color: Colors.red[700], fontSize: 12, @@ -470,18 +609,48 @@ class _RegisterScreenState extends State { 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, - color: Colors.white), - ], + // child: Row( + // mainAxisSize: MainAxisSize.min, + // children: [ + // Text( + // "Login", + // style: TextStyle( + // fontSize: 16, color: Colors.white + // ), + // + // + // ), + // SizedBox(width: 8), + // Icon(Icons.arrow_forward, + // 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, + color: Colors.white), + ], + ), ), ), ), diff --git a/lib/presentation/routes/auth_routes/login_route.dart b/lib/presentation/routes/auth_routes/login_route.dart index d05e1348..3460183b 100644 --- a/lib/presentation/routes/auth_routes/login_route.dart +++ b/lib/presentation/routes/auth_routes/login_route.dart @@ -9,18 +9,57 @@ import 'package:uae_stat/domain/use_cases/auth_use_case.dart'; import 'package:uae_stat/domain/use_cases/language.dart'; import 'package:uae_stat/infrastructure/services/img_asset_paths/banner_asset_path.dart'; import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/misc_icon_asset_path.dart'; +import 'package:uae_stat/presentation/Screens/demo_home.dart'; import 'package:uae_stat/presentation/components/dialogs.dart'; import 'package:uae_stat/presentation/components/lang_toggle.dart'; import 'package:uae_stat/presentation/components/my_bottom_nav_bar.dart'; import 'package:uae_stat/presentation/components/space.dart'; import 'package:uae_stat/presentation/components/themed_text_field.dart'; -import 'package:uae_stat/presentation/routes/drawer_routes/profile/profile_route.dart'; +import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart'; + +import '../../Screens/profilepage.dart'; +import '../../Screens/registration.dart'; +import 'package:pocketbase/pocketbase.dart'; class LoginRoute extends HookConsumerWidget { - const LoginRoute({super.key}); + 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(); @@ -119,76 +158,95 @@ class LoginRoute extends HookConsumerWidget { width: double.infinity, child: ElevatedButton( onPressed: () async { - // final isValid = formKey.currentState!.validate(); - // if (!isValid) return; - // 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) return; + 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; + // 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}'); - context.go('/Profile'); }, style: ButtonStyle( shape: WidgetStatePropertyAll( @@ -224,7 +282,7 @@ class LoginRoute extends HookConsumerWidget { children: [ Text( context.translate( - 'Login log', + 'Login', 'تسجيل الدخول', ), ), @@ -309,7 +367,12 @@ class LoginRoute extends HookConsumerWidget { ], ); final dontHaveAnAccountRegisterBtn = TextButton( - onPressed: () => context.go('/${context.language}/register'), + onPressed: () => { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => RegisterScreen()), + ), + }, child: Text.rich( textAlign: TextAlign.center, TextSpan(