feedback bug fix

This commit is contained in:
venbaittech 2024-11-15 14:32:20 +05:30
parent acaa9f046c
commit e54998ac5f
4 changed files with 426 additions and 284 deletions

View File

@ -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});
//

View File

@ -172,50 +172,27 @@ class _ProfileScreenState extends State<ProfileScreen> {
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<String, dynamic> 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<ProfileScreen> {
SizedBox(height: 20),
Center(
child: GestureDetector(
onTap: (){
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Changepassword()),
MaterialPageRoute(
builder: (context) => Changepassword()),
);
},
child: Text(
"Change Password",

View File

@ -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<RegisterScreen> {
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<RegisterScreen> {
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<RegisterScreen> {
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,
),
),
))
],
),
),
),
),
);
}
}

View File

@ -25,7 +25,8 @@ class FeedbackForm extends StatefulWidget {
_FeedbackFormState createState() => _FeedbackFormState();
}
class _FeedbackFormState extends State<FeedbackForm> {
class _FeedbackFormState extends State<FeedbackForm>
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<FeedbackForm> {
@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<void> _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<void> _saveAllRatings() async {
@ -101,24 +119,36 @@ class _FeedbackFormState extends State<FeedbackForm> {
Future<void> _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<FeedbackForm> {
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 <Server_Timezone>";
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<FeedbackForm> {
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<FeedbackForm> {
@override
void dispose() {
_feedbackController.dispose();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
}