diff --git a/lib/config/my_router.dart b/lib/config/my_router.dart index d1998a87..46dcf678 100644 --- a/lib/config/my_router.dart +++ b/lib/config/my_router.dart @@ -207,7 +207,7 @@ // ), // ), // ]; -// +// // // ShellRoute get inAppRoutes => ShellRoute( // builder: (context, state, child) => LoggedInHomeScaffold( // body: child, @@ -218,7 +218,7 @@ // ], // ); // } -// +// // // String? _pathCache; // // @riverpod @@ -292,9 +292,9 @@ // return router; // } - import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; +import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; import '../domain/use_cases/preferences_use_case.dart'; import '../presentation/Screens/changepassword.dart'; @@ -303,47 +303,59 @@ import '../presentation/Screens/otp_verification.dart'; import '../presentation/Screens/profilepage.dart'; import '../presentation/routes/auth_routes/login_route.dart'; import '../presentation/routes/bottom_bar_routes/tab_routes/home_route.dart'; - - +import '../presentation/routes/drawer_routes/Drawer Items/feedback.dart'; +import '../presentation/routes/drawer_routes/Drawer Items/manage_users.dart'; final GoRouter router = GoRouter( routes: [ GoRoute( path: '/', - builder: (context, state) => LoginRoute(), +//builder: (context, state) => LoginRoute(), + builder: (context, state) => MyHomePage(), ), - GoRoute( - path: '/profile', - builder: (context, state) => ProfileScreen(userId: '',), - ), - // GoRoute( - // path: '/home', - // builder: (context, state) => HomeRoute(), - // ), - GoRoute( path: '/myhomepage', builder: (context, state) => MyHomePage(), ), - GoRoute( - path: '/changepw', - builder: (context, state) => Changepassword(), - ), GoRoute( path: '/mailverification', - builder: (context, state) => EmailVerificationScreen(email: '', userId: '', otp: '', otpId: '', sendVerificationCode: (String ) { },), + builder: (context, state) => EmailVerificationScreen( + email: '', + userId: '', + otp: '', + otpId: '', + sendVerificationCode: (String) {}, + ), ), GoRoute( - path: '/changepass', - builder: (context, state) => Changepassword(), + path: '/changepassword', + builder: (context, state) => Changepassword( + userId: '', + ), ), GoRoute( path: '/confirmpasswd', - builder: (context, state) => ConfirmPassword(email: '', userId: '',), + builder: (context, state) => ConfirmPassword( + email: '', + userId: '', + ), ), - - - +// Drawer Routers + GoRoute( + path: '/feedback', + builder: (context, state) => FeedbackForm(), + ), + GoRoute( + path: '/profile', + builder: (context, state) => ProfileScreen( + userId: '', + ), + ), + GoRoute( + path: '/manageuser', + builder: (context, state) => ManageUserRouter(), + ), ], ); + diff --git a/lib/main.dart b/lib/main.dart index df1ec5b7..5adba4ba 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -27,15 +27,28 @@ class MainApp extends StatelessWidget { @override Widget build(BuildContext context) { - return MaterialApp( - home: RegisterScreen(), - //home: ProfileScreen(), + return MaterialApp.router( debugShowCheckedModeBanner: false, + routerConfig: router, // Use the GoRouter instance from app_router.dart ); } } +// class MainApp extends StatelessWidget { +// const MainApp({super.key}); +// +// @override +// Widget build(BuildContext context) { +// return MaterialApp( +// home: RegisterScreen(), +// //home: ProfileScreen(), +// debugShowCheckedModeBanner: false, +// ); +// } +// } + + // class MainApp extends ConsumerWidget { // const MainApp({super.key}); // diff --git a/lib/presentation/Screens/confirm_password.dart b/lib/presentation/Screens/confirm_password.dart index 496daef0..41cb7c4c 100644 --- a/lib/presentation/Screens/confirm_password.dart +++ b/lib/presentation/Screens/confirm_password.dart @@ -71,7 +71,7 @@ class _ConfirmPasswordState extends State { } // Update password - await pb.collection('users').update( + await _pb.collection('users').update( userId, // User ID body: { 'password': newPassword, diff --git a/lib/presentation/Screens/profilepage.dart b/lib/presentation/Screens/profilepage.dart index f0ab349f..854e7844 100644 --- a/lib/presentation/Screens/profilepage.dart +++ b/lib/presentation/Screens/profilepage.dart @@ -4,6 +4,7 @@ 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'; @@ -515,12 +516,15 @@ class _ProfileScreenState extends State { Center( child: GestureDetector( onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => Changepassword()), - ); + context.go('/confirmpasswd'); }, + // { + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (context) => Changepassword(userId: '',)), + // ); + // }, child: Text( "Change Password", style: TextStyle( @@ -532,35 +536,38 @@ class _ProfileScreenState extends State { SizedBox(height: 20), ElevatedButton.icon( onPressed: () { - 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),)), - // ); - //} - //} + 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),)), + // // ); + // //} + // //} + // }, icon: Icon( Icons.save, color: Colors.white, diff --git a/lib/presentation/routes/auth_routes/login_route.dart b/lib/presentation/routes/auth_routes/login_route.dart index 3460183b..7c23535f 100644 --- a/lib/presentation/routes/auth_routes/login_route.dart +++ b/lib/presentation/routes/auth_routes/login_route.dart @@ -157,97 +157,105 @@ class LoginRoute extends HookConsumerWidget { final loginBtn = SizedBox( width: double.infinity, child: ElevatedButton( - onPressed: () 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: () 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}'); + onPressed: () { + context.go('/profile'); }, + // 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( shape: WidgetStatePropertyAll( RoundedRectangleBorder( diff --git a/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart b/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart index 70b585cd..b4ec7aa6 100644 --- a/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart +++ b/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart @@ -1,164 +1,167 @@ -import 'dart:math'; - -import 'package:external_repos/external_repos.dart'; +// import 'dart:math'; +// +// import 'package:external_repos/external_repos.dart'; +// import 'package:flutter/material.dart'; +// import 'package:flutter_hooks/flutter_hooks.dart'; +// import 'package:hooks_riverpod/hooks_riverpod.dart'; +// import 'package:uae_stat/config/my_theme.dart'; +// import 'package:uae_stat/domain/use_cases/language.dart'; +// import 'package:uae_stat/presentation/components/indicators/indicator_view.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/space.dart'; +// import 'package:uae_stat/presentation/components/themed_app_bar.dart'; +// import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; +// +// bool _wasHomeNeverBuiltThisSession = true; +// +// class HomeRoute extends HookConsumerWidget { +// const HomeRoute({super.key}); +// +// @override +// Widget build(BuildContext context, WidgetRef ref) { +// final bodyContent = Column( +// children: IndicatorTopic.values.map( +// (topic) { +// final scaledHeight = context.height * pow(50 / 880, 1.1); +// final titleText = Text( +// context.translate( +// topic.nameEN, +// topic.nameAR, +// ), +// textAlign: TextAlign.center, +// style: TextStyle( +// fontFamily: context.translate( +// 'Roboto', +// 'NotoKufi', +// ), +// color: Colors.white, +// fontSize: 20, +// fontWeight: FontWeight.w700, +// ), +// ); +// final title = Container( +// height: context.height > 880 ? null : scaledHeight, +// width: context.width, +// margin: const EdgeInsets.symmetric( +// horizontal: 24, +// ), +// padding: const EdgeInsets.symmetric( +// vertical: 5, +// ), +// decoration: BoxDecoration( +// color: MyTheme.topicColor(topic), +// borderRadius: BorderRadius.circular(100), +// ), +// child: context.height > 880 +// ? titleText +// : FittedBox( +// fit: BoxFit.scaleDown, +// child: titleText, +// ), +// ); +// final ids = IndicatorEnum.values.where( +// (id) => id.topic == topic, +// ); +// const horizontalGap = 16.0; +// const verticalGap = 9.0; +// final boxWidth = (context.width - (3 * horizontalGap)) / 2; +// final indicatorBoxes = ids +// .map( +// (id) => SizedBox( +// width: boxWidth, +// height: double.infinity, +// child: IndicatorView(id), +// ), +// ) +// .toList(); +// final scrollCtl = useScrollController(); +// if (_wasHomeNeverBuiltThisSession) { +// WidgetsBinding.instance.addPostFrameCallback( +// (_) async { +// final duration = kThemeAnimationDuration * 0.75; +// const distance = -30.0; +// scrollCtl.animateTo( +// distance, +// duration: duration, +// curve: Curves.fastOutSlowIn, +// ); +// await Future.delayed(duration * 2.5); +// scrollCtl.animateTo( +// distance, +// duration: duration, +// curve: Curves.fastOutSlowIn, +// ); +// _wasHomeNeverBuiltThisSession = false; +// }, +// ); +// } +// final content = SingleChildScrollView( +// controller: scrollCtl, +// padding: const EdgeInsets.symmetric( +// vertical: verticalGap, +// horizontal: horizontalGap, +// ), +// scrollDirection: Axis.horizontal, +// child: Column( +// children: [ +// Expanded( +// child: Row( +// children: [ +// indicatorBoxes[0], +// horizontalGap.horizontalSpace, +// indicatorBoxes[1], +// horizontalGap.horizontalSpace, +// indicatorBoxes[2], +// ], +// ), +// ), +// verticalGap.verticalSpace, +// Expanded( +// child: Row( +// children: [ +// indicatorBoxes[3], +// horizontalGap.horizontalSpace, +// indicatorBoxes[4], +// horizontalGap.horizontalSpace, +// indicatorBoxes.length == 6 +// ? indicatorBoxes[5] +// : boxWidth.horizontalSpace, +// ], +// ), +// ), +// ], +// ), +// ); +// return Expanded( +// child: Column( +// children: [ +// title, +// Expanded( +// child: content, +// ), +// ], +// ), +// ); +// }, +// ).toList(), +// ); +// return SafeArea( +// bottom: false, +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.stretch, +// children: [ +// const ThemedAppBar(), +// Expanded( +// child: bodyContent, +// ), +// ], +// ), +// ); +// } +// } import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:uae_stat/config/my_theme.dart'; -import 'package:uae_stat/domain/use_cases/language.dart'; -import 'package:uae_stat/presentation/components/indicators/indicator_view.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/space.dart'; -import 'package:uae_stat/presentation/components/themed_app_bar.dart'; - -bool _wasHomeNeverBuiltThisSession = true; - -class HomeRoute extends HookConsumerWidget { - const HomeRoute({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final bodyContent = Column( - children: IndicatorTopic.values.map( - (topic) { - final scaledHeight = context.height * pow(50 / 880, 1.1); - final titleText = Text( - context.translate( - topic.nameEN, - topic.nameAR, - ), - textAlign: TextAlign.center, - style: TextStyle( - fontFamily: context.translate( - 'Roboto', - 'NotoKufi', - ), - color: Colors.white, - fontSize: 20, - fontWeight: FontWeight.w700, - ), - ); - final title = Container( - height: context.height > 880 ? null : scaledHeight, - width: context.width, - margin: const EdgeInsets.symmetric( - horizontal: 24, - ), - padding: const EdgeInsets.symmetric( - vertical: 5, - ), - decoration: BoxDecoration( - color: MyTheme.topicColor(topic), - borderRadius: BorderRadius.circular(100), - ), - child: context.height > 880 - ? titleText - : FittedBox( - fit: BoxFit.scaleDown, - child: titleText, - ), - ); - final ids = IndicatorEnum.values.where( - (id) => id.topic == topic, - ); - const horizontalGap = 16.0; - const verticalGap = 9.0; - final boxWidth = (context.width - (3 * horizontalGap)) / 2; - final indicatorBoxes = ids - .map( - (id) => SizedBox( - width: boxWidth, - height: double.infinity, - child: IndicatorView(id), - ), - ) - .toList(); - final scrollCtl = useScrollController(); - if (_wasHomeNeverBuiltThisSession) { - WidgetsBinding.instance.addPostFrameCallback( - (_) async { - final duration = kThemeAnimationDuration * 0.75; - const distance = -30.0; - scrollCtl.animateTo( - distance, - duration: duration, - curve: Curves.fastOutSlowIn, - ); - await Future.delayed(duration * 2.5); - scrollCtl.animateTo( - distance, - duration: duration, - curve: Curves.fastOutSlowIn, - ); - _wasHomeNeverBuiltThisSession = false; - }, - ); - } - final content = SingleChildScrollView( - controller: scrollCtl, - padding: const EdgeInsets.symmetric( - vertical: verticalGap, - horizontal: horizontalGap, - ), - scrollDirection: Axis.horizontal, - child: Column( - children: [ - Expanded( - child: Row( - children: [ - indicatorBoxes[0], - horizontalGap.horizontalSpace, - indicatorBoxes[1], - horizontalGap.horizontalSpace, - indicatorBoxes[2], - ], - ), - ), - verticalGap.verticalSpace, - Expanded( - child: Row( - children: [ - indicatorBoxes[3], - horizontalGap.horizontalSpace, - indicatorBoxes[4], - horizontalGap.horizontalSpace, - indicatorBoxes.length == 6 - ? indicatorBoxes[5] - : boxWidth.horizontalSpace, - ], - ), - ), - ], - ), - ); - return Expanded( - child: Column( - children: [ - title, - Expanded( - child: content, - ), - ], - ), - ); - }, - ).toList(), - ); - return SafeArea( - bottom: false, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const ThemedAppBar(), - Expanded( - child: bodyContent, - ), - ], - ), - ); - } -} +import '../../drawer_routes/custom_drawer_routes.dart'; class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @@ -170,10 +173,8 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { @override Widget build(BuildContext context) { - return Scaffold( - //drawer: MyDrawer(), - //bottomNavigationBar: MyBottomNavBar(), - body: HomeRoute(), + return BaseScaffold( + body: Center(child : Text("Home Page")) ); } } diff --git a/lib/presentation/routes/drawer_routes/Drawer Items/feedback.dart b/lib/presentation/routes/drawer_routes/Drawer Items/feedback.dart new file mode 100644 index 00000000..9cbb8360 --- /dev/null +++ b/lib/presentation/routes/drawer_routes/Drawer Items/feedback.dart @@ -0,0 +1,775 @@ +import 'package:external_repos/external_repos.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_rating_bar/flutter_rating_bar.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:intl/intl.dart'; +import 'package:pocketbase/pocketbase.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:uae_stat/domain/use_cases/language.dart'; +import 'package:uae_stat/infrastructure/services/packages/go_router.dart'; +import 'package:uae_stat/presentation/components/my_drawer.dart'; +import 'package:mailer/mailer.dart'; +import 'package:mailer/smtp_server.dart'; +import 'package:uae_stat/presentation/components/space.dart'; +import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; +import '../../../../config/my_theme.dart'; +import '../../../../domain/use_cases/preferences_use_case.dart'; +import '../../../components/my_toggle.dart'; + + +class FeedbackForm extends StatefulWidget { + const FeedbackForm({super.key}); + + @override + _FeedbackFormState createState() => _FeedbackFormState(); +} + +class _FeedbackFormState extends State + with WidgetsBindingObserver { + final _pb = + PocketBase('https://pb.venbait.in'); // Initialize PocketBase client + // final _pb = + // PocketBase('http://127.0.0.1:8090'); // Initialize PocketBase client + + final TextEditingController _feedbackController = TextEditingController(); + + double _easeOfUseRating = 0; + double _qualityRating = 0; + double _designRating = 0; + double _redundancyRating = 0; + int? _selectedEmojiIndex; + bool _isFeedbackSubmitted = false; // Track if feedback was submitted + bool _isFeedbackFailed = false; // New flag for failed submission + bool _isSmileySelected = true; // Track if smiley is selected + dynamic configEmail; + + List> get _emojiOptions => [ + { + "icon": Icons.sentiment_very_dissatisfied, + "label": context.translate("Terrible", "رهيب"), // Translate here + "value": 1, + }, + { + "icon": Icons.sentiment_dissatisfied, + "label": context.translate("Bad", "سيء"), // Translate here + "value": 2, + }, + { + "icon": Icons.sentiment_neutral, + "label": context.translate("Okay", "تمام"), // Translate here + "value": 3, + }, + { + "icon": Icons.sentiment_satisfied, + "label": context.translate("Good", "جيد"), // Translate here + "value": 4, + }, + { + "icon": Icons.sentiment_very_satisfied, + "label": context.translate("Amazing", "مدهش"), // Translate here + "value": 5, + }, + ]; + + static const int _characterLimit = 1200; + final RegExp _allowedCharacters = RegExp(r'^[a-zA-Z0-9 .,!?-]*$'); + bool _hasError = false; + String _errorMessage = ''; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + _loadFeedbackText(); + _feedbackController.addListener(_handleTextChange); + fetchEmailConfiguration(); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state == AppLifecycleState.detached || + state == AppLifecycleState.paused) { + _removeFeedbackText(); + } + } + + // Detect when navigating to another page + @override + void didPushNext() { + // Called when a new page is pushed on top of FeedbackPage + _removeFeedbackText(); + } + + Future _saveFeedbackText( + int emojiIndex, String ratingKey, double ratingValue) async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setString('feedbackText', _feedbackController.text); + await prefs.setInt('selected_emoji_index', emojiIndex); + await prefs.setDouble(ratingKey, ratingValue); + print('one save'); + } + + Future _saveAllRatings() async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setDouble('ease_of_use_rating', _easeOfUseRating); + await prefs.setDouble('quality_rating', _qualityRating); + await prefs.setDouble('design_rating', _designRating); + await prefs.setDouble('redundancy_rating', _redundancyRating); + } + + Future _loadFeedbackText() async { + final prefs = await SharedPreferences.getInstance(); + + // Step 1: Load values from SharedPreferences into variables + final feedbackText = prefs.getString('feedbackText') ?? ''; + final savedIndex = prefs.getInt('selected_emoji_index'); + final easeOfUseRating = prefs.getDouble('ease_of_use_rating') ?? 0; + final qualityRating = prefs.getDouble('quality_rating') ?? 0; + final designRating = prefs.getDouble('design_rating') ?? 0; + final redundancyRating = prefs.getDouble('redundancy_rating') ?? 0; + + // Step 2: Populate fields with values without immediately clearing storage + setState(() { + _feedbackController.text = feedbackText; + if (savedIndex != null) { + _selectedEmojiIndex = savedIndex; + _isSmileySelected = true; // Indicating the user selected an emoji + } + _easeOfUseRating = easeOfUseRating; + _qualityRating = qualityRating; + _designRating = designRating; + _redundancyRating = redundancyRating; + }); + + // Step 3: Clear storage after a slight delay + Future.delayed(Duration(milliseconds: 50), () async { + await prefs.remove('feedbackText'); + await prefs.remove('selected_emoji_index'); + await prefs.remove('ease_of_use_rating'); + await prefs.remove('quality_rating'); + await prefs.remove('design_rating'); + await prefs.remove('redundancy_rating'); + }); + } + + // Remove feedback text from local storage + Future _removeFeedbackText() async { + final prefs = await SharedPreferences.getInstance(); + await prefs.remove('feedbackText'); + await prefs.remove('selected_emoji_index'); + await prefs.remove('ease_of_use_rating'); + await prefs.remove('quality_rating'); + await prefs.remove('design_rating'); + await prefs.remove('redundancy_rating'); + } + + void _handleTextChange() { + String currentText = _feedbackController.text; + + // Enforce character limit and truncate excess on paste + if (currentText.length > _characterLimit) { + _feedbackController.text = currentText.substring(0, _characterLimit); + _feedbackController.selection = TextSelection.fromPosition( + TextPosition(offset: _feedbackController.text.length), + ); + } + + // Validate characters + if (!_allowedCharacters.hasMatch(currentText)) { + setState(() { + _hasError = true; + _errorMessage = "Invalid Characters"; + }); + } else { + setState(() { + _hasError = false; + _errorMessage = ''; + }); + } + } + + @override + Widget build(BuildContext context) { + return BaseScaffold( + // appBar: AppBar( + // backgroundColor: const Color(0xFFf8f9ff), + // title: Text(context.translate('Feedback Form', 'نموذج الملاحظات')), + // actions: [ + // Align( + // alignment: AlignmentDirectional.topEnd, + // child: LangToggle( + // onSaveFeedback: _saveFeedbackText, + // onLoadFeedback: _loadFeedbackText, + // selectedEmojiIndex: _selectedEmojiIndex ?? 0, + // easeOfUseRating: _easeOfUseRating, + // qualityRating: _qualityRating, + // designRating: _designRating, + // redundancyRating: _redundancyRating, + // ), + // ), + // ], + // ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: SingleChildScrollView( + child: _isFeedbackSubmitted + ? _buildThankYouMessage() + : _isFeedbackFailed + ? _buildFailureMessage() + : _buildFeedbackForm(), + ), + ), + ); + } + + Widget _buildFeedbackForm() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + context.translate( + 'Do you have a suggestion or had any problem? Let us know.', + 'هل لديك اقتراح أو واجهت أي مشكلة؟ اسمحوا لنا أن نعرف.', + ), + style: const TextStyle( + fontSize: 18, + color: Color(0xFF898C81), + fontFamily: 'Roboto', + fontWeight: FontWeight.w700, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 20), + Text( + context.translate('How was your experience with us today?', + 'كيف كانت تجربتك معنا اليوم؟'), + style: const TextStyle( + fontSize: 18, + fontFamily: 'Roboto', + fontWeight: FontWeight.w400, + color: Color(0xFF898C81), + ), + ), + const SizedBox(height: 10), + Container( + decoration: BoxDecoration( + border: Border.all( + color: _isSmileySelected ? Colors.transparent : Colors.red, + width: 1.5, + ), + borderRadius: BorderRadius.circular(8), + ), + padding: const EdgeInsets.all(8), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: List.generate(_emojiOptions.length, (index) { + return _buildEmojiButton( + icon: _emojiOptions[index]["icon"], + label: _emojiOptions[index]["label"], + index: index, + ); + }), + ), + if (!_isSmileySelected) // Show the error message if no smiley is selected + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + context.translate( + 'Please rate your experience before submitting your feedback.', + 'يرجى تقييم تجربتك قبل تقديم ملاحظاتك.', + ), + style: TextStyle(color: Colors.red, fontSize: 14), + ), + ), + ], + ), + ), + const SizedBox(height: 30), + Text( + context.translate('How good did we do in these aspects?', + 'ما مدى جودة ما قمنا به في هذه الجوانب؟'), + style: const TextStyle( + fontSize: 18, + fontFamily: 'Roboto', + fontWeight: FontWeight.w400, + color: Color(0xFF898C81), + ), + ), + const SizedBox(height: 20), + _buildRatingRow( + context.translate('Ease of use', 'سهولة الاستخدام'), + _easeOfUseRating, + (rating) { + setState(() { + _easeOfUseRating = rating; + }); + }, + ), + _buildRatingRow( + context.translate('Quality', 'جودة'), + _qualityRating, + (rating) { + setState(() { + _qualityRating = rating; + }); + }, + ), + _buildRatingRow( + context.translate('Design', 'تصميم'), + _designRating, + (rating) { + setState(() { + _designRating = rating; + }); + }, + ), + _buildRatingRow( + context.translate('Redundant', 'متكرر'), + _redundancyRating, + (rating) { + setState(() { + _redundancyRating = rating; + }); + }, + ), + const SizedBox(height: 20), + TextField( + controller: _feedbackController, + minLines: 5, // Start with 5 lines + maxLines: null, // Allows the field to expand automatically + maxLength: _characterLimit, + decoration: InputDecoration( + hintText: context.translate( + 'Tell us how we can improve', 'أخبرنا كيف يمكننا تحسين'), + border: OutlineInputBorder( + borderSide: BorderSide( + color: _hasError ? Colors.red : Color(0xFF7296BE), + ), + ), + errorText: + _hasError ? _errorMessage : null, // Show error below field + ), + onChanged: (text) { + // Trigger re-validation and character limit on each change + _handleTextChange(); + }, + ), + const SizedBox(height: 20), + Center( + child: ElevatedButton( + onPressed: _submitFeedback, + style: ButtonStyle( + shape: WidgetStatePropertyAll( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + padding: const WidgetStatePropertyAll( + EdgeInsets.symmetric(vertical: 5.5), + ), + textStyle: WidgetStatePropertyAll( + TextStyle( + fontFamily: context.translate( + 'Roboto', + 'NotoKufi', + ), + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + backgroundColor: WidgetStatePropertyAll( + MyTheme.topicColor(IndicatorTopic.environment), + ), + foregroundColor: const WidgetStatePropertyAll( + Colors.white, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + context.translate( + 'Submit', + 'يُقدِّم', + ), + ), + 6.horizontalSpace, + const Icon(Icons.chevron_right_outlined), + ], + ), + ), + ), + ], + ); + } + + Widget _buildThankYouMessage() { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon(Icons.thumb_up, size: 80, color: Color(0xFF7DAFBC)), + const SizedBox(height: 20), + Text( + "Thank You", + style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 10), + const Text( + "For your valuable feedback, we truly appreciate your input!", + style: TextStyle(fontSize: 16), + textAlign: TextAlign.center, + ), + const SizedBox(height: 30), + ElevatedButton( + onPressed: () => context.go('/myhomepage'), + //context.go('/${context.language}/${BottomNavBarItem.home.routePath}'), + child: const Text("Go to Home"), + ), + ], + ), + ); + } + + // New widget for failure message + Widget _buildFailureMessage() { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon(Icons.error, size: 80, color: Colors.red), + const SizedBox(height: 20), + const Text("Failed to Share Feedback", + style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)), + const SizedBox(height: 10), + const Text("Your feedback could not be submitted. Please try again.", + style: TextStyle(fontSize: 16), textAlign: TextAlign.center), + const SizedBox(height: 30), + ElevatedButton( + onPressed: () { + setState(() { + _isFeedbackFailed = false; // Reset failure state + }); + }, + child: const Text("Retry"), + ), + ], + ), + ); + } + + Widget _buildEmojiButton({ + required IconData icon, + required String label, + required int index, + }) { + bool isSelected = _selectedEmojiIndex == index; + + return Column( + children: [ + IconButton( + icon: Icon(icon, size: 40), + color: isSelected ? Colors.green : Colors.amber, + onPressed: () { + setState(() { + _selectedEmojiIndex = index; + _isSmileySelected = true; // Hide error when a smiley is selected + }); + }, + ), + Text( + label, + style: TextStyle( + fontWeight: FontWeight.w400, + color: isSelected ? Colors.green : Color(0xFF000000), + ), + ), + ], + ); + } + + Widget _buildRatingRow( + String label, + double rating, + Function(double) onRatingUpdate, + ) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(label, + style: const TextStyle( + fontSize: 14, + fontFamily: 'Roboto', + fontWeight: FontWeight.w400, + color: Color(0xFF898C81), + )), + RatingBar.builder( + initialRating: rating, + minRating: 1, + direction: Axis.horizontal, + allowHalfRating: true, + itemCount: 5, + itemSize: 30, + itemBuilder: (context, _) => const Icon( + Icons.star, + color: Colors.amber, + ), + onRatingUpdate: onRatingUpdate, + unratedColor: Color(0xFF8E8E8E), + ), + ], + ); + } + + Future _submitFeedback() async { + if (_selectedEmojiIndex == null) { + setState(() { + _isSmileySelected = false; + }); + return; + } else { + setState(() { + _isSmileySelected = true; + }); + } + + if (_hasError) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text("Please correct the errors before submitting.")), + ); + return; + } + + final feedbackData = { + "ease_of_use": _easeOfUseRating, + "quality": _qualityRating, + "design": _designRating, + "redundancy": _redundancyRating, + "emoji_rating": _emojiOptions[_selectedEmojiIndex!] + ["label"], // Emoji rating + "feedback": _feedbackController.text, + }; + + try { + final adminAuth = await _pb.admins + .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com'); + final adminToken = adminAuth.token; + final response = await _pb + .collection('feedback') + .create(body: feedbackData, headers: {'Authorization': adminToken}); + if (response != null) { + final createdTime = response.created; + // Send email + + await sendFeedbackEmail(feedbackData, createdTime); + await _removeFeedbackText(); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text("Feedback submitted successfully!")), + ); + setState(() { + _isFeedbackSubmitted = true; + _isFeedbackFailed = false; + _resetFeedbackForm(); + }); + } + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Failed to submit feedback: $e")), + ); + setState(() { + _isFeedbackFailed = true; // Update state to show failure message + }); + } + } + + void _resetFeedbackForm() { + _easeOfUseRating = 0; + _qualityRating = 0; + _designRating = 0; + _redundancyRating = 0; + _selectedEmojiIndex = null; + _feedbackController.clear(); + } + + Future fetchEmailConfiguration() async { + try { + // Fetch data from the email_configuration collection + final response = + await _pb.collection('email_configuration').getFullList(); + + // Filter the data where the label is "Feedback" + final feedbackConfig = response.firstWhere( + (item) => + item.data['label'] == 'Feedback', // Accessing the 'data' property + ); + + // Check if a match is found + if (feedbackConfig != null) { + configEmail = feedbackConfig.data['email']; // Access the 'email' value + } else { + print('No feedback configuration found.'); + } + } catch (e) { + print('Error fetching email configuration: $e'); + } + } + + Future sendFeedbackEmail( + Map feedbackData, createdTime) async { + String username = 'emailapikey'; // Your SMTP username (API key) + String password = + 'PHtE6r0MFu66jTQp8BAFsP7sH5TwNd4v/+02KwBH5ItACvAES01Tot4okDawqhoiB/FEHfaey4Nvteyf5ePQJG28YW9OCWqyqK3sx/VYSPOZsbq6x00auVwYd0zUVY7pe9ds0yLTvNraNA=='; // Your SMTP password + + final smtpServer = SmtpServer('smtp.zeptomail.in', + port: 587, + username: username, + password: password, + ssl: false, // Use TLS + ignoreBadCertificate: + true); // Set to true if you're testing with a self-signed certificate + + // Check if additional feedback was provided + String additionalFeedback = feedbackData["feedback"]?.isNotEmpty == true + ? feedbackData["feedback"] + : "No additional feedback provided."; + + // Format separately for date and time + // Parse the string to DateTime + // Parse the input as UTC and convert to DateTime in UTC timezone + DateTime feedbackDateTime = DateTime.parse(createdTime).toUtc(); + + // Format date as DD-MM-YYYY in UTC + final String formattedDate = + DateFormat('dd-MM-yyyy').format(feedbackDateTime); + + // Format to the desired output: "dd.MM.yyyy HH:mm 'UTC'" + final DateFormat formatter = DateFormat("dd.MM.yyyy HH:mm 'UTC'"); + String formattedDateTime = formatter.format(feedbackDateTime); + + String email = configEmail; // Direct assignment + // Create the email message + final message = Message() + ..from = Address('bbone@venbait.in', 'FCSC') + ..recipients.add(email) // Set the recipient email + ..subject = 'UAE Stats Feedback' + ..text = ''' + Dear [App Owner/Admin], + + You have received new feedback from a user through the mobile application. + + User Details: + + 1. Name: Guest + 2. Date of Submission: $formattedDate + 3. Time of Submission: $formattedDateTime + + Feedback: + + 1. How was your experience with us today? Rating: ${feedbackData["emoji_rating"]} + 2. How did we perform in key areas? + 1. Ease of Use: ${feedbackData["ease_of_use"]} + 2. Quality: ${feedbackData["quality"]} + 3. Design: ${feedbackData["design"]} + 4. Redundancy: ${feedbackData["redundancy"]} + 3. Additional Feedback: + 1. $additionalFeedback + + Thank you, + The FCSC App Team + '''; + + try { + // Send the email + print("Message details:"); + print("From: ${message.from}"); + print("To: ${message.recipients}"); + print("Subject: ${message.subject}"); + print("Body: ${message.text}"); + final sendReport = await send(message, smtpServer); + print('Message sent: ' + sendReport.toString()); + } catch (e) { + print('Message not sent: $e'); + // Handle the error as needed + } + } + + @override + void dispose() { + _feedbackController.dispose(); + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } +} + +class LangToggle extends ConsumerWidget { + final Future Function( + int emojiIndex, String ratingKey, double ratingValue) onSaveFeedback; + final Future Function() onLoadFeedback; + final int selectedEmojiIndex; + final double easeOfUseRating; + final double qualityRating; + final double designRating; + final double redundancyRating; + + const LangToggle({ + required this.onSaveFeedback, + required this.onLoadFeedback, + required this.selectedEmojiIndex, + required this.easeOfUseRating, + required this.qualityRating, + required this.designRating, + required this.redundancyRating, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final isOn = context.language == LanguageLocale.enUS; + + return MyToggle( + isOn: isOn, + onTap: () async { + await onSaveFeedback( + selectedEmojiIndex, + 'ease_of_use_rating', + easeOfUseRating, + ); + await onSaveFeedback( + selectedEmojiIndex, + 'quality_rating', + qualityRating, + ); + await onSaveFeedback( + selectedEmojiIndex, + 'design_rating', + designRating, + ); + await onSaveFeedback( + selectedEmojiIndex, + 'redundancy_rating', + redundancyRating, + ); + + final languageAfter = isOn ? LanguageLocale.arAE : LanguageLocale.enUS; + final grs = GoRouterState.of(context); + grs.pathParameters['locale'] = languageAfter.toString(); + context.go(grs.pathWithParameters); + + ref.read(preferencesUseCaseProvider.notifier).updatePreferences( + (prefs) => prefs.copyWith(language: languageAfter), + ); + + await onLoadFeedback(); // Reload feedback text after toggling language + }, + knobTextWhenOn: 'ع', + knobTextWhenOff: 'EN', + pathColorWhenOn: Colors.grey.shade300, + pathColorWhenOff: Colors.grey.shade300, + ); + } +} diff --git a/lib/presentation/routes/drawer_routes/Drawer Items/manage_users.dart b/lib/presentation/routes/drawer_routes/Drawer Items/manage_users.dart new file mode 100644 index 00000000..3ac36dda --- /dev/null +++ b/lib/presentation/routes/drawer_routes/Drawer Items/manage_users.dart @@ -0,0 +1,254 @@ +import 'package:flutter/material.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/routes/drawer_routes/custom_drawer_routes.dart'; + +class ManageUserRouter extends StatefulWidget { + @override + State createState() => _ManageUserRouterState(); +} + +class _ManageUserRouterState extends State { + // Sample data for the table + final List userData = List.generate( + 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; + bool _isAscending = true; + + // Method to sort the data based on a column + void _sort(Comparable 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 statusOptions = ['Approved', 'Denied', 'Pending']; + + // Method to get status color + Color getStatusColor(String status) { + switch (status) { + case 'Approved': + return Colors.green; + case 'Denied': + return Colors.red; + case 'Pending': + return Colors.blue; + default: + return Colors.black; + } + } + + //Method to show a confirmation dialog when status is changed + Future _showConfirmationDialog(User user, String newStatus) async { + double myheight = MediaQuery.of(context).size.height; + return showDialog( + context: context, + barrierDismissible: false, // User must tap button to dismiss dialog + builder: (BuildContext context) { + return AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5.0), // Rounded corners + ), + contentPadding: EdgeInsets.zero, // Ensure no padding issues with close icon + content: Stack( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(height: myheight/30,), + Text( + 'Are you sure you want to change \n the status to $newStatus?', + style: TextStyle(fontSize: 15), + textAlign: TextAlign.center, + ), + ], + ), + ), + Positioned( + right: 0, + top: 0, + child: IconButton( + icon: Icon(Icons.close), + onPressed: () { + Navigator.of(context).pop(); // Close the dialog + }, + ), + ), + ], + ), + actions: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + TextButton( + onPressed: () { + Navigator.of(context) + .pop(); // Close dialog without changing status + }, + style: TextButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 10.0), // Adjust the radius as needed + ), + side: BorderSide( + color: Colors.blue, // Set the outline color + width: 2.0, // Set the border width + ), + ), + child: Text('No'), + ), + TextButton( + onPressed: () { + setState(() { + user.status = newStatus; // Change status + }); + Navigator.of(context).pop(); + }, + style: TextButton.styleFrom( + backgroundColor: Colors.blue, // Set background color + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(10.0), // Set text color + )), + child: Text( + 'Yes', + style: TextStyle(color: Colors.white), + ), + ), + ], + ) + ], + ); + + }, + ); + } + + @override + Widget build(BuildContext context) { + double myheight = MediaQuery.of(context).size.height; + return BaseScaffold( + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.only(left: 10.0, right: 10.0, top: 3.0), + child: Column( + children: [ + TextField( + decoration: InputDecoration( + prefixIcon: const Icon(Icons.search), + hintText: 'Search', + border: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(20)), + ), + ), + ), + SizedBox(height: myheight / 40), + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: DataTable( + sortColumnIndex: _sortColumnIndex, + sortAscending: _isAscending, + columns: [ + DataColumn( + label: const Text('User Name'), + onSort: (columnIndex, ascending) => _sort( + (user) => user.userName, columnIndex, ascending), + numeric: false, + ), + DataColumn( + label: const Text('Email ID'), + onSort: (columnIndex, ascending) => + _sort((user) => user.emailId, columnIndex, ascending), + numeric: false, + ), + DataColumn( + label: const Text('Registration Date'), + onSort: (columnIndex, ascending) => _sort( + (user) => user.registrationDate, + columnIndex, + ascending), + numeric: false, + ), + DataColumn( + label: const Text('Status'), + onSort: (columnIndex, ascending) => + _sort((user) => user.status, columnIndex, ascending), + numeric: false, + ), + ], + rows: userData.map((user) { + return DataRow( + cells: [ + DataCell(Text(user.userName)), + DataCell(Text(user.emailId)), + DataCell(Text(user.registrationDate)), + DataCell( + DropdownButton( + value: user.status, + items: statusOptions.map((status) { + return DropdownMenuItem( + value: status, + child: Text( + status, + style: + TextStyle(color: getStatusColor(status)), + ), + ); + }).toList(), + onChanged: (String? newStatus) { + if (newStatus != null) { + _showConfirmationDialog(user, + newStatus); // Show dialog for confirmation + } + }, + ), + ), + ], + ); + }).toList(), + ), + ), + ], + ), + ), + ), + ); + } +} + +// User data model +class User { + final String userName; + final String emailId; + final String registrationDate; + String status; + + User({ + required this.userName, + required this.emailId, + required this.registrationDate, + this.status = 'Pending', + }); +} diff --git a/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart b/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart new file mode 100644 index 00000000..1b0c7b64 --- /dev/null +++ b/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart @@ -0,0 +1,108 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + + +class BaseScaffold extends StatelessWidget { + final Widget body; + + const BaseScaffold({required this.body}); + + @override + Widget build(BuildContext context) { + // Get the current route to highlight the active item + String currentRoute = GoRouterState.of(context).matchedLocation; + + + + return Scaffold( + appBar: AppBar(title: Text(""),), + drawer: Drawer( + child: ListView( + children: [ + DrawerHeader( + decoration: BoxDecoration(color: Colors.blue), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Row(children: [ + + ],) + Divider(), + ListTile() + ], + ), + //child: Text('Drawer Header', style: TextStyle(color: Colors.white, fontSize: 24)), + ), + ListTile( + leading: Icon(Icons.person), + title: Text('Profile'), + onTap: () => context.go('/profile'), + ), + ListTile( + leading: Icon(Icons.note), + title: Text('Feedback'), + onTap: () => context.go('/feedback'), + ), + ListTile( + leading : Icon(Icons.percent), + title: Text('Manage User'), + onTap: () => context.go('/manageuser'), + ), + + ], + ), + ), + bottomNavigationBar: BottomNavigationBar( + currentIndex: _getSelectedIndex(currentRoute), + onTap: (index) => _onItemTapped(context, index), + items: [ + BottomNavigationBarItem(icon: Icon(Icons.home,color: Colors.black,), label: 'Home',), + BottomNavigationBarItem(icon: Icon(Icons.info,color: Colors.black,), label: 'About',), + BottomNavigationBarItem(icon: Icon(Icons.settings,color: Colors.black,), label: 'Settings'), + BottomNavigationBarItem(icon: Icon(Icons.person,color: Colors.black,), label: 'Profile'), + ], + selectedItemColor: Colors.blue, + unselectedItemColor: Colors.grey, + showUnselectedLabels: true, + ), + body: body, + + ); + } + + //Map the current route to the selected index + int _getSelectedIndex(String route) { + switch (route) { + case '/': + return 0; + case '/about': + return 1; + case '/settings': + return 2; + case '/profile': + return 3; + default: + return 0; + } + } + + //Handle navigation when an item is tapped + void _onItemTapped(BuildContext context, int index) { + switch (index) { + case 0: + context.go('/'); + break; + case 1: + context.go('/about'); + break; + case 2: + context.go('/settings'); + break; + case 3: + context.go('/profile'); + break; + } + } +} + +