all functonality added in new router flow
This commit is contained in:
parent
617379deef
commit
c75ae680b0
6
.run/main.dart.run.xml
Normal file
6
.run/main.dart.run.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="main.dart" type="FlutterRunConfigurationType" factoryName="Flutter">
|
||||||
|
<option name="filePath" value="$PROJECT_DIR$/lib/main.dart" />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
@ -303,6 +303,7 @@ import '../presentation/Screens/auth_verification/otp_verification.dart';
|
|||||||
import '../presentation/Screens/profilepage.dart';
|
import '../presentation/Screens/profilepage.dart';
|
||||||
import '../presentation/routes/auth_routes/login_route.dart';
|
import '../presentation/routes/auth_routes/login_route.dart';
|
||||||
import '../presentation/routes/bottom_bar_routes/tab_routes/home_route.dart';
|
import '../presentation/routes/bottom_bar_routes/tab_routes/home_route.dart';
|
||||||
|
import '../presentation/routes/drawer_routes/Drawer Items/edit_profile.dart';
|
||||||
import '../presentation/routes/drawer_routes/Drawer Items/feedback.dart';
|
import '../presentation/routes/drawer_routes/Drawer Items/feedback.dart';
|
||||||
import '../presentation/routes/drawer_routes/Drawer Items/manage_users.dart';
|
import '../presentation/routes/drawer_routes/Drawer Items/manage_users.dart';
|
||||||
|
|
||||||
@ -311,7 +312,7 @@ final GoRouter router = GoRouter(
|
|||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/',
|
path: '/',
|
||||||
//builder: (context, state) => LoginRoute(),
|
//builder: (context, state) => LoginRoute(),
|
||||||
builder: (context, state) => MyHomePage(),
|
builder: (context, state) => LoginRoute(),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/myhomepage',
|
path: '/myhomepage',
|
||||||
@ -328,10 +329,11 @@ final GoRouter router = GoRouter(
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/changepassword',
|
path: '/changepassword/:userId',
|
||||||
builder: (context, state) => Changepassword(
|
builder: (context, state) {
|
||||||
userId: '',
|
final userId = state.pathParameters['userId']!;
|
||||||
),
|
return Changepassword(userId: userId);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/confirmpasswd',
|
path: '/confirmpasswd',
|
||||||
@ -347,10 +349,11 @@ final GoRouter router = GoRouter(
|
|||||||
builder: (context, state) => FeedbackForm(),
|
builder: (context, state) => FeedbackForm(),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/profile',
|
path: '/profile/:userId',
|
||||||
builder: (context, state) => ProfileScreen(
|
builder: (context, state) {
|
||||||
userId: '',
|
final userId = state.pathParameters['userId']!;
|
||||||
),
|
return ProfileScreen(userId: userId);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
// GoRoute(
|
// GoRoute(
|
||||||
// path: '/manageuser',
|
// path: '/manageuser',
|
||||||
@ -360,10 +363,13 @@ final GoRouter router = GoRouter(
|
|||||||
// return ManageUserRouter(title: title);
|
// return ManageUserRouter(title: title);
|
||||||
// },
|
// },
|
||||||
// ),
|
// ),
|
||||||
|
GoRoute(
|
||||||
|
path: '/editProfile',
|
||||||
|
builder: (context, state) => EditProfile(),
|
||||||
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/manageuser',
|
path: '/manageuser',
|
||||||
builder: (context, state) => ManageUserRouter(),
|
builder: (context, state) => ManageUserRouter(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -58,7 +58,7 @@ class _ResetPasswordScreenState extends State<Changepassword> {
|
|||||||
//print('userDetails ->: $userData');
|
//print('userDetails ->: $userData');
|
||||||
if (userData['items'].isNotEmpty) {
|
if (userData['items'].isNotEmpty) {
|
||||||
// Email exists; retrieve user ID
|
// Email exists; retrieve user ID
|
||||||
final userId = userData['items'][0]['id'];
|
// final userId = userData['items'][0]['id'];
|
||||||
|
|
||||||
// Proceed with OTP request
|
// Proceed with OTP request
|
||||||
final otpResponse = await http.post(
|
final otpResponse = await http.post(
|
||||||
@ -87,17 +87,15 @@ class _ResetPasswordScreenState extends State<Changepassword> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Navigate to the Email Verification screen
|
// Navigate to the Email Verification screen
|
||||||
Navigator.push(
|
context.go(
|
||||||
context,
|
'/mailverification',
|
||||||
MaterialPageRoute(
|
extra: {
|
||||||
builder: (context) => EmailVerificationScreen(
|
'email': email,
|
||||||
email: email,
|
'userId': widget.userId, // Pass user ID to the next screen
|
||||||
userId: widget.userId, // Pass user ID to the next screen
|
'otp': otp,
|
||||||
otp: otp,
|
'otpId': otpId,
|
||||||
otpId: otpId,
|
'sendVerificationCode': sendVerificationCode,
|
||||||
sendVerificationCode: sendVerificationCode,
|
},
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final error =
|
final error =
|
||||||
@ -132,6 +130,12 @@ class _ResetPasswordScreenState extends State<Changepassword> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
print(widget.userId);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_emailController.dispose();
|
_emailController.dispose();
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import 'package:pocketbase/pocketbase.dart';
|
|||||||
import 'package:uae_stat/domain/use_cases/language.dart';
|
import 'package:uae_stat/domain/use_cases/language.dart';
|
||||||
import 'package:uae_stat/presentation/Screens/profilepage.dart';
|
import 'package:uae_stat/presentation/Screens/profilepage.dart';
|
||||||
|
|
||||||
import '../../../infrastructure/services/pocketbase_service.dart';
|
|
||||||
import '../../routes/auth_routes/login_route.dart';
|
import '../../routes/auth_routes/login_route.dart';
|
||||||
|
|
||||||
class RegisterScreen extends StatefulWidget {
|
class RegisterScreen extends StatefulWidget {
|
||||||
@ -194,7 +193,7 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
|||||||
'email': _emailController.text,
|
'email': _emailController.text,
|
||||||
'password': _passwordController.text,
|
'password': _passwordController.text,
|
||||||
'passwordConfirm': _passwordController.text,
|
'passwordConfirm': _passwordController.text,
|
||||||
// 'verified': true,
|
'status': 'Pending',
|
||||||
}, headers: {
|
}, headers: {
|
||||||
'Authorization': adminToken
|
'Authorization': adminToken
|
||||||
});
|
});
|
||||||
|
|||||||
@ -9,8 +9,12 @@ import 'package:http/http.dart' as http;
|
|||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:pocketbase/pocketbase.dart';
|
import 'package:pocketbase/pocketbase.dart';
|
||||||
|
import 'package:uae_stat/domain/use_cases/language.dart';
|
||||||
|
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart';
|
||||||
|
|
||||||
|
import '../components/my_bottom_nav_bar.dart';
|
||||||
import 'auth_verification/changepassword.dart';
|
import 'auth_verification/changepassword.dart';
|
||||||
|
import 'demo_home.dart';
|
||||||
|
|
||||||
class ProfileScreen extends StatefulWidget {
|
class ProfileScreen extends StatefulWidget {
|
||||||
final String userId; // Add this field to hold the user ID
|
final String userId; // Add this field to hold the user ID
|
||||||
@ -23,6 +27,25 @@ class ProfileScreen extends StatefulWidget {
|
|||||||
class _ProfileScreenState extends State<ProfileScreen> {
|
class _ProfileScreenState extends State<ProfileScreen> {
|
||||||
final _pb = PocketBase('https://pb.venbait.in');
|
final _pb = PocketBase('https://pb.venbait.in');
|
||||||
// final _pb = PocketBase('http://127.0.0.1:8090');
|
// final _pb = PocketBase('http://127.0.0.1:8090');
|
||||||
|
|
||||||
|
// Add focus nodes and hint states
|
||||||
|
final List<FocusNode> _focusNodes = List.generate(4, (_) => FocusNode());
|
||||||
|
final List<bool> _showHints = [
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
]; // One for each TextField
|
||||||
|
|
||||||
|
String _getControllerText(int index) {
|
||||||
|
if (index == 0) return _usernameController.text;
|
||||||
|
if (index == 1) return _emailController.text;
|
||||||
|
if (index == 2) return _fullNameController.text;
|
||||||
|
if (index == 3) return _dateController.text;
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
final TextEditingController _fullNameController = TextEditingController();
|
final TextEditingController _fullNameController = TextEditingController();
|
||||||
final TextEditingController _dateController = TextEditingController();
|
final TextEditingController _dateController = TextEditingController();
|
||||||
@ -52,12 +75,58 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
print(widget.userId);
|
// print(widget.userId);
|
||||||
|
|
||||||
|
// Add listeners for focus nodes
|
||||||
|
for (int i = 0; i < _focusNodes.length; i++) {
|
||||||
|
_focusNodes[i].addListener(() {
|
||||||
|
setState(() {
|
||||||
|
// Hide hint when focused and text is not empty
|
||||||
|
_showHints[i] =
|
||||||
|
!_focusNodes[i].hasFocus && (_getControllerText(i).isEmpty);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen to text changes
|
||||||
|
if (i == 0) {
|
||||||
|
_usernameController.addListener(() {
|
||||||
|
setState(() {
|
||||||
|
_showHints[i] =
|
||||||
|
!_focusNodes[i].hasFocus && (_getControllerText(i).isEmpty);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else if (i == 1) {
|
||||||
|
_emailController.addListener(() {
|
||||||
|
setState(() {
|
||||||
|
_showHints[i] =
|
||||||
|
!_focusNodes[i].hasFocus && (_getControllerText(i).isEmpty);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else if (i == 1) {
|
||||||
|
_fullNameController.addListener(() {
|
||||||
|
setState(() {
|
||||||
|
_showHints[i] =
|
||||||
|
!_focusNodes[i].hasFocus && (_getControllerText(i).isEmpty);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else if (i == 2) {
|
||||||
|
_dateController.addListener(() {
|
||||||
|
setState(() {
|
||||||
|
_showHints[i] =
|
||||||
|
!_focusNodes[i].hasFocus && (_getControllerText(i).isEmpty);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_fetchUserData(); // Call the function to fetch user data
|
_fetchUserData(); // Call the function to fetch user data
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
for (var focusNode in _focusNodes) {
|
||||||
|
focusNode.dispose();
|
||||||
|
}
|
||||||
_fullNameController.dispose();
|
_fullNameController.dispose();
|
||||||
_dateController.dispose();
|
_dateController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
@ -68,6 +137,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
|||||||
final adminAuth = await _pb.admins
|
final adminAuth = await _pb.admins
|
||||||
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
|
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
|
||||||
final adminToken = adminAuth.token;
|
final adminToken = adminAuth.token;
|
||||||
|
print('adminToken- ${adminToken}');
|
||||||
final userDetailsResponse = await _pb.collection('users').getOne(
|
final userDetailsResponse = await _pb.collection('users').getOne(
|
||||||
widget.userId,
|
widget.userId,
|
||||||
headers: {
|
headers: {
|
||||||
@ -125,7 +195,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
|||||||
// Function to validate the DOB field
|
// Function to validate the DOB field
|
||||||
String? _validateDob(String? value) {
|
String? _validateDob(String? value) {
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
return 'Please select your date of birth';
|
return 'Required';
|
||||||
}
|
}
|
||||||
|
|
||||||
final DateTime selectedDate = _selectedDate!;
|
final DateTime selectedDate = _selectedDate!;
|
||||||
@ -140,7 +210,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
|||||||
|
|
||||||
String? _validateDropdown(String? value) {
|
String? _validateDropdown(String? value) {
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
return 'Please select an option';
|
return 'Required';
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -201,6 +271,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
|||||||
_resetFormFields();
|
_resetFormFields();
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text("Profile updated successfully!")));
|
SnackBar(content: Text("Profile updated successfully!")));
|
||||||
|
context.go('/myhomepage');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text("Failed to update profile: $error")));
|
SnackBar(content: Text("Failed to update profile: $error")));
|
||||||
@ -299,8 +370,9 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
|||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: _usernameController,
|
controller: _usernameController,
|
||||||
|
focusNode: _focusNodes[0],
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'Mohammad Hassan',
|
hintText: _showHints[0] ? 'Mohammad Hassan' : null,
|
||||||
hintStyle: TextStyle(color: Colors.grey),
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
@ -322,8 +394,10 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
|||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: _emailController,
|
controller: _emailController,
|
||||||
|
focusNode: _focusNodes[1],
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'mohammad.hassan@fcsc.gov.ae',
|
hintText:
|
||||||
|
_showHints[1] ? 'mohammad.hassan@fcsc.gov.ae' : null,
|
||||||
hintStyle: TextStyle(color: Colors.grey),
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
@ -349,15 +423,19 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
|||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
return 'Required';
|
return 'Required';
|
||||||
}
|
}
|
||||||
final nameRegex = RegExp(r"^[a-zA-Z\s]+$");
|
|
||||||
|
//RegExp(r"^[a-zA-Z\s]+$");
|
||||||
|
final nameRegex =
|
||||||
|
RegExp(r"^[a-zA-Z0-9'\-\u0627\u0639~\u00C0-\u00FF]+$");
|
||||||
if (!nameRegex.hasMatch(value)) {
|
if (!nameRegex.hasMatch(value)) {
|
||||||
return 'Invalid Characters';
|
return 'Invalid Characters';
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
controller: _fullNameController,
|
controller: _fullNameController,
|
||||||
|
focusNode: _focusNodes[2],
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: "Mohammad",
|
hintText: _showHints[2] ? 'Enter the Full Name' : null,
|
||||||
hintStyle: TextStyle(color: Colors.grey),
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
@ -381,11 +459,14 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
|||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: _dateController,
|
controller: _dateController,
|
||||||
|
focusNode: _focusNodes[3],
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
hintText: 'Select your Date of Birth',
|
hintText:
|
||||||
|
_showHints[3] ? 'Select your Date of Birth' : null,
|
||||||
|
//hintText: 'Select your Date of Birth',
|
||||||
suffixIcon: const Icon(Icons.arrow_drop_down_sharp),
|
suffixIcon: const Icon(Icons.arrow_drop_down_sharp),
|
||||||
),
|
),
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
@ -503,7 +584,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 10.0),
|
padding: const EdgeInsets.only(left: 10.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Required',
|
'Please agree to terms and conditions',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.red[700],
|
color: Colors.red[700],
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
@ -516,15 +597,13 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
|||||||
Center(
|
Center(
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.go('/confirmpasswd');
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) =>
|
||||||
|
Changepassword(userId: widget.userId)),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// Navigator.push(
|
|
||||||
// context,
|
|
||||||
// MaterialPageRoute(
|
|
||||||
// builder: (context) => Changepassword(userId: '',)),
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
child: Text(
|
child: Text(
|
||||||
"Change Password",
|
"Change Password",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@ -536,38 +615,35 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
|||||||
SizedBox(height: 20),
|
SizedBox(height: 20),
|
||||||
ElevatedButton.icon(
|
ElevatedButton.icon(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.go('/myhomepage');
|
if ((_formKey.currentState?.validate() ?? false) &&
|
||||||
|
(isChecked)) {
|
||||||
|
_formKey.currentState?.save();
|
||||||
|
showConfirmationDialog(context);
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
showError =
|
||||||
|
!isChecked; // Show error if the checkbox is not checked
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// if ((_formKey.currentState?.validate() ?? false) && (isChecked)) {
|
||||||
|
// _formKey.currentState?.save();
|
||||||
|
// showConfirmationDialog(context);
|
||||||
|
// // if (isChecked) {
|
||||||
|
// // _formKey.currentState?.save();
|
||||||
|
// // // _confirmSaveProfile();
|
||||||
|
// // showConfirmationDialog(context);
|
||||||
|
// // }
|
||||||
|
// else {
|
||||||
|
// setState(() {
|
||||||
|
// 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),)),
|
||||||
|
// );
|
||||||
|
//}
|
||||||
|
//}
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// if ((_formKey.currentState?.validate() ?? false) &&
|
|
||||||
// (isChecked)) {
|
|
||||||
// _formKey.currentState?.save();
|
|
||||||
// showConfirmationDialog(context);
|
|
||||||
// } else {
|
|
||||||
// setState(() {
|
|
||||||
// showError =
|
|
||||||
// !isChecked; // Show error if the checkbox is not checked
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// // if ((_formKey.currentState?.validate() ?? false) && (isChecked)) {
|
|
||||||
// // _formKey.currentState?.save();
|
|
||||||
// // showConfirmationDialog(context);
|
|
||||||
// // // if (isChecked) {
|
|
||||||
// // // _formKey.currentState?.save();
|
|
||||||
// // // // _confirmSaveProfile();
|
|
||||||
// // // showConfirmationDialog(context);
|
|
||||||
// // // }
|
|
||||||
// // else {
|
|
||||||
// // setState(() {
|
|
||||||
// // 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),)),
|
|
||||||
// // );
|
|
||||||
// //}
|
|
||||||
// //}
|
|
||||||
// },
|
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.save,
|
Icons.save,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:the_validator/the_validator.dart';
|
import 'package:the_validator/the_validator.dart';
|
||||||
import 'package:uae_stat/config/my_theme.dart';
|
import 'package:uae_stat/config/my_theme.dart';
|
||||||
import 'package:uae_stat/domain/use_cases/auth_use_case.dart';
|
import 'package:uae_stat/domain/use_cases/auth_use_case.dart';
|
||||||
@ -17,7 +18,6 @@ import 'package:uae_stat/presentation/components/space.dart';
|
|||||||
import 'package:uae_stat/presentation/components/themed_text_field.dart';
|
import 'package:uae_stat/presentation/components/themed_text_field.dart';
|
||||||
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart';
|
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart';
|
||||||
|
|
||||||
import '../../Screens/profilepage.dart';
|
|
||||||
import '../../Screens/auth_verification/registration.dart';
|
import '../../Screens/auth_verification/registration.dart';
|
||||||
import 'package:pocketbase/pocketbase.dart';
|
import 'package:pocketbase/pocketbase.dart';
|
||||||
|
|
||||||
@ -25,6 +25,7 @@ class LoginRoute extends HookConsumerWidget {
|
|||||||
final pb = PocketBase('https://pb.venbait.in');
|
final pb = PocketBase('https://pb.venbait.in');
|
||||||
// final _pb = PocketBase('http://127.0.0.1:8090');
|
// final _pb = PocketBase('http://127.0.0.1:8090');
|
||||||
LoginRoute({super.key});
|
LoginRoute({super.key});
|
||||||
|
dynamic userData;
|
||||||
|
|
||||||
static final formKey = GlobalKey<FormState>();
|
static final formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
@ -45,21 +46,38 @@ class LoginRoute extends HookConsumerWidget {
|
|||||||
'Authorization': 'Bearer $adminToken',
|
'Authorization': 'Bearer $adminToken',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
print('userDetailsLogin: $userDetailsResponse');
|
print('userDetailsLogin: $userDetailsResponse');
|
||||||
|
|
||||||
// Check if 'is_profile_completed' is true or false
|
// Check if user is verified
|
||||||
if (userDetailsResponse != null &&
|
final bool? isVerified = userDetailsResponse.data['verified'];
|
||||||
userDetailsResponse.data['is_profile_completed'] != null) {
|
final bool? isUserMailVerified =
|
||||||
return userDetailsResponse.data['is_profile_completed'];
|
userDetailsResponse.data['user_mail_verify'];
|
||||||
} else {
|
|
||||||
return false; // Default to false if the field is missing or response is null
|
// Check admin and email verification statuses
|
||||||
|
if (isVerified == false) {
|
||||||
|
throw Exception('Admin not approved');
|
||||||
}
|
}
|
||||||
|
if (isUserMailVerified == false) {
|
||||||
|
throw Exception('Email not verified');
|
||||||
|
}
|
||||||
|
userData = userDetailsResponse;
|
||||||
|
// Check if 'is_profile_completed' is true
|
||||||
|
final bool isProfileCompleted =
|
||||||
|
userDetailsResponse.data['is_profile_completed'] ?? false;
|
||||||
|
|
||||||
|
return isProfileCompleted;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error fetching user details: $e');
|
print('Error fetching user details: $e');
|
||||||
return false; // Return false on error
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> saveUserId(String userId) async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
await prefs.setString('userId', userId); // Save userId locally
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final emailCtl = useTextEditingController();
|
final emailCtl = useTextEditingController();
|
||||||
@ -157,105 +175,121 @@ class LoginRoute extends HookConsumerWidget {
|
|||||||
final loginBtn = SizedBox(
|
final loginBtn = SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
context.go('/profile');
|
final isValid = formKey.currentState!.validate();
|
||||||
|
if (!isValid) return;
|
||||||
|
final session = await context.loaderWithErrorDialog(
|
||||||
|
() => ref
|
||||||
|
.read(
|
||||||
|
authUseCaseProvider.notifier,
|
||||||
|
)
|
||||||
|
.login(
|
||||||
|
emailCtl.text,
|
||||||
|
pwCtl.text,
|
||||||
|
),
|
||||||
|
errorDialogBuilder: (
|
||||||
|
error, [
|
||||||
|
StackTrace? stackTrace,
|
||||||
|
]) {
|
||||||
|
if (error == LoginError.invalidEmailPw) {
|
||||||
|
return context.simpleDialog(
|
||||||
|
title: context.translate(
|
||||||
|
'Incorrect credentials',
|
||||||
|
'أوراق غير صحيحة',
|
||||||
|
),
|
||||||
|
content: context.translate(
|
||||||
|
'Your email or password is invalid. Please try again.',
|
||||||
|
'بريدك الإلكتروني أو كلمة المرور غير صالحة. حاول مرة اخرى.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// if (error == LoginError.emailAddressNotVerified) {
|
||||||
|
// return context.simpleDialog(
|
||||||
|
// title: context.translate(
|
||||||
|
// 'Verification Error',
|
||||||
|
// 'خطأ التحقق',
|
||||||
|
// ),
|
||||||
|
// content: context.translate(
|
||||||
|
// '${emailCtl.text} is not a verified email address. Please check your email for a verification link.',
|
||||||
|
// '${emailCtl.text} ليس عنوان بريد إلكتروني تم التحقق منه. يرجى التحقق من بريدك الإلكتروني للحصول على رابط التحقق.',
|
||||||
|
// ),
|
||||||
|
// extraAction: ElevatedButton(
|
||||||
|
// onPressed: () async {
|
||||||
|
// Navigator.of(
|
||||||
|
// context,
|
||||||
|
// rootNavigator: true,
|
||||||
|
// ).pop();
|
||||||
|
// await context.loaderWithErrorDialog(
|
||||||
|
// () => ref
|
||||||
|
// .read(authUseCaseProvider.notifier)
|
||||||
|
// .requestVerificationEmail(emailCtl.text),
|
||||||
|
// );
|
||||||
|
// if (!context.mounted) return;
|
||||||
|
// context.simpleDialog(
|
||||||
|
// title: 'Email Re-sent',
|
||||||
|
// content:
|
||||||
|
// 'We\'ve sent you the verification email at ${emailCtl.text} again.',
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// child: Text(
|
||||||
|
// context.translate(
|
||||||
|
// 'I did not receive an email',
|
||||||
|
// 'لم أتلق بريدًا إلكترونيًا',
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
return context.simpleDialog();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!context.mounted || session == null) return;
|
||||||
|
final userId = session.id;
|
||||||
|
if (userId.isNotEmpty) {
|
||||||
|
await saveUserId(userId);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final bool isProfileComplete = await profileStatus(userId);
|
||||||
|
print(isProfileComplete);
|
||||||
|
if (isProfileComplete) {
|
||||||
|
print('home');
|
||||||
|
context.go('/myhomepage');
|
||||||
|
} else {
|
||||||
|
print('profile');
|
||||||
|
if (userId != null && userId.isNotEmpty) {
|
||||||
|
context.go('/profile/$userId');
|
||||||
|
} else {
|
||||||
|
print('Error: userId is null or empty.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Handle specific errors based on their message
|
||||||
|
if (e.toString().contains('Admin not approved')) {
|
||||||
|
context.simpleDialog(
|
||||||
|
title: context.translate(
|
||||||
|
'Admin Approval Required', 'موافقة المسؤول مطلوبة'),
|
||||||
|
content: context.translate(
|
||||||
|
'Your account has not been approved by the admin.',
|
||||||
|
'لم تتم الموافقة على حسابك من قبل المسؤول.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (e.toString().contains('Email not verified')) {
|
||||||
|
context.simpleDialog(
|
||||||
|
title: context.translate(
|
||||||
|
'Email Not Verified', 'البريد الإلكتروني غير مُحقق'),
|
||||||
|
content: context.translate(
|
||||||
|
'Your email address is not verified. Please check your email.',
|
||||||
|
'عنوان بريدك الإلكتروني غير مُحقق. يرجى التحقق من بريدك الإلكتروني.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
print('Unexpected error: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// context.go('/${context.language}/${BottomNavBarItem.home.routePath}');
|
||||||
},
|
},
|
||||||
// async {
|
|
||||||
// final isValid = formKey.currentState!.validate();
|
|
||||||
// if (!isValid) return;
|
|
||||||
// final session = await context.loaderWithErrorDialog(
|
|
||||||
// () => ref
|
|
||||||
// .read(
|
|
||||||
// authUseCaseProvider.notifier,
|
|
||||||
// )
|
|
||||||
// .login(
|
|
||||||
// emailCtl.text,
|
|
||||||
// pwCtl.text,
|
|
||||||
// ),
|
|
||||||
// errorDialogBuilder: (
|
|
||||||
// error, [
|
|
||||||
// StackTrace? stackTrace,
|
|
||||||
// ]) {
|
|
||||||
// if (error == LoginError.invalidEmailPw) {
|
|
||||||
// return context.simpleDialog(
|
|
||||||
// title: context.translate(
|
|
||||||
// 'Incorrect credentials',
|
|
||||||
// 'أوراق غير صحيحة',
|
|
||||||
// ),
|
|
||||||
// content: context.translate(
|
|
||||||
// 'Your email or password is invalid. Please try again.',
|
|
||||||
// 'بريدك الإلكتروني أو كلمة المرور غير صالحة. حاول مرة اخرى.',
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// if (error == LoginError.emailAddressNotVerified) {
|
|
||||||
// return context.simpleDialog(
|
|
||||||
// title: context.translate(
|
|
||||||
// 'Verification Error',
|
|
||||||
// 'خطأ التحقق',
|
|
||||||
// ),
|
|
||||||
// content: context.translate(
|
|
||||||
// '${emailCtl.text} is not a verified email address. Please check your email for a verification link.',
|
|
||||||
// '${emailCtl.text} ليس عنوان بريد إلكتروني تم التحقق منه. يرجى التحقق من بريدك الإلكتروني للحصول على رابط التحقق.',
|
|
||||||
// ),
|
|
||||||
// extraAction: ElevatedButton(
|
|
||||||
// onPressed: () {
|
|
||||||
// context.go('/profile');
|
|
||||||
// },
|
|
||||||
// // async {
|
|
||||||
// // Navigator.of(
|
|
||||||
// // context,
|
|
||||||
// // rootNavigator: true,
|
|
||||||
// // ).pop();
|
|
||||||
// // await context.loaderWithErrorDialog(
|
|
||||||
// // () => ref
|
|
||||||
// // .read(authUseCaseProvider.notifier)
|
|
||||||
// // .requestVerificationEmail(emailCtl.text),
|
|
||||||
// // );
|
|
||||||
// // if (!context.mounted) return;
|
|
||||||
// // context.simpleDialog(
|
|
||||||
// // title: 'Email Re-sent',
|
|
||||||
// // content:
|
|
||||||
// // 'We\'ve sent you the verification email at ${emailCtl.text} again.',
|
|
||||||
// // );
|
|
||||||
// // },
|
|
||||||
// child: Text(
|
|
||||||
// context.translate(
|
|
||||||
// 'I did not receive an email',
|
|
||||||
// 'لم أتلق بريدًا إلكترونيًا',
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// return context.simpleDialog();
|
|
||||||
// },
|
|
||||||
// );
|
|
||||||
// // if (!context.mounted || session == null) return;
|
|
||||||
// // Extract userId from the session
|
|
||||||
// // final userId = session.id;
|
|
||||||
// //
|
|
||||||
// // final bool isUpdate = await profileStatus(userId);
|
|
||||||
// //
|
|
||||||
// // print('Is profile completed: $isUpdate');
|
|
||||||
//
|
|
||||||
// // if (isUpdate) {
|
|
||||||
// // Navigator.pushReplacement(
|
|
||||||
// // context,
|
|
||||||
// // MaterialPageRoute(builder: (context) => DemoHome()),
|
|
||||||
// // );
|
|
||||||
// // }
|
|
||||||
// // else {
|
|
||||||
//
|
|
||||||
// // Navigator.pushReplacement(
|
|
||||||
// // context,
|
|
||||||
// // MaterialPageRoute(
|
|
||||||
// // builder: (context) => ProfileScreen(userId: userId)),
|
|
||||||
// // );
|
|
||||||
// //}
|
|
||||||
//
|
|
||||||
// // context.go('/${context.language}/${BottomNavBarItem.home.routePath}');
|
|
||||||
// },
|
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
shape: WidgetStatePropertyAll(
|
shape: WidgetStatePropertyAll(
|
||||||
RoundedRectangleBorder(
|
RoundedRectangleBorder(
|
||||||
|
|||||||
@ -0,0 +1,837 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:image_picker/image_picker.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:pocketbase/pocketbase.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
import '../../../Screens/auth_verification/changepassword.dart';
|
||||||
|
import '../custom_drawer_routes.dart';
|
||||||
|
|
||||||
|
class EditProfile extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
State<EditProfile> createState() => _EditProfileState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _EditProfileState extends State<EditProfile> {
|
||||||
|
final _pb = PocketBase('https://pb.venbait.in');
|
||||||
|
// final _pb = PocketBase('http://127.0.0.1:8090');
|
||||||
|
bool _isProfileCompleted = false;
|
||||||
|
|
||||||
|
// Add focus nodes and hint states
|
||||||
|
final List<FocusNode> _focusNodes = List.generate(4, (_) => FocusNode());
|
||||||
|
final List<bool> _showHints = [
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
]; // One for each TextField
|
||||||
|
|
||||||
|
String _getControllerText(int index) {
|
||||||
|
if (index == 0) return _usernameController.text;
|
||||||
|
if (index == 1) return _emailController.text;
|
||||||
|
if (index == 2) return _fullNameController.text;
|
||||||
|
if (index == 3) return _dateController.text;
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
final TextEditingController _fullNameController = TextEditingController();
|
||||||
|
final TextEditingController _dateController = TextEditingController();
|
||||||
|
final TextEditingController _usernameController = TextEditingController();
|
||||||
|
final TextEditingController _emailController = TextEditingController();
|
||||||
|
// To keep track of the selected date
|
||||||
|
DateTime? _selectedDate;
|
||||||
|
|
||||||
|
// Date format for the display
|
||||||
|
final DateFormat _dateFormat = DateFormat('yyyy-MM-dd');
|
||||||
|
final List<String> _countries = [
|
||||||
|
'United Arab Emirates',
|
||||||
|
'United States',
|
||||||
|
'India',
|
||||||
|
'Canada'
|
||||||
|
];
|
||||||
|
String? _selectedCountry;
|
||||||
|
bool isChecked = false;
|
||||||
|
bool showError = false;
|
||||||
|
final _picker = ImagePicker();
|
||||||
|
File? _profileImage;
|
||||||
|
String _avatarUrl = '';
|
||||||
|
|
||||||
|
// Regular expression to validate Full Name (no special characters)
|
||||||
|
final RegExp _nameRegExp = RegExp(r"^[a-zA-Z\s]+$");
|
||||||
|
late Future<RecordModel> userDetails;
|
||||||
|
dynamic userId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
// Add listeners for focus nodes
|
||||||
|
for (int i = 0; i < _focusNodes.length; i++) {
|
||||||
|
_focusNodes[i].addListener(() {
|
||||||
|
setState(() {
|
||||||
|
// Hide hint when focused and text is not empty
|
||||||
|
_showHints[i] =
|
||||||
|
!_focusNodes[i].hasFocus && (_getControllerText(i).isEmpty);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen to text changes
|
||||||
|
if (i == 0) {
|
||||||
|
_usernameController.addListener(() {
|
||||||
|
setState(() {
|
||||||
|
_showHints[i] =
|
||||||
|
!_focusNodes[i].hasFocus && (_getControllerText(i).isEmpty);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else if (i == 1) {
|
||||||
|
_emailController.addListener(() {
|
||||||
|
setState(() {
|
||||||
|
_showHints[i] =
|
||||||
|
!_focusNodes[i].hasFocus && (_getControllerText(i).isEmpty);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else if (i == 1) {
|
||||||
|
_fullNameController.addListener(() {
|
||||||
|
setState(() {
|
||||||
|
_showHints[i] =
|
||||||
|
!_focusNodes[i].hasFocus && (_getControllerText(i).isEmpty);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else if (i == 2) {
|
||||||
|
_dateController.addListener(() {
|
||||||
|
setState(() {
|
||||||
|
_showHints[i] =
|
||||||
|
!_focusNodes[i].hasFocus && (_getControllerText(i).isEmpty);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkUserId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
for (var focusNode in _focusNodes) {
|
||||||
|
focusNode.dispose();
|
||||||
|
}
|
||||||
|
_fullNameController.dispose();
|
||||||
|
_dateController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> getUserId() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
return prefs.getString('userId'); // Retrieve the userId
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> checkUserId() async {
|
||||||
|
userId = await getUserId();
|
||||||
|
//userId = 'tplu4by67phkfoq';
|
||||||
|
if (userId != null && userId.isNotEmpty) {
|
||||||
|
print('User ID: $userId');
|
||||||
|
_fetchUserData();
|
||||||
|
} else {
|
||||||
|
print('No userId found');
|
||||||
|
// Handle case where userId is not available
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _fetchUserData() async {
|
||||||
|
try {
|
||||||
|
final adminAuth = await _pb.admins
|
||||||
|
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
|
||||||
|
final adminToken = adminAuth.token;
|
||||||
|
print('adminToken- ${adminToken}');
|
||||||
|
final userDetailsResponse = await _pb.collection('users').getOne(
|
||||||
|
userId,
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer $adminToken',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
print('userDetails: $userDetailsResponse');
|
||||||
|
setState(() {
|
||||||
|
_usernameController.text = userDetailsResponse.data['username'] ?? '';
|
||||||
|
_emailController.text = userDetailsResponse.data['email'] ?? '';
|
||||||
|
_fullNameController.text = userDetailsResponse.data['full_name'] ?? '';
|
||||||
|
_selectedCountry = userDetailsResponse.data['country_region'] ?? '';
|
||||||
|
_isProfileCompleted =
|
||||||
|
userDetailsResponse.data['is_profile_completed'] ?? false;
|
||||||
|
//_isProfileCompleted = true ;
|
||||||
|
|
||||||
|
// Parse and format the date
|
||||||
|
String dateString = userDetailsResponse.data['dob'] ?? '';
|
||||||
|
if (dateString.isNotEmpty) {
|
||||||
|
DateTime dob = DateTime.parse(dateString);
|
||||||
|
_dateController.text =
|
||||||
|
DateFormat('dd/MM/yyyy').format(dob); // Format to dd/mm/yyyy
|
||||||
|
} else {
|
||||||
|
_dateController.text = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the avatar URL
|
||||||
|
String avatarFilename = userDetailsResponse.data['avatar'] ?? '';
|
||||||
|
String recordId = userId;
|
||||||
|
String collectionId =
|
||||||
|
userDetailsResponse.data['collectionId'] ?? '_pb_users_auth_';
|
||||||
|
|
||||||
|
if (avatarFilename.isNotEmpty && recordId.isNotEmpty) {
|
||||||
|
_avatarUrl =
|
||||||
|
'https://pb.venbait.in/api/files/$collectionId/$recordId/$avatarFilename';
|
||||||
|
} else {
|
||||||
|
_avatarUrl = ''; // Reset to default or empty
|
||||||
|
}
|
||||||
|
|
||||||
|
// String avatarFilename = userDetailsResponse.data['avatar'] ?? '';
|
||||||
|
// //print("PROFILE AVAILABLE -$avatarFilename");
|
||||||
|
// String recordId = userId;
|
||||||
|
// //String recordId = userDetailsResponse.data['id'] ?? "";
|
||||||
|
// print("PROFILE AVAILABLE -${userDetailsResponse.data['id']}");
|
||||||
|
// //print("User ID: $recordId");
|
||||||
|
|
||||||
|
//String collectionId = userDetailsResponse.data['collectionId'] ?? '_pb_users_auth_';
|
||||||
|
|
||||||
|
// Ensure recordId and avatarFilename are valid
|
||||||
|
// if (avatarFilename.isNotEmpty && recordId.isNotEmpty) {
|
||||||
|
// print("PROFILE AVAILABLE");
|
||||||
|
// String _avatarUrl = 'https://pb.venbait.in/api/files/$collectionId/$recordId/$avatarFilename';
|
||||||
|
// print("PROFILE AVAILABLE1- $_avatarUrl");
|
||||||
|
// //_avatarUrl = File(imageUrl); // This won't work directly for a URL, you need to download the image first
|
||||||
|
// } else {
|
||||||
|
// print("PROFILE NOT AVAILABLE");
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
print('Error fetching user details: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _pickImage() async {
|
||||||
|
final XFile? pickedFile =
|
||||||
|
await _picker.pickImage(source: ImageSource.gallery);
|
||||||
|
if (pickedFile != null) {
|
||||||
|
setState(() {
|
||||||
|
_profileImage = File(pickedFile.path);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to open the date picker
|
||||||
|
Future<void> _pickDate() async {
|
||||||
|
final DateTime today = DateTime.now();
|
||||||
|
final DateTime initialDate = _selectedDate ??
|
||||||
|
today.subtract(
|
||||||
|
const Duration(days: 365 * 18)); // Default to 18 years ago
|
||||||
|
final DateTime firstDate = today.subtract(const Duration(
|
||||||
|
days: 365 * 100)); // Allow picking dates back to 100 years ago
|
||||||
|
final DateTime lastDate = today; // Allow picking dates up to today
|
||||||
|
|
||||||
|
// Updated Date format to DD/MM/YYYY
|
||||||
|
final DateFormat _dateFormat = DateFormat('dd/MM/yyyy');
|
||||||
|
|
||||||
|
final DateTime? pickedDate = await showDatePicker(
|
||||||
|
context: context,
|
||||||
|
initialDate: initialDate,
|
||||||
|
firstDate: firstDate,
|
||||||
|
lastDate: lastDate,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (pickedDate != null && pickedDate != _selectedDate) {
|
||||||
|
setState(() {
|
||||||
|
_selectedDate = pickedDate;
|
||||||
|
_dateController.text = _dateFormat.format(pickedDate);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to validate the DOB field
|
||||||
|
String? _validateDob(String? value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return 'Required';
|
||||||
|
}
|
||||||
|
|
||||||
|
final DateTime selectedDate = _selectedDate!;
|
||||||
|
final DateTime today = DateTime.now();
|
||||||
|
|
||||||
|
// Check if the selected date is in the future
|
||||||
|
if (selectedDate.isAfter(today)) {
|
||||||
|
return 'Date of birth cannot be in the future';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? _validateDropdown(String? value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return 'Required';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _toggleCheckbox(bool? value) {
|
||||||
|
setState(() {
|
||||||
|
isChecked = value ?? false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void showConfirmationDialog(BuildContext context) async {
|
||||||
|
final result = await showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => const ConfirmationDialog(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result == true) {
|
||||||
|
// Validate only the country field
|
||||||
|
if (_validateDropdown(_selectedCountry) == null) {
|
||||||
|
try {
|
||||||
|
String userID = userId;
|
||||||
|
|
||||||
|
print("ShowConfirmationuserID - $userID ");
|
||||||
|
// Retrieve data from the country/region field
|
||||||
|
String countryRegion =
|
||||||
|
_selectedCountry ?? ''; // Ensure the country is selected
|
||||||
|
|
||||||
|
// Create a multipart request
|
||||||
|
final uri =
|
||||||
|
Uri.parse('${_pb.baseUrl}/api/collections/users/records/$userID');
|
||||||
|
final request = http.MultipartRequest('PATCH', uri);
|
||||||
|
|
||||||
|
// Add fields to the request
|
||||||
|
request.fields['country_region'] =
|
||||||
|
countryRegion; // Only update country here
|
||||||
|
|
||||||
|
// If profile image exists, add it
|
||||||
|
if (_profileImage != null) {
|
||||||
|
request.files.add(await http.MultipartFile.fromPath(
|
||||||
|
'avatar',
|
||||||
|
_profileImage!.path,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add headers (e.g., authorization)
|
||||||
|
request.headers['Authorization'] = 'Bearer ${_pb.authStore.token}';
|
||||||
|
|
||||||
|
// Send the request
|
||||||
|
final response = await request.send();
|
||||||
|
print(response);
|
||||||
|
|
||||||
|
// Handle response
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
_resetFormFields();
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text("Profile updated successfully!")),
|
||||||
|
);
|
||||||
|
context.go('/myhomepage');
|
||||||
|
} else {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content:
|
||||||
|
Text("Failed to update profile: ${response.statusCode}")),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text("Failed to update profile: $error")),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Show an error if the country is invalid
|
||||||
|
setState(() {
|
||||||
|
showError = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _resetFormFields() {
|
||||||
|
print('reset');
|
||||||
|
setState(() {
|
||||||
|
// Reset all text controllers
|
||||||
|
_fullNameController.clear();
|
||||||
|
_dateController.clear();
|
||||||
|
_selectedCountry = null;
|
||||||
|
isChecked = false;
|
||||||
|
|
||||||
|
// Reset profile image
|
||||||
|
_profileImage = null;
|
||||||
|
|
||||||
|
// Reset form validation state
|
||||||
|
_formKey.currentState?.reset();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BaseScaffold(
|
||||||
|
title: Text("Profile"),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 10.0, right: 10.0, top: 3.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
SingleChildScrollView(
|
||||||
|
child: Form(
|
||||||
|
key: _formKey,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(20.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
CircleAvatar(
|
||||||
|
radius: 50,
|
||||||
|
backgroundImage: _profileImage != null
|
||||||
|
? FileImage(
|
||||||
|
_profileImage!) // If a local file is selected
|
||||||
|
: _avatarUrl.isNotEmpty
|
||||||
|
? NetworkImage(_avatarUrl) // Load from URL
|
||||||
|
: AssetImage(
|
||||||
|
"assets/edit_profile/profile.png")
|
||||||
|
as ImageProvider,
|
||||||
|
|
||||||
|
//backgroundImage: NetworkImage(_avatarUrl) as ImageProvider,
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.bottomRight,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: _pickImage, // Call `_pickImage` on tap
|
||||||
|
child: CircleAvatar(
|
||||||
|
radius: 15,
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
child: Icon(
|
||||||
|
Icons.camera_alt,
|
||||||
|
size: 15,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"User Name",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16, fontWeight: FontWeight.w500),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
TextFormField(
|
||||||
|
controller: _usernameController,
|
||||||
|
focusNode: _focusNodes[0],
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: _showHints[0] ? 'Mohammad Hassan' : null,
|
||||||
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"E-mail",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16, fontWeight: FontWeight.w500),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
TextFormField(
|
||||||
|
controller: _emailController,
|
||||||
|
focusNode: _focusNodes[1],
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: _showHints[1]
|
||||||
|
? 'mohammad.hassan@fcsc.gov.ae'
|
||||||
|
: null,
|
||||||
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
enabled: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Full Name",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16, fontWeight: FontWeight.w500),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
TextFormField(
|
||||||
|
enabled: false,
|
||||||
|
validator: (value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return 'Required';
|
||||||
|
}
|
||||||
|
|
||||||
|
//RegExp(r"^[a-zA-Z\s]+$");
|
||||||
|
final nameRegex = RegExp(
|
||||||
|
r"^[a-zA-Z0-9'\-\u0627\u0639~\u00C0-\u00FF]+$");
|
||||||
|
if (!nameRegex.hasMatch(value)) {
|
||||||
|
return 'Invalid Characters';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
controller: _fullNameController,
|
||||||
|
focusNode: _focusNodes[2],
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: _showHints[2] ? 'Mohammad' : null,
|
||||||
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
// counterText: '',
|
||||||
|
enabled: !_isProfileCompleted,
|
||||||
|
),
|
||||||
|
maxLength:
|
||||||
|
40, // Set the maximum length to 20 characters
|
||||||
|
maxLengthEnforcement: MaxLengthEnforcement.enforced,
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Date of Birth*",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16, fontWeight: FontWeight.w500),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
TextFormField(
|
||||||
|
controller: _dateController,
|
||||||
|
focusNode: _focusNodes[3],
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
hintText: _showHints[3]
|
||||||
|
? 'Select your Date of Birth'
|
||||||
|
: null,
|
||||||
|
//hintText: 'Select your Date of Birth',
|
||||||
|
suffixIcon: const Icon(Icons.arrow_drop_down_sharp),
|
||||||
|
enabled: !_isProfileCompleted,
|
||||||
|
),
|
||||||
|
readOnly: true,
|
||||||
|
onTap: _pickDate,
|
||||||
|
validator: _validateDob,
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Country/Region*",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16, fontWeight: FontWeight.w500),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
DropdownButtonFormField<String>(
|
||||||
|
value: _selectedCountry,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
labelText: 'Select',
|
||||||
|
),
|
||||||
|
items: _countries
|
||||||
|
.map((item) => DropdownMenuItem<String>(
|
||||||
|
value: item,
|
||||||
|
child: Text(item),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
onChanged: (String? newValue) {
|
||||||
|
setState(() {
|
||||||
|
_selectedCountry = newValue;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
validator: _validateDropdown,
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
if (!_isProfileCompleted) // Conditional rendering
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
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: Column(
|
||||||
|
mainAxisAlignment:
|
||||||
|
MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment:
|
||||||
|
CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(height: 10),
|
||||||
|
Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
text: 'I agree to the ',
|
||||||
|
style:
|
||||||
|
TextStyle(color: Colors.black),
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: 'Terms & Conditions',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.blue,
|
||||||
|
decoration:
|
||||||
|
TextDecoration.underline,
|
||||||
|
),
|
||||||
|
recognizer:
|
||||||
|
TapGestureRecognizer()
|
||||||
|
..onTap = () {
|
||||||
|
// Add action for Terms & Conditions tap
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: ' and ',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.black),
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: 'Privacy Policy',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.blue,
|
||||||
|
decoration:
|
||||||
|
TextDecoration.underline,
|
||||||
|
),
|
||||||
|
recognizer:
|
||||||
|
TapGestureRecognizer()
|
||||||
|
..onTap = () {
|
||||||
|
// Add action for Privacy Policy tap
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: ' of FCSC.',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.black),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.visible,
|
||||||
|
softWrap: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (showError)
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.only(left: 10.0),
|
||||||
|
child: Text(
|
||||||
|
'Please agree to terms and conditions',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.red[700],
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Center(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) =>
|
||||||
|
Changepassword(userId: userId)),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
"Change Password",
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.blue,
|
||||||
|
decoration: TextDecoration.underline),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
ElevatedButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
showConfirmationDialog(context);
|
||||||
|
|
||||||
|
// if (_formKey.currentState?.validate() ?? false){
|
||||||
|
// _formKey.currentState?.save();
|
||||||
|
// showConfirmationDialog(context);
|
||||||
|
// } else {
|
||||||
|
// setState(() {
|
||||||
|
// showError =
|
||||||
|
// !isChecked; // Show error if the checkbox is not checked
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
icon: Icon(
|
||||||
|
Icons.save,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
label: Text(
|
||||||
|
'Save',
|
||||||
|
style: TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Color(0xFF92722A),
|
||||||
|
minimumSize: Size(double.infinity, 50),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
//bottomNavigationBar: MyBottomNavBar(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConfirmationDialog extends StatelessWidget {
|
||||||
|
const ConfirmationDialog({Key? key}) : super(key: key);
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Are you sure you want to save this page?",
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Color(0xFF898C81),
|
||||||
|
fontFamily: 'Roboto',
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
SizedBox(height: 8),
|
||||||
|
Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
text: "Once saved, you will not be able to change your ",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Color(0xFF898C81),
|
||||||
|
fontFamily: 'Roboto',
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: "Country",
|
||||||
|
style:
|
||||||
|
TextStyle(fontWeight: FontWeight.w700), // Bold for "name"
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: " or ",
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: "Profile Image",
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.w700), // Bold for "date of birth"
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: ".",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
contentPadding: const EdgeInsets.fromLTRB(24, 20, 24, 0),
|
||||||
|
actionsPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
|
actions: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
OutlinedButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop(false);
|
||||||
|
},
|
||||||
|
style: OutlinedButton.styleFrom(
|
||||||
|
side: BorderSide(color: Color(0xFF92722A)),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(7)),
|
||||||
|
), // Set the border color here
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
"Cancel",
|
||||||
|
style: TextStyle(color: Color(0xFF92722A)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(true),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: const Color(0xFF92722A), // Brown color
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Text(
|
||||||
|
'Confirm',
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 5,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,7 +17,6 @@ import '../../../../config/my_theme.dart';
|
|||||||
import '../../../../domain/use_cases/preferences_use_case.dart';
|
import '../../../../domain/use_cases/preferences_use_case.dart';
|
||||||
import '../../../components/my_toggle.dart';
|
import '../../../components/my_toggle.dart';
|
||||||
|
|
||||||
|
|
||||||
class FeedbackForm extends StatefulWidget {
|
class FeedbackForm extends StatefulWidget {
|
||||||
const FeedbackForm({super.key});
|
const FeedbackForm({super.key});
|
||||||
|
|
||||||
@ -28,7 +27,7 @@ class FeedbackForm extends StatefulWidget {
|
|||||||
class _FeedbackFormState extends State<FeedbackForm>
|
class _FeedbackFormState extends State<FeedbackForm>
|
||||||
with WidgetsBindingObserver {
|
with WidgetsBindingObserver {
|
||||||
final _pb =
|
final _pb =
|
||||||
PocketBase('https://pb.venbait.in'); // Initialize PocketBase client
|
PocketBase('https://pb.venbait.in'); // Initialize PocketBase client
|
||||||
// final _pb =
|
// final _pb =
|
||||||
// PocketBase('http://127.0.0.1:8090'); // Initialize PocketBase client
|
// PocketBase('http://127.0.0.1:8090'); // Initialize PocketBase client
|
||||||
|
|
||||||
@ -43,34 +42,35 @@ class _FeedbackFormState extends State<FeedbackForm>
|
|||||||
bool _isFeedbackFailed = false; // New flag for failed submission
|
bool _isFeedbackFailed = false; // New flag for failed submission
|
||||||
bool _isSmileySelected = true; // Track if smiley is selected
|
bool _isSmileySelected = true; // Track if smiley is selected
|
||||||
dynamic configEmail;
|
dynamic configEmail;
|
||||||
|
dynamic userId;
|
||||||
|
|
||||||
List<Map<String, dynamic>> get _emojiOptions => [
|
List<Map<String, dynamic>> get _emojiOptions => [
|
||||||
{
|
{
|
||||||
"icon": Icons.sentiment_very_dissatisfied,
|
"icon": Icons.sentiment_very_dissatisfied,
|
||||||
"label": context.translate("Terrible", "رهيب"), // Translate here
|
"label": context.translate("Terrible", "رهيب"), // Translate here
|
||||||
"value": 1,
|
"value": 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"icon": Icons.sentiment_dissatisfied,
|
"icon": Icons.sentiment_dissatisfied,
|
||||||
"label": context.translate("Bad", "سيء"), // Translate here
|
"label": context.translate("Bad", "سيء"), // Translate here
|
||||||
"value": 2,
|
"value": 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"icon": Icons.sentiment_neutral,
|
"icon": Icons.sentiment_neutral,
|
||||||
"label": context.translate("Okay", "تمام"), // Translate here
|
"label": context.translate("Okay", "تمام"), // Translate here
|
||||||
"value": 3,
|
"value": 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"icon": Icons.sentiment_satisfied,
|
"icon": Icons.sentiment_satisfied,
|
||||||
"label": context.translate("Good", "جيد"), // Translate here
|
"label": context.translate("Good", "جيد"), // Translate here
|
||||||
"value": 4,
|
"value": 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"icon": Icons.sentiment_very_satisfied,
|
"icon": Icons.sentiment_very_satisfied,
|
||||||
"label": context.translate("Amazing", "مدهش"), // Translate here
|
"label": context.translate("Amazing", "مدهش"), // Translate here
|
||||||
"value": 5,
|
"value": 5,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
static const int _characterLimit = 1200;
|
static const int _characterLimit = 1200;
|
||||||
final RegExp _allowedCharacters = RegExp(r'^[a-zA-Z0-9 .,!?-]*$');
|
final RegExp _allowedCharacters = RegExp(r'^[a-zA-Z0-9 .,!?-]*$');
|
||||||
@ -83,7 +83,23 @@ class _FeedbackFormState extends State<FeedbackForm>
|
|||||||
WidgetsBinding.instance.addObserver(this);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
_loadFeedbackText();
|
_loadFeedbackText();
|
||||||
_feedbackController.addListener(_handleTextChange);
|
_feedbackController.addListener(_handleTextChange);
|
||||||
fetchEmailConfiguration();
|
// fetchEmailConfiguration();
|
||||||
|
checkUserId();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> getUserId() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
return prefs.getString('userId'); // Retrieve the userId
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> checkUserId() async {
|
||||||
|
userId = await getUserId();
|
||||||
|
if (userId != null && userId.isNotEmpty) {
|
||||||
|
print('User ID: $userId');
|
||||||
|
} else {
|
||||||
|
print('No userId found');
|
||||||
|
// Handle case where userId is not available
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -217,8 +233,8 @@ class _FeedbackFormState extends State<FeedbackForm>
|
|||||||
child: _isFeedbackSubmitted
|
child: _isFeedbackSubmitted
|
||||||
? _buildThankYouMessage()
|
? _buildThankYouMessage()
|
||||||
: _isFeedbackFailed
|
: _isFeedbackFailed
|
||||||
? _buildFailureMessage()
|
? _buildFailureMessage()
|
||||||
: _buildFeedbackForm(),
|
: _buildFeedbackForm(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -303,7 +319,7 @@ class _FeedbackFormState extends State<FeedbackForm>
|
|||||||
_buildRatingRow(
|
_buildRatingRow(
|
||||||
context.translate('Ease of use', 'سهولة الاستخدام'),
|
context.translate('Ease of use', 'سهولة الاستخدام'),
|
||||||
_easeOfUseRating,
|
_easeOfUseRating,
|
||||||
(rating) {
|
(rating) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_easeOfUseRating = rating;
|
_easeOfUseRating = rating;
|
||||||
});
|
});
|
||||||
@ -312,7 +328,7 @@ class _FeedbackFormState extends State<FeedbackForm>
|
|||||||
_buildRatingRow(
|
_buildRatingRow(
|
||||||
context.translate('Quality', 'جودة'),
|
context.translate('Quality', 'جودة'),
|
||||||
_qualityRating,
|
_qualityRating,
|
||||||
(rating) {
|
(rating) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_qualityRating = rating;
|
_qualityRating = rating;
|
||||||
});
|
});
|
||||||
@ -321,7 +337,7 @@ class _FeedbackFormState extends State<FeedbackForm>
|
|||||||
_buildRatingRow(
|
_buildRatingRow(
|
||||||
context.translate('Design', 'تصميم'),
|
context.translate('Design', 'تصميم'),
|
||||||
_designRating,
|
_designRating,
|
||||||
(rating) {
|
(rating) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_designRating = rating;
|
_designRating = rating;
|
||||||
});
|
});
|
||||||
@ -330,7 +346,7 @@ class _FeedbackFormState extends State<FeedbackForm>
|
|||||||
_buildRatingRow(
|
_buildRatingRow(
|
||||||
context.translate('Redundant', 'متكرر'),
|
context.translate('Redundant', 'متكرر'),
|
||||||
_redundancyRating,
|
_redundancyRating,
|
||||||
(rating) {
|
(rating) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_redundancyRating = rating;
|
_redundancyRating = rating;
|
||||||
});
|
});
|
||||||
@ -351,7 +367,7 @@ class _FeedbackFormState extends State<FeedbackForm>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
errorText:
|
errorText:
|
||||||
_hasError ? _errorMessage : null, // Show error below field
|
_hasError ? _errorMessage : null, // Show error below field
|
||||||
),
|
),
|
||||||
onChanged: (text) {
|
onChanged: (text) {
|
||||||
// Trigger re-validation and character limit on each change
|
// Trigger re-validation and character limit on each change
|
||||||
@ -428,7 +444,7 @@ class _FeedbackFormState extends State<FeedbackForm>
|
|||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => context.go('/myhomepage'),
|
onPressed: () => context.go('/myhomepage'),
|
||||||
//context.go('/${context.language}/${BottomNavBarItem.home.routePath}'),
|
//context.go('/${context.language}/${BottomNavBarItem.home.routePath}'),
|
||||||
child: const Text("Go to Home"),
|
child: const Text("Go to Home"),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -495,10 +511,10 @@ class _FeedbackFormState extends State<FeedbackForm>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildRatingRow(
|
Widget _buildRatingRow(
|
||||||
String label,
|
String label,
|
||||||
double rating,
|
double rating,
|
||||||
Function(double) onRatingUpdate,
|
Function(double) onRatingUpdate,
|
||||||
) {
|
) {
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
@ -548,12 +564,13 @@ class _FeedbackFormState extends State<FeedbackForm>
|
|||||||
}
|
}
|
||||||
|
|
||||||
final feedbackData = {
|
final feedbackData = {
|
||||||
|
"userId": userId,
|
||||||
"ease_of_use": _easeOfUseRating,
|
"ease_of_use": _easeOfUseRating,
|
||||||
"quality": _qualityRating,
|
"quality": _qualityRating,
|
||||||
"design": _designRating,
|
"design": _designRating,
|
||||||
"redundancy": _redundancyRating,
|
"redundancy": _redundancyRating,
|
||||||
"emoji_rating": _emojiOptions[_selectedEmojiIndex!]
|
"emoji_rating": _emojiOptions[_selectedEmojiIndex!]
|
||||||
["label"], // Emoji rating
|
["label"], // Emoji rating
|
||||||
"feedback": _feedbackController.text,
|
"feedback": _feedbackController.text,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -568,7 +585,7 @@ class _FeedbackFormState extends State<FeedbackForm>
|
|||||||
final createdTime = response.created;
|
final createdTime = response.created;
|
||||||
// Send email
|
// Send email
|
||||||
|
|
||||||
await sendFeedbackEmail(feedbackData, createdTime);
|
// await sendFeedbackEmail(feedbackData, createdTime);
|
||||||
await _removeFeedbackText();
|
await _removeFeedbackText();
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(content: Text("Feedback submitted successfully!")),
|
const SnackBar(content: Text("Feedback submitted successfully!")),
|
||||||
@ -598,107 +615,107 @@ class _FeedbackFormState extends State<FeedbackForm>
|
|||||||
_feedbackController.clear();
|
_feedbackController.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> fetchEmailConfiguration() async {
|
// Future<void> fetchEmailConfiguration() async {
|
||||||
try {
|
// try {
|
||||||
// Fetch data from the email_configuration collection
|
// // Fetch data from the email_configuration collection
|
||||||
final response =
|
// final response =
|
||||||
await _pb.collection('email_configuration').getFullList();
|
// await _pb.collection('email_configuration').getFullList();
|
||||||
|
//
|
||||||
// Filter the data where the label is "Feedback"
|
// // Filter the data where the label is "Feedback"
|
||||||
final feedbackConfig = response.firstWhere(
|
// final feedbackConfig = response.firstWhere(
|
||||||
(item) =>
|
// (item) =>
|
||||||
item.data['label'] == 'Feedback', // Accessing the 'data' property
|
// item.data['label'] == 'Feedback', // Accessing the 'data' property
|
||||||
);
|
// );
|
||||||
|
//
|
||||||
// Check if a match is found
|
// // Check if a match is found
|
||||||
if (feedbackConfig != null) {
|
// if (feedbackConfig != null) {
|
||||||
configEmail = feedbackConfig.data['email']; // Access the 'email' value
|
// configEmail = feedbackConfig.data['email']; // Access the 'email' value
|
||||||
} else {
|
// } else {
|
||||||
print('No feedback configuration found.');
|
// print('No feedback configuration found.');
|
||||||
}
|
// }
|
||||||
} catch (e) {
|
// } catch (e) {
|
||||||
print('Error fetching email configuration: $e');
|
// print('Error fetching email configuration: $e');
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Future<void> sendFeedbackEmail(
|
// Future<void> sendFeedbackEmail(
|
||||||
Map<String, dynamic> feedbackData, createdTime) async {
|
// Map<String, dynamic> feedbackData, createdTime) async {
|
||||||
String username = 'emailapikey'; // Your SMTP username (API key)
|
// String username = 'emailapikey'; // Your SMTP username (API key)
|
||||||
String password =
|
// String password =
|
||||||
'PHtE6r0MFu66jTQp8BAFsP7sH5TwNd4v/+02KwBH5ItACvAES01Tot4okDawqhoiB/FEHfaey4Nvteyf5ePQJG28YW9OCWqyqK3sx/VYSPOZsbq6x00auVwYd0zUVY7pe9ds0yLTvNraNA=='; // Your SMTP password
|
// 'PHtE6r0MFu66jTQp8BAFsP7sH5TwNd4v/+02KwBH5ItACvAES01Tot4okDawqhoiB/FEHfaey4Nvteyf5ePQJG28YW9OCWqyqK3sx/VYSPOZsbq6x00auVwYd0zUVY7pe9ds0yLTvNraNA=='; // Your SMTP password
|
||||||
|
//
|
||||||
final smtpServer = SmtpServer('smtp.zeptomail.in',
|
// final smtpServer = SmtpServer('smtp.zeptomail.in',
|
||||||
port: 587,
|
// port: 587,
|
||||||
username: username,
|
// username: username,
|
||||||
password: password,
|
// password: password,
|
||||||
ssl: false, // Use TLS
|
// ssl: false, // Use TLS
|
||||||
ignoreBadCertificate:
|
// ignoreBadCertificate:
|
||||||
true); // Set to true if you're testing with a self-signed certificate
|
// true); // Set to true if you're testing with a self-signed certificate
|
||||||
|
//
|
||||||
// Check if additional feedback was provided
|
// // Check if additional feedback was provided
|
||||||
String additionalFeedback = feedbackData["feedback"]?.isNotEmpty == true
|
// String additionalFeedback = feedbackData["feedback"]?.isNotEmpty == true
|
||||||
? feedbackData["feedback"]
|
// ? feedbackData["feedback"]
|
||||||
: "No additional feedback provided.";
|
// : "No additional feedback provided.";
|
||||||
|
//
|
||||||
// Format separately for date and time
|
// // Format separately for date and time
|
||||||
// Parse the string to DateTime
|
// // Parse the string to DateTime
|
||||||
// Parse the input as UTC and convert to DateTime in UTC timezone
|
// // Parse the input as UTC and convert to DateTime in UTC timezone
|
||||||
DateTime feedbackDateTime = DateTime.parse(createdTime).toUtc();
|
// DateTime feedbackDateTime = DateTime.parse(createdTime).toUtc();
|
||||||
|
//
|
||||||
// Format date as DD-MM-YYYY in UTC
|
// // Format date as DD-MM-YYYY in UTC
|
||||||
final String formattedDate =
|
// final String formattedDate =
|
||||||
DateFormat('dd-MM-yyyy').format(feedbackDateTime);
|
// DateFormat('dd-MM-yyyy').format(feedbackDateTime);
|
||||||
|
//
|
||||||
// Format to the desired output: "dd.MM.yyyy HH:mm 'UTC'"
|
// // Format to the desired output: "dd.MM.yyyy HH:mm 'UTC'"
|
||||||
final DateFormat formatter = DateFormat("dd.MM.yyyy HH:mm 'UTC'");
|
// final DateFormat formatter = DateFormat("dd.MM.yyyy HH:mm 'UTC'");
|
||||||
String formattedDateTime = formatter.format(feedbackDateTime);
|
// String formattedDateTime = formatter.format(feedbackDateTime);
|
||||||
|
//
|
||||||
String email = configEmail; // Direct assignment
|
// String email = configEmail; // Direct assignment
|
||||||
// Create the email message
|
// // Create the email message
|
||||||
final message = Message()
|
// final message = Message()
|
||||||
..from = Address('bbone@venbait.in', 'FCSC')
|
// ..from = Address('bbone@venbait.in', 'FCSC')
|
||||||
..recipients.add(email) // Set the recipient email
|
// ..recipients.add(email) // Set the recipient email
|
||||||
..subject = 'UAE Stats Feedback'
|
// ..subject = 'UAE Stats Feedback'
|
||||||
..text = '''
|
// ..text = '''
|
||||||
Dear [App Owner/Admin],
|
// Dear [App Owner/Admin],
|
||||||
|
//
|
||||||
You have received new feedback from a user through the mobile application.
|
// You have received new feedback from a user through the mobile application.
|
||||||
|
//
|
||||||
User Details:
|
// User Details:
|
||||||
|
//
|
||||||
1. Name: Guest
|
// 1. Name: Guest
|
||||||
2. Date of Submission: $formattedDate
|
// 2. Date of Submission: $formattedDate
|
||||||
3. Time of Submission: $formattedDateTime
|
// 3. Time of Submission: $formattedDateTime
|
||||||
|
//
|
||||||
Feedback:
|
// Feedback:
|
||||||
|
//
|
||||||
1. How was your experience with us today? Rating: ${feedbackData["emoji_rating"]}
|
// 1. How was your experience with us today? Rating: ${feedbackData["emoji_rating"]}
|
||||||
2. How did we perform in key areas?
|
// 2. How did we perform in key areas?
|
||||||
1. Ease of Use: ${feedbackData["ease_of_use"]}
|
// 1. Ease of Use: ${feedbackData["ease_of_use"]}
|
||||||
2. Quality: ${feedbackData["quality"]}
|
// 2. Quality: ${feedbackData["quality"]}
|
||||||
3. Design: ${feedbackData["design"]}
|
// 3. Design: ${feedbackData["design"]}
|
||||||
4. Redundancy: ${feedbackData["redundancy"]}
|
// 4. Redundancy: ${feedbackData["redundancy"]}
|
||||||
3. Additional Feedback:
|
// 3. Additional Feedback:
|
||||||
1. $additionalFeedback
|
// 1. $additionalFeedback
|
||||||
|
//
|
||||||
Thank you,
|
// Thank you,
|
||||||
The FCSC App Team
|
// The FCSC App Team
|
||||||
''';
|
// ''';
|
||||||
|
//
|
||||||
try {
|
// try {
|
||||||
// Send the email
|
// // Send the email
|
||||||
print("Message details:");
|
// print("Message details:");
|
||||||
print("From: ${message.from}");
|
// print("From: ${message.from}");
|
||||||
print("To: ${message.recipients}");
|
// print("To: ${message.recipients}");
|
||||||
print("Subject: ${message.subject}");
|
// print("Subject: ${message.subject}");
|
||||||
print("Body: ${message.text}");
|
// print("Body: ${message.text}");
|
||||||
final sendReport = await send(message, smtpServer);
|
// final sendReport = await send(message, smtpServer);
|
||||||
print('Message sent: ' + sendReport.toString());
|
// print('Message sent: ' + sendReport.toString());
|
||||||
} catch (e) {
|
// } catch (e) {
|
||||||
print('Message not sent: $e');
|
// print('Message not sent: $e');
|
||||||
// Handle the error as needed
|
// // Handle the error as needed
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
@ -763,7 +780,7 @@ class LangToggle extends ConsumerWidget {
|
|||||||
|
|
||||||
ref.read(preferencesUseCaseProvider.notifier).updatePreferences(
|
ref.read(preferencesUseCaseProvider.notifier).updatePreferences(
|
||||||
(prefs) => prefs.copyWith(language: languageAfter),
|
(prefs) => prefs.copyWith(language: languageAfter),
|
||||||
);
|
);
|
||||||
|
|
||||||
await onLoadFeedback(); // Reload feedback text after toggling language
|
await onLoadFeedback(); // Reload feedback text after toggling language
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,50 +1,25 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:pocketbase/pocketbase.dart';
|
||||||
import 'package:uae_stat/presentation/components/my_bottom_nav_bar.dart';
|
import 'package:uae_stat/presentation/components/my_bottom_nav_bar.dart';
|
||||||
import 'package:uae_stat/presentation/components/my_drawer.dart';
|
import 'package:uae_stat/presentation/components/my_drawer.dart';
|
||||||
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
|
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
|
||||||
|
|
||||||
class ManageUserRouter extends StatefulWidget {
|
class ManageUserRouter extends StatefulWidget {
|
||||||
late final Widget title;
|
late final Widget title;
|
||||||
@override
|
@override
|
||||||
State<ManageUserRouter> createState() => _ManageUserRouterState();
|
State<ManageUserRouter> createState() => _ManageUserRouterState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ManageUserRouterState extends State<ManageUserRouter> {
|
class _ManageUserRouterState extends State<ManageUserRouter> {
|
||||||
// Sample data for the table
|
final _pb = PocketBase('https://pb.venbait.in');
|
||||||
final List<User> userData = List.generate(
|
List<User> userData = [];
|
||||||
5,
|
|
||||||
(index) => User(
|
|
||||||
userName: 'Conan Keller ${index + 1}',
|
|
||||||
emailId: 'ConanKeller${index + 1}@gmail.com',
|
|
||||||
registrationDate: '18/07/2024',
|
|
||||||
status: 'Pending',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Sorting state
|
|
||||||
int _sortColumnIndex = 0;
|
int _sortColumnIndex = 0;
|
||||||
bool _isAscending = true;
|
bool _isAscending = true;
|
||||||
|
List<User> filteredUserData = [];
|
||||||
|
|
||||||
// Method to sort the data based on a column
|
|
||||||
void _sort<T>(Comparable<T> Function(User user) getField, int columnIndex,
|
|
||||||
bool ascending) {
|
|
||||||
setState(() {
|
|
||||||
_sortColumnIndex = columnIndex;
|
|
||||||
_isAscending = ascending;
|
|
||||||
userData.sort((a, b) {
|
|
||||||
final aValue = getField(a);
|
|
||||||
final bValue = getField(b);
|
|
||||||
return ascending
|
|
||||||
? Comparable.compare(aValue, bValue)
|
|
||||||
: Comparable.compare(bValue, aValue);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define dropdown items
|
|
||||||
final List<String> statusOptions = ['Approved', 'Denied', 'Pending'];
|
final List<String> statusOptions = ['Approved', 'Denied', 'Pending'];
|
||||||
|
|
||||||
// Method to get status color
|
|
||||||
Color getStatusColor(String status) {
|
Color getStatusColor(String status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'Approved':
|
case 'Approved':
|
||||||
@ -58,9 +33,80 @@ class _ManageUserRouterState extends State<ManageUserRouter> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
fetchUnverifiedUsers();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> fetchUnverifiedUsers() async {
|
||||||
|
try {
|
||||||
|
await _pb.admins
|
||||||
|
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
|
||||||
|
final result = await _pb.collection('users').getFullList(
|
||||||
|
filter: 'verified = false',
|
||||||
|
);
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
userData = result.map((record) {
|
||||||
|
final createdDate =
|
||||||
|
DateTime.parse(record.created); // Parse the created date
|
||||||
|
final formattedDate = DateFormat('dd/MM/yyyy').format(createdDate);
|
||||||
|
return User(
|
||||||
|
id: record.id, // Correctly passing the ID
|
||||||
|
userName: record.getStringValue('username'),
|
||||||
|
emailId: record.getStringValue('email'),
|
||||||
|
registrationDate: formattedDate,
|
||||||
|
status: record.getStringValue('status'),
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
filteredUserData = List.from(userData);
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
print('Error fetching unverified users: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to filter the list based on the search input
|
||||||
|
void filterUsers(String query) {
|
||||||
|
print('Filterd User: $query');
|
||||||
|
setState(() {
|
||||||
|
filteredUserData = userData.where((user) {
|
||||||
|
return user.userName.toLowerCase().contains(query.toLowerCase()) ||
|
||||||
|
user.emailId.toLowerCase().contains(query.toLowerCase()) ||
|
||||||
|
user.registrationDate.contains(query) ||
|
||||||
|
user.status.toLowerCase().contains(query.toLowerCase());
|
||||||
|
}).toList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _sort<T>(
|
||||||
|
Comparable<T>? Function(User user) getField,
|
||||||
|
int columnIndex,
|
||||||
|
bool ascending,
|
||||||
|
) {
|
||||||
|
setState(() {
|
||||||
|
_sortColumnIndex = columnIndex;
|
||||||
|
_isAscending = ascending;
|
||||||
|
|
||||||
|
filteredUserData.sort((a, b) {
|
||||||
|
final aValue = getField(a);
|
||||||
|
final bValue = getField(b);
|
||||||
|
if (aValue == null || bValue == null) {
|
||||||
|
return 0; // Handle null-safe sorting if needed
|
||||||
|
}
|
||||||
|
return ascending
|
||||||
|
? Comparable.compare(aValue, bValue)
|
||||||
|
: Comparable.compare(bValue, aValue);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
//Method to show a confirmation dialog when status is changed
|
//Method to show a confirmation dialog when status is changed
|
||||||
Future<void> _showConfirmationDialog(User user, String newStatus) async {
|
Future<void> _showConfirmationDialog(User user, String newStatus) async {
|
||||||
|
print('user $user');
|
||||||
double myheight = MediaQuery.of(context).size.height;
|
double myheight = MediaQuery.of(context).size.height;
|
||||||
|
|
||||||
return showDialog<void>(
|
return showDialog<void>(
|
||||||
context: context,
|
context: context,
|
||||||
barrierDismissible: false, // User must tap button to dismiss dialog
|
barrierDismissible: false, // User must tap button to dismiss dialog
|
||||||
@ -69,7 +115,8 @@ class _ManageUserRouterState extends State<ManageUserRouter> {
|
|||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(5.0), // Rounded corners
|
borderRadius: BorderRadius.circular(5.0), // Rounded corners
|
||||||
),
|
),
|
||||||
contentPadding: EdgeInsets.zero, // Ensure no padding issues with close icon
|
contentPadding:
|
||||||
|
EdgeInsets.zero, // Ensure no padding issues with close icon
|
||||||
content: Stack(
|
content: Stack(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
@ -77,7 +124,9 @@ class _ManageUserRouterState extends State<ManageUserRouter> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: myheight/30,),
|
SizedBox(
|
||||||
|
height: myheight / 30,
|
||||||
|
),
|
||||||
Text(
|
Text(
|
||||||
'Are you sure you want to change \n the status to $newStatus?',
|
'Are you sure you want to change \n the status to $newStatus?',
|
||||||
style: TextStyle(fontSize: 15),
|
style: TextStyle(fontSize: 15),
|
||||||
@ -114,25 +163,45 @@ class _ManageUserRouterState extends State<ManageUserRouter> {
|
|||||||
),
|
),
|
||||||
side: BorderSide(
|
side: BorderSide(
|
||||||
color: Colors.blue, // Set the outline color
|
color: Colors.blue, // Set the outline color
|
||||||
width: 2.0, // Set the border width
|
width: 2.0, // Set the border width
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Text('No'),
|
child: Text('No'),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
setState(() {
|
try {
|
||||||
user.status = newStatus; // Change status
|
// Update status in PocketBase
|
||||||
});
|
await _pb.collection('users').update(
|
||||||
Navigator.of(context).pop();
|
user.id, // User's unique ID
|
||||||
|
body: {
|
||||||
|
'status': newStatus, // Update status
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update local state
|
||||||
|
// setState(() {
|
||||||
|
// user.status = newStatus;
|
||||||
|
// });
|
||||||
|
fetchUnverifiedUsers();
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Status updated successfully!')),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Error updating status: $e')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Navigator.of(context).pop(); // Close dialog
|
||||||
},
|
},
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
backgroundColor: Colors.blue, // Set background color
|
backgroundColor: Colors.blue, // Set background color
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius:
|
borderRadius:
|
||||||
BorderRadius.circular(10.0), // Set text color
|
BorderRadius.circular(10.0), // Set text color
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Yes',
|
'Yes',
|
||||||
style: TextStyle(color: Colors.white),
|
style: TextStyle(color: Colors.white),
|
||||||
@ -142,7 +211,6 @@ class _ManageUserRouterState extends State<ManageUserRouter> {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -165,71 +233,97 @@ class _ManageUserRouterState extends State<ManageUserRouter> {
|
|||||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
onChanged: filterUsers,
|
||||||
),
|
),
|
||||||
SizedBox(height: myheight / 40),
|
SizedBox(height: myheight / 40),
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
child: DataTable(
|
child: ConstrainedBox(
|
||||||
sortColumnIndex: _sortColumnIndex,
|
constraints: BoxConstraints(
|
||||||
sortAscending: _isAscending,
|
minWidth: MediaQuery.of(context).size.width,
|
||||||
columns: [
|
),
|
||||||
DataColumn(
|
child: DataTable(
|
||||||
label: const Text('User Name'),
|
sortColumnIndex: _sortColumnIndex,
|
||||||
onSort: (columnIndex, ascending) => _sort(
|
sortAscending: _isAscending,
|
||||||
(user) => user.userName, columnIndex, ascending),
|
columns: [
|
||||||
numeric: false,
|
DataColumn(
|
||||||
),
|
label: const Text('User Name'),
|
||||||
DataColumn(
|
onSort: (columnIndex, ascending) => _sort(
|
||||||
label: const Text('Email ID'),
|
(user) => user.userName, columnIndex, ascending),
|
||||||
onSort: (columnIndex, ascending) =>
|
),
|
||||||
_sort((user) => user.emailId, columnIndex, ascending),
|
DataColumn(
|
||||||
numeric: false,
|
label: const Text('Email ID'),
|
||||||
),
|
onSort: (columnIndex, ascending) => _sort(
|
||||||
DataColumn(
|
(user) => user.emailId, columnIndex, ascending),
|
||||||
label: const Text('Registration Date'),
|
),
|
||||||
onSort: (columnIndex, ascending) => _sort(
|
DataColumn(
|
||||||
(user) => user.registrationDate,
|
label: const Text('Registration Date'),
|
||||||
columnIndex,
|
onSort: (columnIndex, ascending) {
|
||||||
ascending),
|
_sort(
|
||||||
numeric: false,
|
(user) => DateTime.tryParse(
|
||||||
),
|
user.registrationDate), // Convert to DateTime
|
||||||
DataColumn(
|
columnIndex,
|
||||||
label: const Text('Status'),
|
ascending,
|
||||||
onSort: (columnIndex, ascending) =>
|
);
|
||||||
_sort((user) => user.status, columnIndex, ascending),
|
},
|
||||||
numeric: false,
|
),
|
||||||
),
|
DataColumn(
|
||||||
],
|
label: const Text('Status'),
|
||||||
rows: userData.map((user) {
|
onSort: (columnIndex, ascending) => _sort(
|
||||||
return DataRow(
|
(user) => user.status, columnIndex, ascending),
|
||||||
cells: [
|
),
|
||||||
DataCell(Text(user.userName)),
|
],
|
||||||
DataCell(Text(user.emailId)),
|
rows: filteredUserData.isEmpty
|
||||||
DataCell(Text(user.registrationDate)),
|
? [
|
||||||
DataCell(
|
DataRow(
|
||||||
DropdownButton<String>(
|
cells: List<DataCell>.generate(
|
||||||
value: user.status,
|
4, // Ensure it matches the number of DataColumns
|
||||||
items: statusOptions.map((status) {
|
(index) => DataCell(
|
||||||
return DropdownMenuItem<String>(
|
index == 0
|
||||||
value: status,
|
? Text(
|
||||||
child: Text(
|
'No results found',
|
||||||
status,
|
style: TextStyle(
|
||||||
style:
|
fontStyle: FontStyle.italic),
|
||||||
TextStyle(color: getStatusColor(status)),
|
)
|
||||||
|
: const Text(
|
||||||
|
''), // Empty cells for other columns
|
||||||
|
placeholder: true,
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
}).toList(),
|
),
|
||||||
onChanged: (String? newStatus) {
|
]
|
||||||
if (newStatus != null) {
|
: filteredUserData.map((user) {
|
||||||
_showConfirmationDialog(user,
|
return DataRow(
|
||||||
newStatus); // Show dialog for confirmation
|
cells: [
|
||||||
}
|
DataCell(Text(user.userName)),
|
||||||
},
|
DataCell(Text(user.emailId)),
|
||||||
),
|
DataCell(Text(user.registrationDate)),
|
||||||
),
|
DataCell(
|
||||||
],
|
DropdownButton<String>(
|
||||||
);
|
value: user.status,
|
||||||
}).toList(),
|
items: statusOptions.map((status) {
|
||||||
|
return DropdownMenuItem<String>(
|
||||||
|
value: status,
|
||||||
|
child: Text(
|
||||||
|
status,
|
||||||
|
style: TextStyle(
|
||||||
|
color: getStatusColor(status)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
onChanged: (newStatus) {
|
||||||
|
print(user);
|
||||||
|
if (newStatus != null) {
|
||||||
|
_showConfirmationDialog(
|
||||||
|
user, newStatus);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -242,12 +336,14 @@ class _ManageUserRouterState extends State<ManageUserRouter> {
|
|||||||
|
|
||||||
// User data model
|
// User data model
|
||||||
class User {
|
class User {
|
||||||
|
final String id;
|
||||||
final String userName;
|
final String userName;
|
||||||
final String emailId;
|
final String emailId;
|
||||||
final String registrationDate;
|
final String registrationDate;
|
||||||
String status;
|
String status;
|
||||||
|
|
||||||
User({
|
User({
|
||||||
|
required this.id,
|
||||||
required this.userName,
|
required this.userName,
|
||||||
required this.emailId,
|
required this.emailId,
|
||||||
required this.registrationDate,
|
required this.registrationDate,
|
||||||
|
|||||||
@ -23,9 +23,8 @@ class BaseScaffold extends StatelessWidget {
|
|||||||
double myheight = MediaQuery.of(context).size.height;
|
double myheight = MediaQuery.of(context).size.height;
|
||||||
double mywidth = MediaQuery.of(context).size.width;
|
double mywidth = MediaQuery.of(context).size.width;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(title: title, actions: [
|
||||||
title: title,
|
IconButton(onPressed: () {}, icon: Icon(Icons.toggle_off_outlined)),
|
||||||
actions: [IconButton(onPressed: (){}, icon: Icon(Icons.toggle_off_outlined)),
|
|
||||||
]),
|
]),
|
||||||
drawer: Drawer(
|
drawer: Drawer(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
@ -45,7 +44,7 @@ class BaseScaffold extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
Divider(),
|
Divider(),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () => context.go('/profile'),
|
onTap: () => context.go('/editProfile'),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
@ -69,7 +68,6 @@ class BaseScaffold extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: SizedBox(
|
leading: SizedBox(
|
||||||
height: myheight / 15,
|
height: myheight / 15,
|
||||||
@ -79,7 +77,6 @@ class BaseScaffold extends StatelessWidget {
|
|||||||
title: Text('Feedback'),
|
title: Text('Feedback'),
|
||||||
onTap: () => context.go('/feedback'),
|
onTap: () => context.go('/feedback'),
|
||||||
),
|
),
|
||||||
|
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: SizedBox(
|
leading: SizedBox(
|
||||||
height: myheight / 15,
|
height: myheight / 15,
|
||||||
@ -98,18 +95,35 @@ class BaseScaffold extends StatelessWidget {
|
|||||||
type: BottomNavigationBarType.fixed,
|
type: BottomNavigationBarType.fixed,
|
||||||
items: [
|
items: [
|
||||||
BottomNavigationBarItem(
|
BottomNavigationBarItem(
|
||||||
icon: SizedBox(height: myheight/15,width : mywidth/15,child: Image(image: AssetImage('assets/icons/bottom_bar/home.png'))),
|
icon: SizedBox(
|
||||||
|
height: myheight / 15,
|
||||||
|
width: mywidth / 15,
|
||||||
|
child: Image(
|
||||||
|
image: AssetImage('assets/icons/bottom_bar/home.png'))),
|
||||||
label: 'Home',
|
label: 'Home',
|
||||||
),
|
),
|
||||||
BottomNavigationBarItem(
|
BottomNavigationBarItem(
|
||||||
icon: SizedBox(height: myheight/15,width : mywidth/15,child: Image(image: AssetImage('assets/icons/bottom_bar/uae_map.png'))),
|
icon: SizedBox(
|
||||||
|
height: myheight / 15,
|
||||||
|
width: mywidth / 15,
|
||||||
|
child: Image(
|
||||||
|
image: AssetImage('assets/icons/bottom_bar/uae_map.png'))),
|
||||||
label: 'UAE Numbers',
|
label: 'UAE Numbers',
|
||||||
),
|
),
|
||||||
BottomNavigationBarItem(
|
BottomNavigationBarItem(
|
||||||
icon: SizedBox(height: myheight/15,width : mywidth/15,child: Image(image: AssetImage('assets/icons/bottom_bar/ranking.png'))),
|
icon: SizedBox(
|
||||||
|
height: myheight / 15,
|
||||||
|
width: mywidth / 15,
|
||||||
|
child: Image(
|
||||||
|
image:
|
||||||
|
AssetImage('assets/icons/bottom_bar/ranking.png'))),
|
||||||
label: 'Competitiveness'),
|
label: 'Competitiveness'),
|
||||||
BottomNavigationBarItem(
|
BottomNavigationBarItem(
|
||||||
icon: SizedBox(height: myheight/15,width : mywidth/15,child: Image(image: AssetImage('assets/icons/bottom_bar/globe.png'))),
|
icon: SizedBox(
|
||||||
|
height: myheight / 15,
|
||||||
|
width: mywidth / 15,
|
||||||
|
child: Image(
|
||||||
|
image: AssetImage('assets/icons/bottom_bar/globe.png'))),
|
||||||
label: 'Country Profile'),
|
label: 'Country Profile'),
|
||||||
],
|
],
|
||||||
selectedItemColor: Colors.black,
|
selectedItemColor: Colors.black,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user