all functonality added in new router flow

This commit is contained in:
venbaittech 2024-12-12 13:51:27 +05:30
parent 617379deef
commit c75ae680b0
10 changed files with 1528 additions and 439 deletions

6
.run/main.dart.run.xml Normal file
View 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>

View File

@ -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(),
), ),
], ],
); );

View File

@ -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();

View File

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

View File

@ -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,

View File

@ -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(

View File

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

View File

@ -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
}, },

View File

@ -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,

View File

@ -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,