From e54998ac5fa3794605c33981889df20ab8768f2a Mon Sep 17 00:00:00 2001 From: venbaittech Date: Fri, 15 Nov 2024 14:32:20 +0530 Subject: [PATCH] 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(); } }