import 'dart:convert'; 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:http/http.dart' as http; import 'package:uae_stat/config/theme/service.dart'; import 'package:uae_stat/config/theme/theme_provider.dart'; import 'package:pocketbase/pocketbase.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:uae_stat/config/api_config.dart'; import 'package:uae_stat/config/connectivity_provider.dart'; import 'package:uae_stat/config/toggle_lang_service.dart'; import 'package:uae_stat/domain/use_cases/language.dart'; import 'package:uae_stat/infrastructure/services/img_asset_paths/feedback_asset_path.dart'; import 'package:uae_stat/infrastructure/services/packages/go_router.dart'; import 'package:uae_stat/infrastructure/services/pocketbase_service.dart'; import 'package:uae_stat/l10n/app_localizations.dart'; import 'package:uae_stat/presentation/components/indicators/locale_provider.dart'; import 'package:uae_stat/presentation/components/space.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; import '../../../../domain/use_cases/preferences_use_case.dart'; import '../../../components/my_toggle.dart'; class FeedbackForm extends ConsumerStatefulWidget { const FeedbackForm({super.key}); @override ConsumerState createState() => _FeedbackFormState(); } class _FeedbackFormState extends ConsumerState with WidgetsBindingObserver { final _pb = PocketBase(apiUrl); // Initialize PocketBase client bool isLoading = false; // final _pb = // PocketBase('http://127.0.0.1:8090'); // Initialize PocketBase client final TextEditingController _feedbackController = TextEditingController(); List homePageData = []; List filteredData = []; 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; dynamic userId; List> get _emojiOptions => [ { // "icon": Icons.emoji_emotions_rounded, 'icon': Image.asset( FeedBackAssetPath.disappointedfb, width: 25, height: 25, ), "label": context.translate("Terrible", "سئ جدا"), // Translate here "value": 1, }, { // "icon": Icons.sentiment_dissatisfied, 'icon': Image.asset( FeedBackAssetPath.frowningfb, width: 25, height: 25, ), "label": context.translate("Bad", "سيء"), // Translate here "value": 2, }, { 'icon': Image.asset( FeedBackAssetPath.neutralfb, width: 25, height: 25, ), "label": context.translate("Okay", "مقبول"), // Translate here "value": 3, }, { 'icon': Image.asset( FeedBackAssetPath.smilingfb, width: 25, height: 25, ), "label": context.translate("Good", "جيد"), // Translate here "value": 4, }, { // "icon": Icons.sentiment_very_satisfied, 'icon': Image.asset( FeedBackAssetPath.smilingFacefb, width: 25, height: 25, ), "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 = ''; late UserService _userService; late ThemeMode currentTheme; @override void initState() { super.initState(); _userService = UserService(); final locale = ref.read(localeProvider); currentTheme = ref.read(themeProvider); WidgetsBinding.instance.addPostFrameCallback((_) { final isDark = currentTheme == ThemeMode.dark || (currentTheme == ThemeMode.system && MediaQuery.of(context).platformBrightness == Brightness.dark); final themeMode = (isDark ? 'dark' : 'light'); fetchData(locale?.languageCode ?? 'en', themeMode); }); WidgetsBinding.instance.addObserver(this); _loadFeedbackText(); _feedbackController.addListener(_handleTextChange); // fetchEmailConfiguration(); checkUserId(); } Future fetchData(locale, themeMode) async { const baseUrl = '${apiUrl}api/getUAENumbersData'; try { final response = await http.get( Uri.parse(baseUrl + '?language=$locale&color_mode=$themeMode'), headers: {'APP_SIGNATURE': 'fcsc.gov.ae.X7pL9qZm2A'}); if (response.statusCode == 200) { setState(() { homePageData = (json.decode(response.body) as List) .map((e) => Map.from(e)) .toList(); filteredData = List.from(homePageData); isLoading = false; }); } else { throw Exception('Failed to load data'); } } catch (e) { setState(() { isLoading = false; }); } } Future getUserId() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString('userId'); // Retrieve the userId } Future checkUserId() async { userId = await getUserId(); if (userId != null && userId.isNotEmpty) { } else { // Handle case where userId is not available } } @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); } 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) { // final uri = Uri.parse(GoRouterState.of(context).uri.toString()); // final userName = uri.queryParameters['userName'] ?? 'Guest User'; ref.listen>(connectivityProvider, (previous, hasInternet) { if (hasInternet.value == false) { context.push('/internetcheck'); } }); // ref.listen(localeProvider, (previous, next) async { // final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null // await _userService.updateLanguage(localeCode); // }); ref.listen(localeProvider, (previous, next) async { final localeCode = next?.languageCode ?? 'en'; setState(() { isLoading = true; }); await _userService.updateLanguage(localeCode, ref); final currentTheme = ref.read(themeProvider); final isDark = currentTheme == ThemeMode.dark || (currentTheme == ThemeMode.system && MediaQuery.of(context).platformBrightness == Brightness.dark); final themeString = isDark ? 'dark' : 'light'; fetchData(localeCode, themeString); }); ref.listen(themeProvider, (previous, next) async { final localeCode = ref.watch(localeProvider)?.languageCode ?? 'en'; // Default to 'en' if null setState(() { isLoading = true; }); final currentTheme = ref.read(themeProvider); final isDark = currentTheme == ThemeMode.dark || (currentTheme == ThemeMode.system && MediaQuery.of(context).platformBrightness == Brightness.dark); final themeString = isDark ? 'dark' : 'light'; fetchData(localeCode, themeString); }); final themeMode = ref.read(themeProvider); final isDarkTheme = themeMode == ThemeMode.dark || (themeMode == ThemeMode.system && MediaQuery.of(context).platformBrightness == Brightness.dark); final userName = GoRouterState.of(context).extra as String? ?? 'Guest User'; return PopScope( canPop: false, // Allow back navigation only if not login screen onPopInvokedWithResult: (didPop, result) { if (didPop) return; context.go('/myhomepage'); // Show exit confirmation dialog }, child: BaseScaffold( backgroundColor: isDarkTheme ? Colors.black : Colors.white, appbarColor: isDarkTheme ? Colors.black : Colors.white, // backgroundColor: isDarkTheme ? Color(0xFF333333) : Color(0xFF414042), // appbarColor: isDarkTheme ? Color(0xFF333333) : Color(0xFF414042), // title: Text("Feedback"), title: Text( AppLocalizations.of(context)!.feedback_title, style: TextStyle( fontSize: 24, fontFamily: context.translate( 'Roboto', 'NotoKufi', ), fontWeight: FontWeight.w500), ), // appBar: AppBar( // backgroundColor: const Color(0xFFf8f9ff), // title: Text(AppLocalizations.of(context)!.feedback_title), // actions: [ // Align( // alignment: AlignmentDirectional.topEnd, // child: LangToggle( // onSaveFeedback: _saveFeedbackText, // onLoadFeedback: _loadFeedbackText, // selectedEmojiIndex: _selectedEmojiIndex ?? 0, // easeOfUseRating: _easeOfUseRating, // qualityRating: _qualityRating, // designRating: _designRating, // redundancyRating: _redundancyRating, // ), // ), // ], // ), body: isLoading ? Container( // color: Color(0x98FFFCE5), // Semi-transparent background color: isDarkTheme ? Color(0xFF000000) : Colors.white, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( margin: EdgeInsets.symmetric( horizontal: 40), // Left & Right space child: LinearProgressIndicator( minHeight: 5, // Adjust thickness backgroundColor: Colors.grey[100], // Optional: Background color valueColor: AlwaysStoppedAnimation( Color(0xFFAA8E83)), // Loader color ), ), ], ), ) : Padding( padding: const EdgeInsets.only( top: 8.0, bottom: 16.0, left: 23.0, right: 23.0), child: SingleChildScrollView( child: _isFeedbackSubmitted ? _buildThankYouMessage(userName, isDarkTheme) : _isFeedbackFailed ? _buildFailureMessage(isDarkTheme) : _buildFeedbackForm(isDarkTheme), ), ), )); } Widget _buildFeedbackForm(bool isDarkTheme) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( context.translate( 'Do you have a suggestion or had any problem? Let us know.', 'هل لديك اقتراح أو واجهت أي مشكلة؟ أخبرنا', ), style: TextStyle( fontSize: 18, color: isDarkTheme ? Colors.white : Color(0xFF898C81), fontFamily: context.translate( 'Roboto', 'NotoKufi', ), fontWeight: FontWeight.w700, ), textAlign: TextAlign.center, ), const SizedBox(height: 20), Center( child: FittedBox( fit: BoxFit .scaleDown, // This will make the text smaller to fit within the available space child: Text( context.translate('How was your experience with us today?', 'كيف كانت تجربتك معنا اليوم؟'), style: TextStyle( fontSize: 18, fontFamily: context.translate( 'Roboto', 'NotoKufi', ), fontWeight: FontWeight.w400, color: isDarkTheme ? Colors.white : Color(0xff898C81), // color: Color(0xff898C81), ), ), ), ), const SizedBox(height: 15), Container( width: double.infinity, decoration: BoxDecoration( border: Border.all( color: _isSmileySelected ? Colors.transparent : Colors.transparent, width: 1.5, ), borderRadius: BorderRadius.circular(8), ), padding: const EdgeInsets.all(0.0), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: List.generate(_emojiOptions.length, (index) { return _buildEmojiButton( icon: _emojiOptions[index]["icon"], // icon: _emojiOptions[index]["icon"], label: _emojiOptions[index]["label"], index: index, isDarkTheme: isDarkTheme, ); }), ), 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: Color(0xFFD83731), fontSize: 14, fontFamily: context.translate( 'Roboto', 'NotoKufi', ), ), ), ), ], ), ), const SizedBox(height: 15), FittedBox( fit: BoxFit .scaleDown, // This will make the text smaller to fit within the available space child: Text( context.translate('How good did we do in these aspects?', 'ما مدى جودة أدائنا في هذه الجوانب؟'), style: TextStyle( fontSize: 18, fontFamily: context.translate( 'Roboto', 'NotoKufi', ), fontWeight: FontWeight.w400, // color: Color(0xFF898C81), color: isDarkTheme ? Colors.white : Color(0xff898C81), ), )), const SizedBox(height: 15), _buildRatingRow(context.translate('Ease of use', 'سهولة الاستخدام'), _easeOfUseRating, (rating) { setState(() { _easeOfUseRating = rating; }); }, isDarkTheme), _buildRatingRow( context.translate('Quality', 'الجودة'), _qualityRating, (rating) { setState(() { _qualityRating = rating; }); }, isDarkTheme, ), _buildRatingRow( context.translate('Design', 'التصميم'), _designRating, (rating) { setState(() { _designRating = rating; }); }, isDarkTheme, ), _buildRatingRow( context.translate('Redundant', 'مكرر'), _redundancyRating, (rating) { setState(() { _redundancyRating = rating; }); }, isDarkTheme, ), const SizedBox(height: 20), TextField( controller: _feedbackController, minLines: 5, // Start with 5 lines maxLines: null, // Allows the field to expand automatically maxLength: _characterLimit, style: TextStyle( color: isDarkTheme ? Colors.white : Color(0xFF898C81), fontFamily: context.translate( 'Roboto', 'NotoKufi', ), ), decoration: InputDecoration( hintText: context.translate( 'Tell us how we can improve', 'أخبرنا كيف يمكننا التحسين'), hintStyle: TextStyle( color: isDarkTheme ? Colors.white : Color(0xFF898C81), fontFamily: context.translate( 'Roboto', 'NotoKufi', ), ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(10.0), borderSide: BorderSide(color: Color(0xFFB68A34), width: 2), ), // Border when the TextField is enabled but not focused enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10.0), borderSide: BorderSide(color: Color(0xFFB68A34), width: 2), ), // Border when there is an error errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10.0), borderSide: BorderSide(color: Color(0xFFD83731), width: 2), ), // Border when focused and there is an error focusedErrorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10.0), borderSide: BorderSide(color: Color(0xFFD83731), width: 2), ), errorText: _hasError ? _errorMessage : null, // Show error below field errorStyle: TextStyle( color: Color(0xFFD83731), ), ), onChanged: (text) { // Trigger re-validation and character limit on each change _handleTextChange(); }, ), const SizedBox(height: 15), 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(Color(0xFFB68A34)), // backgroundColor: WidgetStatePropertyAll( // MyTheme.topicColor(IndicatorTopic.environment), // ), foregroundColor: const WidgetStatePropertyAll( Colors.white, ), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( context.translate( 'Submit', 'إرسال', ), style: TextStyle( fontFamily: context.translate( 'Roboto', 'NotoKufi', ), color: Colors.white, ), ), 6.horizontalSpace, Icon( Icons.chevron_right_outlined, // color: Colors.white, color: Colors.white, ), ], ), ), ), ], ); } Widget _buildThankYouMessage(String userName, bool isDarkTheme) { double screenHeight = MediaQuery.of(context).size.height; return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox(height: screenHeight / 5), Container( padding: EdgeInsets.all(20), decoration: BoxDecoration( color: Color(0xFFF9F9F9), // Circle background color shape: BoxShape.circle, // Make it a circle boxShadow: [ BoxShadow( color: Color.fromRGBO(0, 0, 0, 0.2), blurRadius: 5, spreadRadius: 2, offset: Offset(0, 3), // Shadow position ), ], ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset( FeedBackAssetPath.thumbfb, width: 40, height: 40, ) // Icon( // Icons.thumb, // size: 80, // color: Color(0xFF7DAFBC), // Icon color // ), // const SizedBox(height: 20), ], ), ), // Icon(Icons.thumb_up, size: 80, color: Color(0xFF7DAFBC)), const SizedBox(height: 20), Text( '${AppLocalizations.of(context)!.thankYou} $userName !', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, fontFamily: context.translate( 'Roboto', 'NotoKufi', ), ), ), const SizedBox(height: 15), Text( AppLocalizations.of(context)!.feedback_message, style: TextStyle( fontSize: 16, color: isDarkTheme ? Colors.white : Color(0xFF898C81), fontFamily: context.translate( 'Roboto', 'NotoKufi', ), ), 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(bool isDarkTheme) { double screenHeight = MediaQuery.of(context).size.height; double screenWidth = MediaQuery.of(context).size.width; return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox(height: screenHeight / 5), Container( padding: EdgeInsets.all(20), decoration: BoxDecoration( color: Color(0xFFF9F9F9), // Circle background color shape: BoxShape.circle, // Make it a circle boxShadow: [ BoxShadow( color: Color.fromRGBO(0, 0, 0, 0.2), blurRadius: 5, spreadRadius: 2, offset: Offset(0, 3), // Shadow position ), ], ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset( FeedBackAssetPath.errorfb, width: 40, height: 40, ), ])), // Icon(Icons.error, size: 80, color: Colors.red), const SizedBox(height: 20), Text( AppLocalizations.of(context)!.failed_to_sharefeedback, style: TextStyle( fontFamily: context.translate( 'Roboto', 'NotoKufi', ), fontSize: 24, fontWeight: FontWeight.bold, color: isDarkTheme ? Colors.white : Color(0xFF414042)), ), const SizedBox(height: 10), Text(AppLocalizations.of(context)!.feedback_failed_msg, style: TextStyle( fontSize: 16, color: isDarkTheme ? Colors.white : Color(0xFF898C81), fontFamily: context.translate( 'Roboto', 'NotoKufi', ), ), 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 Widget icon, required String label, required int index, required bool isDarkTheme, }) { bool isSelected = _selectedEmojiIndex == index; return GestureDetector( onTap: () { setState(() { _selectedEmojiIndex = index; _isSmileySelected = true; // Hide error when a smiley is selected }); }, child: Container( padding: EdgeInsets.symmetric(vertical: 8, horizontal: 8), decoration: BoxDecoration( border: Border.all( color: isSelected ? Color(0xFF8E8E8E) : Colors.transparent, width: 2, ), borderRadius: BorderRadius.circular(12), // Rounded corners ), child: Column( mainAxisSize: MainAxisSize.min, children: [ icon, // Assuming `icon` is an Icon widget SizedBox(height: 3), FittedBox( fit: BoxFit .scaleDown, // This will make the text smaller to fit within the available space child: Text( label, style: TextStyle( fontFamily: context.translate( 'Roboto', 'NotoKufi', ), fontWeight: FontWeight.w400, fontSize: 14, color: isDarkTheme ? Colors.white : Colors.black, // ❌ Wrong: black in dark theme? // color: Colors.black, // color: isSelected ? Colors.green : Colors.black, ), ), ), ], ), ), ); // return Column( // children: [ // // IconButton( // // icon: Icon(icon, size: 40), // icon: icon, // 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, bool isDarkTheme, ) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(label, style: TextStyle( fontSize: 14, fontFamily: context.translate( 'Roboto', 'NotoKufi', ), fontWeight: FontWeight.w400, // color: Color(0xFF898C81), color: isDarkTheme ? Colors.white : Color(0xff898C81), )), RatingBar.builder( initialRating: rating, minRating: 0, direction: Axis.horizontal, allowHalfRating: true, itemCount: 5, itemSize: 28, itemBuilder: (context, index) => Icon( index < rating ? Icons.star_rounded : Icons.star_border_sharp, color: Colors.amber, size: 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( SnackBar( content: Text( AppLocalizations.of(context)!.formError, style: TextStyle( fontFamily: context.translate( 'Roboto', 'NotoKufi', )), // "Please correct the errors before submitting." )), ); return; } setState(() { isLoading = true; }); final feedbackData = { "userId": userId, "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 auth = ref.watch(authProvider); // final adminAuth = await pb.admins.authWithPassword( // auth.email, // auth.password, // ); // // final adminToken = adminAuth.token; final themeService = ThemeBaseService(); // final adminToken = await themeService.getAdminToken( // email: auth.email, // password: auth.password, // ); final response = await _pb.collection('feedback').create( body: feedbackData, headers: {'APP_SIGNATURE': 'fcsc.gov.ae.X7pL9qZm2A'}, ); if (response != null) { final createdTime = response.created; // Send email // await sendFeedbackEmail(feedbackData, createdTime); await _removeFeedbackText(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( AppLocalizations.of(context)!.feedback_submitted, style: TextStyle( fontFamily: context.translate( 'Roboto', 'NotoKufi', )), ), ), ); setState(() { _isFeedbackSubmitted = true; _isFeedbackFailed = false; _resetFeedbackForm(); isLoading = false; }); } } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( // "Failed to submit feedback: $e" AppLocalizations.of(context)!.feedback_failed(e.toString()), style: TextStyle( fontFamily: context.translate( 'Roboto', 'NotoKufi', )), )), ); 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: Color(0xFFD9D9D9), pathColorWhenOff: Color(0xFFD9D9D9), knobTextStyleWhenOn: TextStyle( color: Color(0xFF010101), fontWeight: FontWeight.bold, ), knobTextStyleWhenOff: TextStyle( color: Color(0xFF010101), fontWeight: FontWeight.bold, ), ); } }