From acaa9f046c646e7beba5544ba6a9bb26ecb0056f Mon Sep 17 00:00:00 2001 From: venbaittech Date: Thu, 14 Nov 2024 10:15:20 +0530 Subject: [PATCH 1/3] register functionality added --- .../services/pocketbase_service.dart | 2 +- lib/presentation/Screens/profilepage.dart | 62 ++++----- lib/presentation/Screens/registration.dart | 131 +++++++++++++----- 3 files changed, 130 insertions(+), 65 deletions(-) diff --git a/lib/infrastructure/services/pocketbase_service.dart b/lib/infrastructure/services/pocketbase_service.dart index 02d70cb0..20fb6ace 100644 --- a/lib/infrastructure/services/pocketbase_service.dart +++ b/lib/infrastructure/services/pocketbase_service.dart @@ -3,7 +3,7 @@ import 'dart:convert'; import 'package:pocketbase/pocketbase.dart'; abstract class PocketBaseService { - static const _host = 'https://pocket.fcsc.gov.ae'; + static const _host = 'https://pb.venbait.in'; // static const _host = 'http://127.0.0.1:8090'; static final _pb = PocketBase(_host); static final users = _pb.collection('users'); diff --git a/lib/presentation/Screens/profilepage.dart b/lib/presentation/Screens/profilepage.dart index f39ca581..26e3a013 100644 --- a/lib/presentation/Screens/profilepage.dart +++ b/lib/presentation/Screens/profilepage.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:http/http.dart' as http; import 'package:image_picker/image_picker.dart'; import 'package:intl/intl.dart'; @@ -11,6 +12,9 @@ import 'package:pocketbase/pocketbase.dart'; import 'changepassword.dart'; class ProfileScreen extends StatefulWidget { + final String userId; // Add this field to hold the user ID + + const ProfileScreen({Key? key, required this.userId}) : super(key: key); @override State createState() => _ProfileScreenState(); } @@ -42,10 +46,12 @@ 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; @override void initState() { super.initState(); + print(widget.userId); _fetchUserData(); // Call the function to fetch user data } @@ -56,32 +62,24 @@ class _ProfileScreenState extends State { super.dispose(); } - void _fetchUserData() async { + Future _fetchUserData() async { try { - final userId = 'tsgehyvgy6owypj'; final adminAuth = await _pb.admins .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com'); final adminToken = adminAuth.token; - final response = await http.get( - Uri.parse( - 'https://pb.venbait.in/api/collections/users/records/$userId'), + final userDetailsResponse = await _pb.collection('users').getOne( + widget.userId, headers: { - 'Authorization': 'Bearer $adminToken', // Add token to header - 'Content-Type': 'application/json', + 'Authorization': 'Bearer $adminToken', }, ); - - if (response.statusCode == 200) { - final data = jsonDecode(response.body); - setState(() { - _usernameController.text = data['username']; - _emailController.text = data['email']; - }); - } else { - print('Failed to fetch user data: ${response.body}'); - } - } catch (error) { - print('Failed to fetch user data: $error'); + print('userDetails: $userDetailsResponse'); + setState(() { + _usernameController.text = userDetailsResponse.data['username'] ?? ''; + _emailController.text = userDetailsResponse.data['email'] ?? ''; + }); + } catch (e) { + print('Error fetching user details: $e'); } } @@ -160,7 +158,7 @@ class _ProfileScreenState extends State { if (result == true) { if (_formKey.currentState?.validate() ?? false) { try { - String userID = 'tsgehyvgy6owypj'; + String userID = widget.userId; // Retrieve data from text fields and other inputs String fullName = _fullNameController.text; // String username = 'users55538'; @@ -208,7 +206,7 @@ class _ProfileScreenState extends State { if (response.statusCode == 200) { print(response.statusCode); _resetFormFields(); - Navigator.pushNamed(context, 'home'); + // Navigator.pushNamed(context, 'home'); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Profile updated successfully!"))); } else { @@ -379,7 +377,10 @@ class _ProfileScreenState extends State { border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), ), + counterText: '', ), + maxLength: 40, // Set the maximum length to 20 characters + maxLengthEnforcement: MaxLengthEnforcement.enforced, ), SizedBox(height: 10), Row( @@ -526,7 +527,6 @@ class _ProfileScreenState extends State { ), ], ), - SizedBox(height: 20), Center( child: GestureDetector( @@ -548,14 +548,14 @@ class _ProfileScreenState extends State { SizedBox(height: 20), ElevatedButton.icon( onPressed: () { - - if((_formKey.currentState?.validate() ?? false) && (isChecked)){ + if ((_formKey.currentState?.validate() ?? false) && + (isChecked)) { _formKey.currentState?.save(); showConfirmationDialog(context); - } - else { + } else { setState(() { - showError = !isChecked; // Show error if the checkbox is not checked + showError = + !isChecked; // Show error if the checkbox is not checked }); } // if ((_formKey.currentState?.validate() ?? false) && (isChecked)) { @@ -571,10 +571,10 @@ class _ProfileScreenState extends State { // showError = !isChecked; // Show error if the checkbox is not checked // }); - // ScaffoldMessenger.of(context).showSnackBar( - // SnackBar(content: Text('Please accept the terms and conditions.',style: TextStyle(color: Colors.white),)), - // ); - //} + // ScaffoldMessenger.of(context).showSnackBar( + // SnackBar(content: Text('Please accept the terms and conditions.',style: TextStyle(color: Colors.white),)), + // ); + //} //} }, icon: Icon( diff --git a/lib/presentation/Screens/registration.dart b/lib/presentation/Screens/registration.dart index fea5bcd8..30507d83 100644 --- a/lib/presentation/Screens/registration.dart +++ b/lib/presentation/Screens/registration.dart @@ -1,6 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:pocketbase/pocketbase.dart'; import 'package:uae_stat/presentation/Screens/profilepage.dart'; +import '../../infrastructure/services/pocketbase_service.dart'; + class RegisterScreen extends StatefulWidget { @override _RegisterScreenState createState() => _RegisterScreenState(); @@ -8,13 +12,30 @@ class RegisterScreen extends StatefulWidget { class _RegisterScreenState extends State { final _formKey = GlobalKey(); + final _usernameController = TextEditingController(); + final _emailController = TextEditingController(); + final _passwordController = TextEditingController(); bool _obscurePassword = true; bool _obscureConfirmPassword = true; bool isChecked = false; bool showError = false; - String? _password; + final pb = PocketBase('https://pb.venbait.in'); + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + _usernameController.dispose(); + _emailController.dispose(); + _passwordController.dispose(); + super.dispose(); + } + String? _validateUsername(String? value) { if (value == null || value.isEmpty) { return 'Required'; @@ -52,6 +73,53 @@ class _RegisterScreenState extends State { return null; } + Future _registerUser() async { + if (_formKey.currentState?.validate() ?? false) { + if (isChecked) { + try { + // 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 + }); + if (response.id != null) { + // Request email verification + PocketBaseService.users.requestVerification(_emailController.text); + + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Registration Successed')), + ); + + // 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'); + } + } catch (e) { + // Handle registration error + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Registration failed: $e')), + ); + } + } else { + setState(() { + showError = true; // Show error if checkbox is not checked + }); + } + } else { + setState(() { + showError = !isChecked; // Show error if the checkbox is not checked + }); + } + } + @override Widget build(BuildContext context) { double screenheight = MediaQuery.of(context).size.height; @@ -75,6 +143,7 @@ class _RegisterScreenState extends State { Text('Please enter your details'), SizedBox(height: 20), TextFormField( + controller: _usernameController, decoration: InputDecoration( hintText: 'Username', prefixIcon: Icon( @@ -82,11 +151,15 @@ class _RegisterScreenState extends State { color: Colors.blue, ), border: OutlineInputBorder(), + counterText: '', ), validator: _validateUsername, + maxLength: 40, // Set the maximum length to 20 characters + maxLengthEnforcement: MaxLengthEnforcement.enforced, ), SizedBox(height: 15), TextFormField( + controller: _emailController, decoration: InputDecoration( hintText: 'Enter your email', prefixIcon: Icon( @@ -99,6 +172,7 @@ class _RegisterScreenState extends State { ), SizedBox(height: 15), TextFormField( + controller: _passwordController, obscureText: _obscurePassword, decoration: InputDecoration( hintText: 'Enter your password', @@ -203,23 +277,12 @@ class _RegisterScreenState extends State { ), SizedBox(height: 20), SizedBox( - width: screenwidth/1.3, + width: screenwidth / 1.3, child: ElevatedButton( - onPressed: () { - if ((_formKey.currentState?.validate() ?? false) && isChecked) { - // Proceed with registration if form is valid and checkbox is checked - Navigator.push( - context, - MaterialPageRoute(builder: (context) => ProfileScreen()), - ); - } else { - setState(() { - showError = !isChecked; // Show error if the checkbox is not checked - }); - } - }, + onPressed: _registerUser, style: ElevatedButton.styleFrom( - backgroundColor: Color(0xFFA7887A), // Brownish color for Register + backgroundColor: + Color(0xFFA7887A), // Brownish color for Register shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), @@ -244,13 +307,14 @@ class _RegisterScreenState extends State { ), SizedBox(height: 10), SizedBox( - width: screenwidth/1.3, + width: screenwidth / 1.3, child: ElevatedButton( onPressed: () { // Add your login logic here }, style: ElevatedButton.styleFrom( - backgroundColor: Color(0xFF82AFCB), // Blueish color for Login + backgroundColor: + Color(0xFF82AFCB), // Blueish color for Login shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), @@ -268,18 +332,22 @@ class _RegisterScreenState extends State { ), ), ), - SizedBox(height: 10,), - Center( - child: Container( - height: screenheight/8, - width: screenwidth/2, - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage("assets/splash_screen/logo.png"), // Background image asset - fit: BoxFit.fill, - ), - ), - ))], + SizedBox( + height: 10, + ), + Center( + child: Container( + height: screenheight / 8, + width: screenwidth / 2, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage( + "assets/splash_screen/logo.png"), // Background image asset + fit: BoxFit.fill, + ), + ), + )) + ], ), ), ), @@ -287,6 +355,3 @@ class _RegisterScreenState extends State { ); } } - - - From e54998ac5fa3794605c33981889df20ab8768f2a Mon Sep 17 00:00:00 2001 From: venbaittech Date: Fri, 15 Nov 2024 14:32:20 +0530 Subject: [PATCH 2/3] feedback bug fix --- lib/main.dart | 3 +- lib/presentation/Screens/profilepage.dart | 63 +- lib/presentation/Screens/registration.dart | 581 +++++++++++------- .../routes/drawer_routes/feedback_route.dart | 63 +- 4 files changed, 426 insertions(+), 284 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index df1ec5b7..45a467e9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -27,7 +27,7 @@ class MainApp extends StatelessWidget { @override Widget build(BuildContext context) { - return MaterialApp( + return MaterialApp( home: RegisterScreen(), //home: ProfileScreen(), debugShowCheckedModeBanner: false, @@ -35,7 +35,6 @@ class MainApp extends StatelessWidget { } } - // class MainApp extends ConsumerWidget { // const MainApp({super.key}); // diff --git a/lib/presentation/Screens/profilepage.dart b/lib/presentation/Screens/profilepage.dart index 26e3a013..56ec11e5 100644 --- a/lib/presentation/Screens/profilepage.dart +++ b/lib/presentation/Screens/profilepage.dart @@ -172,50 +172,27 @@ class _ProfileScreenState extends State { DateTime dob = DateFormat('dd/MM/yyyy').parse(dateOfBirth); String formattedDob = DateFormat('yyyy-MM-dd').format(dob); - // Prepare form data - final request = http.MultipartRequest( - 'PATCH', - Uri.parse( - 'https://pb.venbait.in/api/collections/users/records/$userID'), // replace with actual user ID - ); + // Prepare the data to be updated + final Map userData = { + 'full_name': fullName, + 'dob': formattedDob, + 'country_region': countryRegion, + 'is_profile_completed': 'True' + }; - // Set the fields for the user profile - request.fields['full_name'] = fullName; - // request.fields['username'] = username; - // request.fields['email'] = email; - request.fields['dob'] = formattedDob; // ensure correct format - request.fields['country_region'] = countryRegion; - request.fields['terms_accepted'] = termsAccepted.toString(); - - // Add image file if selected + // If there's a profile image, upload it separately or include in userData if PocketBase supports files in the update if (_profileImage != null) { - request.files.add(await http.MultipartFile.fromPath( - 'avatar', _profileImage!.path)); + userData['avatar'] = await http.MultipartFile.fromPath( + 'avatar', _profileImage!.path); } - final adminAuth = await _pb.admins - .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com'); - final adminToken = adminAuth.token; - // Add PocketBase auth headers if needed (for authenticated requests) - request.headers['Authorization'] = 'Bearer ${adminToken}'; - print(request); - - // Send the request - final response = await request.send(); - - if (response.statusCode == 200) { - print(response.statusCode); - _resetFormFields(); - // Navigator.pushNamed(context, 'home'); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text("Profile updated successfully!"))); - } else { - // Log the response body for better debugging - final responseBody = await response.stream.bytesToString(); - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text( - "Failed to update profile: ${response.reasonPhrase}, Body: $responseBody"))); - } + // Update the user profile in PocketBase + final updatedUser = + await _pb.collection('users').update(userID, body: userData); + print(updatedUser); + _resetFormFields(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Profile updated successfully!"))); } catch (error) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Failed to update profile: $error"))); @@ -530,12 +507,12 @@ class _ProfileScreenState extends State { SizedBox(height: 20), Center( child: GestureDetector( - onTap: (){ + onTap: () { Navigator.push( context, - MaterialPageRoute(builder: (context) => Changepassword()), + MaterialPageRoute( + builder: (context) => Changepassword()), ); - }, child: Text( "Change Password", diff --git a/lib/presentation/Screens/registration.dart b/lib/presentation/Screens/registration.dart index 30507d83..ccf11118 100644 --- a/lib/presentation/Screens/registration.dart +++ b/lib/presentation/Screens/registration.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:go_router/go_router.dart'; 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'; @@ -20,7 +22,8 @@ class _RegisterScreenState extends State { bool isChecked = false; bool showError = false; String? _password; - + bool registrationSuccess = false; + bool registrationFailed = false; final pb = PocketBase('https://pb.venbait.in'); @override @@ -88,25 +91,26 @@ class _RegisterScreenState extends State { if (response.id != null) { // Request email verification PocketBaseService.users.requestVerification(_emailController.text); - - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Registration Successed')), - ); + // Show success message instead of navigating away + setState(() { + 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)), - ); + // Navigator.pushReplacement( + // context, + // MaterialPageRoute( + // builder: (context) => ProfileScreen(userId: response.id)), + // ); } else { throw Exception('User registration failed: missing user ID'); } } catch (e) { - // Handle registration error - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Registration failed: $e')), - ); + setState(() { + registrationFailed = true; + registrationSuccess = false; + }); } } else { setState(() { @@ -124,234 +128,369 @@ class _RegisterScreenState extends State { Widget build(BuildContext context) { double screenheight = MediaQuery.of(context).size.height; double screenwidth = MediaQuery.of(context).size.width; + + Widget buildIconContainer(IconData icon, Color iconColor) { + return Container( + width: 60, + height: 60, + decoration: BoxDecoration( + color: Color(0xFFF9F9F9), + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: Colors.black12, + blurRadius: 6, + offset: Offset(0, 2), + ), + ], + ), + child: Icon( + icon, + color: iconColor, + size: 30, + ), + ); + } + return Scaffold( backgroundColor: Colors.white, - 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( - 'Register', - style: TextStyle(fontSize: 32, fontWeight: FontWeight.w400), - ), - SizedBox(height: 10), - Text('Please enter your details'), - SizedBox(height: 20), - TextFormField( - controller: _usernameController, - decoration: InputDecoration( - hintText: 'Username', - prefixIcon: Icon( - Icons.person, - color: Colors.blue, - ), - border: OutlineInputBorder(), - counterText: '', + body: registrationSuccess + ? Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox(height: screenheight / 8), + buildIconContainer(Icons.report, Color(0xFF7DAFBC)), + SizedBox(height: 20), + Text( + "Your registration is pending for Admin Approval.", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: Color(0xFF414042)), ), - validator: _validateUsername, - maxLength: 40, // Set the maximum length to 20 characters - maxLengthEnforcement: MaxLengthEnforcement.enforced, - ), - SizedBox(height: 15), - TextFormField( - controller: _emailController, - decoration: InputDecoration( - hintText: 'Enter your email', - prefixIcon: Icon( - Icons.email, - color: Colors.blue, + SizedBox(height: 10), + Text( + "Access will be granted once your account is approved.", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14, + color: Color(0xFF898C81), ), - border: OutlineInputBorder(), ), - validator: _validateEmail, - ), - SizedBox(height: 15), - TextFormField( - controller: _passwordController, - obscureText: _obscurePassword, - decoration: InputDecoration( - hintText: 'Enter your password', - prefixIcon: Icon( - Icons.lock, - color: Colors.blue, + SizedBox(height: 10), + ElevatedButton( + onPressed: () => context.go( + '/${context.language}/login', ), - suffixIcon: IconButton( - icon: Icon( - _obscurePassword - ? Icons.visibility - : Icons.visibility_off, - color: Colors.blue, - ), - onPressed: () { - setState(() { - _obscurePassword = !_obscurePassword; - }); - }, + child: Text( + 'Go to Login', ), - border: OutlineInputBorder(), ), - validator: _validatePassword, - ), - SizedBox(height: 15), - TextFormField( - obscureText: _obscureConfirmPassword, - decoration: InputDecoration( - hintText: 'Confirm password', - prefixIcon: Icon( - Icons.lock, - color: Colors.blue, - ), - suffixIcon: IconButton( - icon: Icon( - _obscureConfirmPassword - ? Icons.visibility - : Icons.visibility_off, - color: Colors.blue, - ), - onPressed: () { - setState(() { - _obscureConfirmPassword = !_obscureConfirmPassword; - }); - }, - ), - border: OutlineInputBorder(), - ), - validator: _validateConfirmPassword, - ), - SizedBox(height: 10), - Row( - children: [ - Checkbox( - value: isChecked, - onChanged: (value) { - setState(() { - isChecked = value ?? false; - showError = false; - }); - }, - side: BorderSide( - color: showError ? Colors.red : Colors.grey, - width: 1.5, + SizedBox(height: screenheight / 5), + Center( + child: Container( + height: screenheight / 8, + width: screenwidth / 2, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage( + "assets/splash_screen/logo.png"), // Background image asset + fit: BoxFit.fill, ), ), - Expanded( - child: Text.rich( - TextSpan( - text: 'I agree to ', - children: [ - TextSpan( - text: 'Terms & Conditions', - style: TextStyle(color: Colors.blue), - ), - TextSpan(text: ' and '), - TextSpan( - text: 'Privacy Policy', - style: TextStyle(color: Colors.blue), - ), - ], + )) + ], + ), + ) + : registrationFailed + ? Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox(height: screenheight / 8), + buildIconContainer(Icons.report, Colors.red), + SizedBox(height: 20), + Text( + "Sorry ${_usernameController.text}!", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: Color(0xFF414042)), + ), + SizedBox(height: 10), + Text( + "Your registration process failed. For further assistance, please contact support.", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14, + color: Color(0xFF898C81), ), ), - ), - ], - ), - if (showError) - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.only(left: 10.0), + SizedBox(height: 10), + ElevatedButton( + onPressed: () => context.go( + '/${context.language}/login', + ), child: Text( - 'Required', - style: TextStyle( - color: Colors.red[700], - fontSize: 12, + 'Retry', + ), + ), + SizedBox(height: screenheight / 5), + Center( + child: Container( + height: screenheight / 8, + width: screenwidth / 2, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage( + "assets/splash_screen/logo.png"), // Background image asset + fit: BoxFit.fill, ), ), - ), + )) ], ), - SizedBox(height: 20), - SizedBox( - width: screenwidth / 1.3, - child: ElevatedButton( - onPressed: _registerUser, - style: ElevatedButton.styleFrom( - backgroundColor: - Color(0xFFA7887A), // Brownish color for Register - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), + ) + : Padding( + padding: const EdgeInsets.all(20.0), + child: Form( + key: _formKey, + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox(height: screenheight / 6), + Text( + 'Register', + style: TextStyle( + fontSize: 32, fontWeight: FontWeight.w400), + ), + SizedBox(height: 10), + Text('Please enter your details'), + SizedBox(height: 20), + // Display this if registration is pending approval + TextFormField( + controller: _usernameController, + decoration: InputDecoration( + hintText: 'Username', + prefixIcon: Icon( + Icons.person, + color: Colors.blue, + ), + border: OutlineInputBorder(), + counterText: '', + ), + validator: _validateUsername, + maxLength: + 40, // Set the maximum length to 20 characters + maxLengthEnforcement: MaxLengthEnforcement.enforced, + ), + SizedBox(height: 15), + TextFormField( + controller: _emailController, + decoration: InputDecoration( + hintText: 'Enter your email', + prefixIcon: Icon( + Icons.email, + color: Colors.blue, + ), + border: OutlineInputBorder(), + ), + validator: _validateEmail, + ), + SizedBox(height: 15), + TextFormField( + controller: _passwordController, + obscureText: _obscurePassword, + decoration: InputDecoration( + hintText: 'Enter your password', + prefixIcon: Icon( + Icons.lock, + color: Colors.blue, + ), + suffixIcon: IconButton( + icon: Icon( + _obscurePassword + ? Icons.visibility + : Icons.visibility_off, + color: Colors.blue, + ), + onPressed: () { + setState(() { + _obscurePassword = !_obscurePassword; + }); + }, + ), + border: OutlineInputBorder(), + ), + validator: _validatePassword, + ), + SizedBox(height: 15), + TextFormField( + obscureText: _obscureConfirmPassword, + decoration: InputDecoration( + hintText: 'Confirm password', + prefixIcon: Icon( + Icons.lock, + color: Colors.blue, + ), + suffixIcon: IconButton( + icon: Icon( + _obscureConfirmPassword + ? Icons.visibility + : Icons.visibility_off, + color: Colors.blue, + ), + onPressed: () { + setState(() { + _obscureConfirmPassword = + !_obscureConfirmPassword; + }); + }, + ), + border: OutlineInputBorder(), + ), + validator: _validateConfirmPassword, + ), + SizedBox(height: 10), + Row( + children: [ + Checkbox( + value: isChecked, + onChanged: (value) { + setState(() { + isChecked = value ?? false; + showError = false; + }); + }, + side: BorderSide( + color: showError ? Colors.red : Colors.grey, + width: 1.5, + ), + ), + Expanded( + child: Text.rich( + TextSpan( + text: 'I agree to ', + children: [ + TextSpan( + text: 'Terms & Conditions', + style: TextStyle(color: Colors.blue), + ), + TextSpan(text: ' and '), + TextSpan( + text: 'Privacy Policy', + style: TextStyle(color: Colors.blue), + ), + ], + ), + ), + ), + ], + ), + if (showError) + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(left: 10.0), + child: Text( + 'Required', + style: TextStyle( + color: Colors.red[700], + fontSize: 12, + ), + ), + ), + ], + ), + SizedBox(height: 20), + SizedBox( + width: screenwidth / 1.3, + child: ElevatedButton( + onPressed: _registerUser, + style: ElevatedButton.styleFrom( + backgroundColor: Color( + 0xFFA7887A), // Brownish color for Register + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Register", + style: TextStyle( + fontSize: 16, color: Colors.white), + ), + SizedBox(width: 8), + Icon(Icons.arrow_forward, + color: Colors.white), + ], + ), + ), + ), + SizedBox(height: 10), + Text( + "Already have an account?", + style: TextStyle( + fontSize: 16, color: Colors.grey[600]), + ), + SizedBox(height: 10), + SizedBox( + width: screenwidth / 1.3, + child: ElevatedButton( + onPressed: () { + // Add your login logic here + }, + style: ElevatedButton.styleFrom( + backgroundColor: Color( + 0xFF82AFCB), // Blueish color for Login + 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, + color: Colors.white), + ], + ), + ), + ), + SizedBox( + height: 10, + ), + Center( + child: Container( + height: screenheight / 8, + width: screenwidth / 2, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage( + "assets/splash_screen/logo.png"), // Background image asset + fit: BoxFit.fill, + ), + ), + )) + ], ), ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - "Register", - style: TextStyle(fontSize: 16, color: Colors.white), - ), - SizedBox(width: 8), - Icon(Icons.arrow_forward, color: Colors.white), - ], - ), ), ), - SizedBox(height: 10), - Text( - "Already have an account?", - style: TextStyle(fontSize: 16, color: Colors.grey[600]), - ), - SizedBox(height: 10), - SizedBox( - width: screenwidth / 1.3, - child: ElevatedButton( - onPressed: () { - // Add your login logic here - }, - style: ElevatedButton.styleFrom( - backgroundColor: - Color(0xFF82AFCB), // Blueish color for Login - 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, color: Colors.white), - ], - ), - ), - ), - SizedBox( - height: 10, - ), - Center( - child: Container( - height: screenheight / 8, - width: screenwidth / 2, - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage( - "assets/splash_screen/logo.png"), // Background image asset - fit: BoxFit.fill, - ), - ), - )) - ], - ), - ), - ), - ), ); } } diff --git a/lib/presentation/routes/drawer_routes/feedback_route.dart b/lib/presentation/routes/drawer_routes/feedback_route.dart index ac97ff5b..ec860155 100644 --- a/lib/presentation/routes/drawer_routes/feedback_route.dart +++ b/lib/presentation/routes/drawer_routes/feedback_route.dart @@ -25,7 +25,8 @@ class FeedbackForm extends StatefulWidget { _FeedbackFormState createState() => _FeedbackFormState(); } -class _FeedbackFormState extends State { +class _FeedbackFormState extends State + with WidgetsBindingObserver { final _pb = PocketBase('https://pb.venbait.in'); // Initialize PocketBase client // final _pb = PocketBase('http://127.0.0.1:8090'); // Initialize PocketBase client @@ -78,17 +79,34 @@ class _FeedbackFormState extends State { @override void initState() { super.initState(); + WidgetsBinding.instance.addObserver(this); _loadFeedbackText(); _feedbackController.addListener(_handleTextChange); fetchEmailConfiguration(); } + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state == AppLifecycleState.detached || + state == AppLifecycleState.paused) { + _removeFeedbackText(); + } + } + + // Detect when navigating to another page + @override + void didPushNext() { + // Called when a new page is pushed on top of FeedbackPage + _removeFeedbackText(); + } + Future _saveFeedbackText( int emojiIndex, String ratingKey, double ratingValue) async { final prefs = await SharedPreferences.getInstance(); await prefs.setString('feedbackText', _feedbackController.text); await prefs.setInt('selected_emoji_index', emojiIndex); await prefs.setDouble(ratingKey, ratingValue); + print('one save'); } Future _saveAllRatings() async { @@ -101,24 +119,36 @@ class _FeedbackFormState extends State { Future _loadFeedbackText() async { final prefs = await SharedPreferences.getInstance(); + + // Step 1: Load values from SharedPreferences into variables final feedbackText = prefs.getString('feedbackText') ?? ''; + final savedIndex = prefs.getInt('selected_emoji_index'); + final easeOfUseRating = prefs.getDouble('ease_of_use_rating') ?? 0; + final qualityRating = prefs.getDouble('quality_rating') ?? 0; + final designRating = prefs.getDouble('design_rating') ?? 0; + final redundancyRating = prefs.getDouble('redundancy_rating') ?? 0; + + // Step 2: Populate fields with values without immediately clearing storage setState(() { _feedbackController.text = feedbackText; - }); - - final savedIndex = prefs.getInt('selected_emoji_index'); - if (savedIndex != null) { - setState(() { + if (savedIndex != null) { _selectedEmojiIndex = savedIndex; _isSmileySelected = true; // Indicating the user selected an emoji - }); - } + } + _easeOfUseRating = easeOfUseRating; + _qualityRating = qualityRating; + _designRating = designRating; + _redundancyRating = redundancyRating; + }); - setState(() { - _easeOfUseRating = prefs.getDouble('ease_of_use_rating') ?? 0; - _qualityRating = prefs.getDouble('quality_rating') ?? 0; - _designRating = prefs.getDouble('design_rating') ?? 0; - _redundancyRating = prefs.getDouble('redundancy_rating') ?? 0; + // Step 3: Clear storage after a slight delay + Future.delayed(Duration(milliseconds: 50), () async { + await prefs.remove('feedbackText'); + await prefs.remove('selected_emoji_index'); + await prefs.remove('ease_of_use_rating'); + await prefs.remove('quality_rating'); + await prefs.remove('design_rating'); + await prefs.remove('redundancy_rating'); }); } @@ -622,10 +652,6 @@ class _FeedbackFormState extends State { final DateFormat formatter = DateFormat("dd.MM.yyyy HH:mm 'UTC'"); String formattedDateTime = formatter.format(feedbackDateTime); - // Assuming the server timezone as UTC, you can append as needed - String submissionTime = "$formattedDateTime "; - - print("Formatted Date (UTC): $formattedDate"); String email = configEmail; // Direct assignment // Create the email message final message = Message() @@ -641,7 +667,7 @@ class _FeedbackFormState extends State { 1. Name: Guest 2. Date of Submission: $formattedDate - 3. Time of Submission: $submissionTime + 3. Time of Submission: $formattedDateTime Feedback: @@ -676,6 +702,7 @@ class _FeedbackFormState extends State { @override void dispose() { _feedbackController.dispose(); + WidgetsBinding.instance.removeObserver(this); super.dispose(); } } From 3beae201804e19345f1639d4a597b49c2cd0a574 Mon Sep 17 00:00:00 2001 From: venbaittech Date: Sun, 24 Nov 2024 11:25:46 +0530 Subject: [PATCH 3/3] change password functionality added --- lib/presentation/Screens/changepassword.dart | 161 +++++++++++++++--- .../Screens/confirm_password.dart | 143 +++++++++++++--- .../Screens/otp_verification.dart | 153 +++++++++++++---- lib/presentation/Screens/profilepage.dart | 35 ++-- lib/presentation/Screens/registration.dart | 22 ++- .../routes/drawer_routes/feedback_route.dart | 3 +- 6 files changed, 410 insertions(+), 107 deletions(-) diff --git a/lib/presentation/Screens/changepassword.dart b/lib/presentation/Screens/changepassword.dart index f4d4c18e..8c0155e0 100644 --- a/lib/presentation/Screens/changepassword.dart +++ b/lib/presentation/Screens/changepassword.dart @@ -1,5 +1,7 @@ - +import 'dart:convert'; +import 'package:http/http.dart' as http; import 'package:flutter/material.dart'; +import 'package:pocketbase/pocketbase.dart'; import 'otp_verification.dart'; class Changepassword extends StatefulWidget { @@ -10,22 +12,118 @@ class Changepassword extends StatefulWidget { } class _ResetPasswordScreenState extends State { + final _pb = PocketBase('https://pb.venbait.in'); + // final pb = PocketBase('http://127.0.0.1:8090'); final _formKey = GlobalKey(); final _emailController = TextEditingController(); bool _isLoading = false; + String? _otpId; - void _handleSubmit() { + String? _verificationCode; + Future sendVerificationCode(String email) async { if (_formKey.currentState!.validate()) { setState(() { _isLoading = true; }); - Navigator.push( - context, - MaterialPageRoute(builder: (context) => EmailVerificationScreen()), - ); - // TODO: Implement your password reset logic here - // After API call, set _isLoading back to false + final email = _emailController.text.trim(); + + //print('Email - $email'); + + try { + // Authenticate admin to get the token + final adminAuth = await _pb.admins + .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com'); + final authToken = adminAuth.token; + + //print('Admin Token: $authToken'); + + // Check if email exists in the user collection + final userCheckResponse = await http.get( + Uri.parse( + 'https://pb.venbait.in/api/collections/users/records?filter=email="$email"'), + headers: { + 'Authorization': 'Bearer $authToken', + }, + ); + + if (userCheckResponse.statusCode == 200) { + final userData = jsonDecode(userCheckResponse.body); + + //print('userDetails ->: $userData'); + if (userData['items'].isNotEmpty) { + // Email exists; retrieve user ID + final userId = userData['items'][0]['id']; + + // Proceed with OTP request + final otpResponse = await http.post( + Uri.parse( + 'https://pb.venbait.in/api/collections/otp_requests/records'), + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer $authToken', + }, + body: jsonEncode({ + 'email': email, + }), + ); + + if (otpResponse.statusCode == 200) { + //print('otpResponse- ${otpResponse.body} '); + + final otpData = jsonDecode(otpResponse.body); + 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, + ), + ), + ); + } else { + final error = + jsonDecode(otpResponse.body)['error'] ?? "Unknown error"; + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Error: $error")), + ); + } + } else { + // Email not found + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Please Enter Registered Mail ID")), + ); + } + } else { + // Error in user collection request + final error = + jsonDecode(userCheckResponse.body)['error'] ?? "Unknown error"; + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Error: $error")), + ); + } + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Failed to process request: $e")), + ); + } finally { + setState(() { + _isLoading = false; + }); + } } } @@ -134,7 +232,12 @@ class _ResetPasswordScreenState extends State { width: double.infinity, height: 48, child: ElevatedButton( - onPressed: _isLoading ? null : _handleSubmit, + onPressed: _isLoading + ? null + : () { + final email = _emailController.text.trim(); + sendVerificationCode(email); + }, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF8B7355), foregroundColor: Colors.white, @@ -163,9 +266,14 @@ class _ResetPasswordScreenState extends State { fontWeight: FontWeight.w500, ), ), - SizedBox(width: 2,), - Icon(Icons.arrow_forward_ios,color: Colors.white38,size: 18,), - + SizedBox( + width: 2, + ), + Icon( + Icons.arrow_forward_ios, + color: Colors.white38, + size: 18, + ), ], ], ), @@ -173,19 +281,21 @@ class _ResetPasswordScreenState extends State { ), ], ), - SizedBox(height: screenheight/3,), - + SizedBox( + height: screenheight / 3, + ), Center( child: Container( - height: screenheight/8, - width: screenwidth/2, - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage("assets/splash_screen/logo.png"), // Background image asset - fit: BoxFit.fill, - ), - ), - )) + height: screenheight / 8, + width: screenwidth / 2, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage( + "assets/splash_screen/logo.png"), // Background image asset + fit: BoxFit.fill, + ), + ), + )) ], ), ), @@ -195,8 +305,3 @@ class _ResetPasswordScreenState extends State { ); } } - - - - - diff --git a/lib/presentation/Screens/confirm_password.dart b/lib/presentation/Screens/confirm_password.dart index 0edd45c6..6bb6b26d 100644 --- a/lib/presentation/Screens/confirm_password.dart +++ b/lib/presentation/Screens/confirm_password.dart @@ -1,13 +1,19 @@ import 'package:flutter/material.dart'; +import 'package:pocketbase/pocketbase.dart'; +import 'package:uae_stat/presentation/Screens/profilepage.dart'; class ConfirmPassword extends StatefulWidget { - ConfirmPassword({super.key}); + final String email; + final String userId; + ConfirmPassword({required this.email, required this.userId}); @override State createState() => _ConfirmPasswordState(); } class _ConfirmPasswordState extends State { + final _pb = PocketBase('https://pb.venbait.in'); + // final pb = PocketBase('http://127.0.0.1:8090'); final _formKey = GlobalKey(); bool _obscurePassword = true; bool _obscureConfirmPassword = true; @@ -32,6 +38,68 @@ class _ConfirmPasswordState extends State { return null; } + // Function to check and update password + Future updatePassword(String userId, String newPassword) async { + try { + //print('userRecord- $userId'); + + // Authenticate as admin + + final adminAuth = await _pb.admins + .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com'); + final token = adminAuth.token; + + final headers = { + 'Authorization': 'Bearer $token', + }; + + final userRecord = await _pb.collection('users').getOne(userId); + //print('userRecord- $userRecord'); + final oldPassword = userRecord.data['password']; + print(oldPassword); + + // Check if the new password is the same as the old password + if (oldPassword == newPassword) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + 'New password is similar to the old password. Please try a different one.'), + backgroundColor: Colors.orange, + ), + ); + return; // Exit early + } + + // Update password + await _pb.collection('users').update( + userId, // User ID + body: {'password': newPassword}, // Updated password + ); + + // Show success Snackbar + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Password updated successfully'), + backgroundColor: Colors.blue, + ), + ); + + Navigator.push( + context, + MaterialPageRoute(builder: (context) => ProfileScreen(userId: userId)), + ); + } catch (e) { + print('Error updating password: $e'); + // Show error Snackbar + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Failed to update password'), + backgroundColor: Colors.red, + ), + ); + } + } + @override Widget build(BuildContext context) { double screenheight = MediaQuery.of(context).size.height; @@ -49,7 +117,9 @@ class _ConfirmPasswordState extends State { padding: const EdgeInsets.all(24.0), child: Column( children: [ - SizedBox(height: screenheight/6,), + SizedBox( + height: screenheight / 6, + ), Text( "Create New Password", style: TextStyle( @@ -57,7 +127,9 @@ class _ConfirmPasswordState extends State { fontWeight: FontWeight.bold, ), ), - SizedBox(height: screenheight/35,), + SizedBox( + height: screenheight / 35, + ), Text( "Your new password must de different\nform previously used password", textAlign: TextAlign.center, @@ -66,7 +138,9 @@ class _ConfirmPasswordState extends State { color: Colors.grey[600], ), ), - SizedBox(height: screenheight/35,), + SizedBox( + height: screenheight / 35, + ), TextFormField( obscureText: _obscurePassword, decoration: InputDecoration( @@ -92,7 +166,9 @@ class _ConfirmPasswordState extends State { ), validator: _validatePassword, ), - SizedBox(height: screenheight/35,), + SizedBox( + height: screenheight / 35, + ), TextFormField( obscureText: _obscureConfirmPassword, decoration: InputDecoration( @@ -110,7 +186,8 @@ class _ConfirmPasswordState extends State { ), onPressed: () { setState(() { - _obscureConfirmPassword = !_obscureConfirmPassword; + _obscureConfirmPassword = + !_obscureConfirmPassword; }); }, ), @@ -118,20 +195,28 @@ class _ConfirmPasswordState extends State { ), validator: _validateConfirmPassword, ), - SizedBox(height: screenheight/35,), SizedBox( - width: screenwidth/1.1, + height: screenheight / 35, + ), + SizedBox( + width: screenwidth / 1.1, child: ElevatedButton( - onPressed: () { + onPressed: () async { if ((_formKey.currentState?.validate() ?? false)) { - // Proceed with registration if form is valid and checkbox is checked + //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(horizontal: 80, vertical: 16), + padding: EdgeInsets.symmetric( + horizontal: 80, vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), @@ -141,10 +226,17 @@ class _ConfirmPasswordState extends State { children: [ Text( "Save", - style: TextStyle(fontSize: 18,color: Colors.white), + style: TextStyle( + fontSize: 18, color: Colors.white), + ), + SizedBox( + width: 2, + ), + Icon( + Icons.arrow_forward_ios, + color: Colors.white38, + size: 18, ), - SizedBox(width: 2,), - Icon(Icons.arrow_forward_ios,color: Colors.white38,size: 18,), ], ), ), @@ -152,18 +244,21 @@ class _ConfirmPasswordState extends State { ], ), ), - SizedBox(height: screenheight/15,), + SizedBox( + height: screenheight / 15, + ), Center( child: Container( - height: screenheight/10, - width: screenwidth/2.5, - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage("assets/splash_screen/logo.png"), // Background image asset - fit: BoxFit.fill, - ), - ), - )) + height: screenheight / 10, + width: screenwidth / 2.5, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage( + "assets/splash_screen/logo.png"), // Background image asset + fit: BoxFit.fill, + ), + ), + )) ], ), ), diff --git a/lib/presentation/Screens/otp_verification.dart b/lib/presentation/Screens/otp_verification.dart index e2fddc2b..f5c4c6c8 100644 --- a/lib/presentation/Screens/otp_verification.dart +++ b/lib/presentation/Screens/otp_verification.dart @@ -1,20 +1,35 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:pocketbase/pocketbase.dart'; import 'confirm_password.dart'; class EmailVerificationScreen extends StatefulWidget { + final String email; + final String userId, otp, otpId; + final Function(String) sendVerificationCode; + + EmailVerificationScreen( + {required this.email, + required this.userId, + required this.otp, + required this.otpId, + required this.sendVerificationCode}); + @override _EmailVerificationScreenState createState() => _EmailVerificationScreenState(); } class _EmailVerificationScreenState extends State { + // final pb = PocketBase('http://127.0.0.1:8090'); + final _pb = PocketBase('https://pb.venbait.in'); final List _otpControllers = - List.generate(4, (_) => TextEditingController()); + List.generate(4, (_) => TextEditingController()); int _secondsRemaining = 120; // 2 minutes timer late Timer _timer; + bool _canResend = false; @override void initState() { @@ -23,12 +38,16 @@ class _EmailVerificationScreenState extends State { } void _startTimer() { + _canResend = false; _timer = Timer.periodic(Duration(seconds: 1), (timer) { if (_secondsRemaining > 0) { setState(() { _secondsRemaining--; }); } else { + setState(() { + _canResend = true; // Enable "Resend Code" when the timer ends + }); timer.cancel(); } }); @@ -49,6 +68,42 @@ class _EmailVerificationScreenState extends State { super.dispose(); } + void verify(BuildContext context, String email, String enteredOTP, + String userId) async { + // Validate OTP length + if (enteredOTP.length != 4) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Please enter a valid 4-digit code!")), + ); + return; + } + + try { + if (enteredOTP == widget.otp && email == widget.email) { + // Parse the response + + //print('VERIFIED OTF'); + + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ConfirmPassword( + email: widget.email, // Pass the email + userId: widget.userId)), + ); + } else { + // No matching record found + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Invalid OTP. Please try again.")), + ); + } + } catch (e) { + // Catch network or other unexpected errors + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Something went wrong. Please try again.")), + ); + } + } @override Widget build(BuildContext context) { @@ -63,7 +118,9 @@ class _EmailVerificationScreenState extends State { children: [ Column( children: [ - SizedBox(height: screenheight/6,), + SizedBox( + height: screenheight / 6, + ), Text( "Verify Your Email", style: TextStyle( @@ -90,11 +147,11 @@ class _EmailVerificationScreenState extends State { height: 50, alignment: Alignment.center, decoration: BoxDecoration( - border: Border.all(color: Colors.grey.shade400, width: 1), + border: + Border.all(color: Colors.grey.shade400, width: 1), borderRadius: BorderRadius.circular(8), ), - child: - TextField( + child: TextField( controller: _otpControllers[index], keyboardType: TextInputType.number, textAlign: TextAlign.center, @@ -103,11 +160,13 @@ class _EmailVerificationScreenState extends State { counterText: "", border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: BorderSide(color: Colors.blue, width: 2), + borderSide: + BorderSide(color: Colors.blue, width: 2), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: BorderSide(color: Colors.blue, width: 2), + borderSide: + BorderSide(color: Colors.blue, width: 2), ), ), onChanged: (value) { @@ -127,36 +186,49 @@ class _EmailVerificationScreenState extends State { children: [ TextSpan( text: _formattedTime, - style: TextStyle(fontWeight: FontWeight.w900,color: Colors.black87), + style: TextStyle( + fontWeight: FontWeight.w900, + color: Colors.black87), ), ], ), ), SizedBox(height: 5), TextButton( - onPressed: () { - setState(() { - _secondsRemaining = 120; - _startTimer(); - }); - }, - child: Text("Resend Code",style: TextStyle(color: Colors.brown,fontWeight: FontWeight.bold),), + onPressed: _canResend + ? () { + setState(() { + _secondsRemaining = 120; + _startTimer(); + }); + widget.sendVerificationCode(widget.email); + } + : null, + child: Text( + "Resend Code", + style: TextStyle( + color: Colors.brown, fontWeight: FontWeight.bold), + ), ), SizedBox(height: 5), SizedBox( - width: screenwidth/1.2, + width: screenwidth / 1.2, child: ElevatedButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute(builder: (context) => ConfirmPassword()), - ); + 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 }, style: ElevatedButton.styleFrom( backgroundColor: Colors.brown[300], - padding: EdgeInsets.symmetric(horizontal: 80, vertical: 16), + padding: + EdgeInsets.symmetric(horizontal: 80, vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), @@ -166,32 +238,41 @@ class _EmailVerificationScreenState extends State { children: [ Text( "Verify", - style: TextStyle(fontSize: 18,color: Colors.white), + style: TextStyle(fontSize: 18, color: Colors.white), + ), + SizedBox( + width: 3, + ), + Icon( + Icons.arrow_forward_ios, + color: Colors.white38, + size: 18, ), - SizedBox(width: 3,), - Icon(Icons.arrow_forward_ios,color: Colors.white38,size: 18,), ], ), ), ), ], ), - SizedBox(height: screenheight/6,), + SizedBox( + height: screenheight / 6, + ), Center( child: Container( - height: screenheight/8, - width: screenwidth/2, - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage("assets/splash_screen/logo.png"), // Background image asset - fit: BoxFit.fill, - ), - ), - )) + height: screenheight / 8, + width: screenwidth / 2, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage( + "assets/splash_screen/logo.png"), // Background image asset + fit: BoxFit.fill, + ), + ), + )) ], ), ), ), ); } -} \ No newline at end of file +} diff --git a/lib/presentation/Screens/profilepage.dart b/lib/presentation/Screens/profilepage.dart index 56ec11e5..f0ab349f 100644 --- a/lib/presentation/Screens/profilepage.dart +++ b/lib/presentation/Screens/profilepage.dart @@ -172,24 +172,31 @@ class _ProfileScreenState extends State { DateTime dob = DateFormat('dd/MM/yyyy').parse(dateOfBirth); String formattedDob = DateFormat('yyyy-MM-dd').format(dob); - // Prepare the data to be updated - final Map userData = { - 'full_name': fullName, - 'dob': formattedDob, - 'country_region': countryRegion, - 'is_profile_completed': 'True' - }; + // Create a multipart request + final uri = + Uri.parse('${_pb.baseUrl}/api/collections/users/records/$userID'); + final request = http.MultipartRequest('PATCH', uri); - // If there's a profile image, upload it separately or include in userData if PocketBase supports files in the update + // Add other fields + 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) { - userData['avatar'] = await http.MultipartFile.fromPath( - 'avatar', _profileImage!.path); + request.files.add(await http.MultipartFile.fromPath( + 'avatar', + _profileImage!.path, + )); } - // Update the user profile in PocketBase - final updatedUser = - await _pb.collection('users').update(userID, body: userData); - print(updatedUser); + // Add headers (if required, e.g., authorization) + request.headers['Authorization'] = 'Bearer ${_pb.authStore.token}'; + + // Send the request + final response = await request.send(); + print(response); _resetFormFields(); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Profile updated successfully!"))); diff --git a/lib/presentation/Screens/registration.dart b/lib/presentation/Screens/registration.dart index ccf11118..d87b0562 100644 --- a/lib/presentation/Screens/registration.dart +++ b/lib/presentation/Screens/registration.dart @@ -24,7 +24,9 @@ class _RegisterScreenState extends State { String? _password; bool registrationSuccess = false; bool registrationFailed = false; + dynamic userID; final pb = PocketBase('https://pb.venbait.in'); + // final pb = PocketBase('http://127.0.0.1:8090'); @override void initState() { @@ -80,6 +82,9 @@ class _RegisterScreenState extends State { if (_formKey.currentState?.validate() ?? false) { if (isChecked) { try { + final adminAuth = await pb.admins + .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com'); + final adminToken = adminAuth.token; // Create user in PocketBase final response = await pb.collection('users').create(body: { 'username': _usernameController.text, @@ -87,12 +92,16 @@ class _RegisterScreenState extends State { 'password': _passwordController.text, 'passwordConfirm': _passwordController .text, // PocketBase requires password confirmation + }, headers: { + 'Authorization': adminToken }); if (response.id != null) { + userID = response.id; // Request email verification - PocketBaseService.users.requestVerification(_emailController.text); + // PocketBaseService.users.requestVerification(_emailController.text); // Show success message instead of navigating away setState(() { + userID = response.id; registrationSuccess = true; registrationFailed = false; // Show success message on success }); @@ -182,9 +191,14 @@ class _RegisterScreenState extends State { ), SizedBox(height: 10), ElevatedButton( - onPressed: () => context.go( - '/${context.language}/login', - ), + onPressed: () => { + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => + ProfileScreen(userId: userID)), + ), + }, child: Text( 'Go to Login', ), diff --git a/lib/presentation/routes/drawer_routes/feedback_route.dart b/lib/presentation/routes/drawer_routes/feedback_route.dart index ec860155..d23f43fa 100644 --- a/lib/presentation/routes/drawer_routes/feedback_route.dart +++ b/lib/presentation/routes/drawer_routes/feedback_route.dart @@ -29,7 +29,8 @@ class _FeedbackFormState extends State with WidgetsBindingObserver { final _pb = PocketBase('https://pb.venbait.in'); // Initialize PocketBase client - // final _pb = PocketBase('http://127.0.0.1:8090'); // Initialize PocketBase client + // final _pb = + // PocketBase('http://127.0.0.1:8090'); // Initialize PocketBase client final TextEditingController _feedbackController = TextEditingController();