diff --git a/android/app/build.gradle b/android/app/build.gradle index fb773bcb..d472874b 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -20,12 +20,12 @@ if (project.hasProperty('google-services.json')) { def flutterVersionCode = localProperties.getProperty("flutter.versionCode") if (flutterVersionCode == null) { - flutterVersionCode = "36" + flutterVersionCode = "37" } def flutterVersionName = localProperties.getProperty("flutter.versionName") if (flutterVersionName == null) { - flutterVersionName = "1.0.35" + flutterVersionName = "1.0.36" } def keystorePropertiesFile = rootProject.file("key.properties") @@ -72,6 +72,7 @@ android { // Signing with the debug keys for now, so `flutter run --release` works. signingConfig signingConfigs.release minifyEnabled true // Enable code shrinking for smaller APKs + shrinkResources true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } diff --git a/lib/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/uae_numbers.dart b/lib/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/uae_numbers.dart index 368cc4bf..277fc97a 100644 --- a/lib/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/uae_numbers.dart +++ b/lib/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/uae_numbers.dart @@ -182,12 +182,38 @@ class _UaenumberWidgetState extends ConsumerState { ref.listen(localeProvider, (previous, next) { final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null + setState(() { + isLoading = true; + }); fetchData(localeCode); }); double myheight = MediaQuery.of(context).size.height; double mywidth = MediaQuery.of(context).size.width; + if (isLoading) { + return Container( + color: Color(0x98FFFCE5), // Semi-transparent background + 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 + ), + ), + ], + ), + ); + } else if (filteredData.isEmpty) { + return Center(child: Text("No data available")); + } + return Padding( padding: const EdgeInsets.only(left: 16.0, right: 16.0, top: 5.0, bottom: 5.0), diff --git a/lib/presentation/Screens/auth_verification/create_new_pw.dart b/lib/presentation/Screens/auth_verification/create_new_pw.dart index 2dce15c5..5e1b0d06 100644 --- a/lib/presentation/Screens/auth_verification/create_new_pw.dart +++ b/lib/presentation/Screens/auth_verification/create_new_pw.dart @@ -3,10 +3,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.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/my_router.dart'; import 'package:uae_stat/domain/use_cases/language.dart'; +import 'package:uae_stat/infrastructure/data/p_auth_repo.dart'; import 'package:uae_stat/infrastructure/services/img_asset_paths/banner_asset_path.dart'; import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/misc_icon_asset_path.dart'; import 'package:uae_stat/presentation/Screens/profilepage.dart'; @@ -34,6 +36,7 @@ class CreateNewPw extends ConsumerStatefulWidget { class _CreateNewPwState extends ConsumerState { final _pb = PocketBase(apiUrl); + bool isLoading = false; final _formKey = GlobalKey(); bool _obscureOldPassword = true; bool _obscureNewPassword = true; @@ -139,6 +142,9 @@ class _CreateNewPwState extends ConsumerState { Future updatePassword( String userId, String oldPassword, String newPassword) async { try { + setState(() { + isLoading = true; + }); // Authenticate as admin final adminAuth = await _pb.admins .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com'); @@ -164,6 +170,11 @@ class _CreateNewPwState extends ConsumerState { headers: headers, ); + setState(() { + isLoading = false; + _isPasswordUpdated = true; + }); + ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( @@ -172,9 +183,7 @@ class _CreateNewPwState extends ConsumerState { ), ); - setState(() { - _isPasswordUpdated = true; // Toggle UI on success - }); + print('_isPasswordUpdated $_isPasswordUpdated'); // Navigator.push( // context, @@ -183,6 +192,7 @@ class _CreateNewPwState extends ConsumerState { // ); } else { setState(() { + isLoading = false; _formKey.currentState?.validate(); }); ScaffoldMessenger.of(context).showSnackBar( @@ -195,6 +205,9 @@ class _CreateNewPwState extends ConsumerState { return; } } catch (e) { + setState(() { + isLoading = false; + }); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Failed to update password: $e'), @@ -204,6 +217,15 @@ class _CreateNewPwState extends ConsumerState { } } + final PAuthRepo _authRepo = PAuthRepo(); + + Future logout(BuildContext context) async { + final prefs = await SharedPreferences.getInstance(); + await _authRepo.logout(); + prefs.clear(); + context.go('/login'); // Redirect to login after logout + } + @override Widget build(BuildContext context) { double screenHeight = MediaQuery.of(context).size.height; @@ -216,15 +238,36 @@ class _CreateNewPwState extends ConsumerState { }); return Scaffold( - backgroundColor: Colors.white, - body: SingleChildScrollView( - child: SafeArea( - child: _isPasswordUpdated - ? _buildSuccessContent(screenHeight, screenWidth) - : _buildPasswordForm(ref, screenHeight, screenWidth), - ), - ), - ); + backgroundColor: Colors.white, + body: Stack(children: [ + SingleChildScrollView( + child: SafeArea( + child: _isPasswordUpdated + ? _buildSuccessContent(screenHeight, screenWidth) + : _buildPasswordForm(ref, screenHeight, screenWidth), + ), + ), + if (isLoading) + Container( + color: Color(0x98FFFCE5), // Semi-transparent background + 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 + ), + ), + ], + ), + ) + ])); } Widget _buildPasswordForm( @@ -626,8 +669,9 @@ class _CreateNewPwState extends ConsumerState { } Widget _buildSuccessContent(double screenHeight, double screenWidth) { + print('IN'); return Padding( - padding: const EdgeInsets.all(20.0), + padding: EdgeInsets.all(20.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, @@ -647,16 +691,14 @@ class _CreateNewPwState extends ConsumerState { ), SizedBox(height: 20), ElevatedButton( - onPressed: () async { - context.go('/login'); - }, + onPressed: () => logout(context), style: ButtonStyle( shape: WidgetStatePropertyAll( RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), - padding: const WidgetStatePropertyAll( + padding: WidgetStatePropertyAll( EdgeInsets.symmetric(vertical: 10.5), ), textStyle: WidgetStatePropertyAll( @@ -675,7 +717,7 @@ class _CreateNewPwState extends ConsumerState { // surfaceTintColor: MaterialStatePropertyAll( // MyTheme.economy[800], // ), - foregroundColor: const WidgetStatePropertyAll( + foregroundColor: WidgetStatePropertyAll( Colors.white, ), ), @@ -697,12 +739,12 @@ class _CreateNewPwState extends ConsumerState { ), ), 6.horizontalSpace, - const Icon(Icons.chevron_right_outlined, color: Colors.white), + Icon(Icons.chevron_right_outlined, color: Colors.white), ], ), ), SizedBox(height: screenHeight / 2.2), - Spacer(), + // Spacer(), Center( // child: Container( // height: 40, diff --git a/lib/presentation/Screens/auth_verification/forgot_password.dart b/lib/presentation/Screens/auth_verification/forgot_password.dart new file mode 100644 index 00000000..42745bb5 --- /dev/null +++ b/lib/presentation/Screens/auth_verification/forgot_password.dart @@ -0,0 +1,632 @@ +import 'package:external_repos/external_repos.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.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/my_router.dart'; +import 'package:uae_stat/domain/use_cases/language.dart'; +import 'package:uae_stat/infrastructure/data/p_auth_repo.dart'; +import 'package:uae_stat/infrastructure/services/img_asset_paths/banner_asset_path.dart'; +import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/misc_icon_asset_path.dart'; +import 'package:uae_stat/presentation/Screens/profilepage.dart'; +import 'package:uae_stat/presentation/components/indicators/locale_provider.dart'; +import 'package:uae_stat/presentation/components/my_toggle.dart'; +import 'package:uae_stat/presentation/components/space.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +import 'package:uae_stat/config/my_theme.dart'; + +class ForgotPassword extends ConsumerStatefulWidget { + final String userId; + + const ForgotPassword({Key? key, required this.userId}); + + @override + ConsumerState createState() => _ForgotPasswordState(); +} + +class _ForgotPasswordState extends ConsumerState { + final _pb = PocketBase(apiUrl); + bool isLoading = false; + final _formKey = GlobalKey(); + bool _obscureNewPassword = true; + bool _obscureConfirmPassword = true; + bool _isPasswordUpdated = false; // Variable to toggle UI + bool hasValidated = false; + String? _oldPassword; + String? _newPassword; + String? _confirmPassword; + + final fcscBanner = Image.asset( + BannerAssetPath.fcsc, + height: 40, + ); + + String? _validateNewPassword(String? value) { + // Define the regular expression for allowed characters + final regex = RegExp( + r'^[A-Za-z0-9~!@#$%^&*()_+=[\]{}|;:,.<>?/-àèìòùáéíóúÀÈÌÒÙÁÉÍÓÚ]*$'); + + // Define regular expressions for password complexity requirements + final hasUppercase = RegExp(r'[A-Z]'); + final hasLowercase = RegExp(r'[a-z]'); + final hasDigit = RegExp(r'\d'); + final hasSpecialCharacter = RegExp(r'[~!@#$%^&*()_+=[\]{}|;:,.<>?/-]'); + + if (value == null || value.isEmpty) { + return AppLocalizations.of(context)!.new_password_required; + } else if (value.length < 8 || value.length > 64) { + return AppLocalizations.of(context)!.password_between_8_to_40; + } else if (value == _oldPassword) { + // return 'New password must not be the same as the old password'; + return AppLocalizations.of(context)!.new_password_not_same_as_old; + } + + // Check the regular expression for allowed characters + if (!regex.hasMatch(value)) { + return AppLocalizations.of(context)!.password_invalid; + } + + // Track missing constraints + List missingConstraints = []; + + if (!hasUppercase.hasMatch(value)) { + missingConstraints.add(context.translate('uppercase letter', 'حرف كبير')); + } + if (!hasLowercase.hasMatch(value)) { + missingConstraints.add(context.translate('lowercase letter', 'حرف صغير')); + } + if (!hasDigit.hasMatch(value)) { + missingConstraints.add(context.translate('numeric digit', 'رقم')); + } + if (!hasSpecialCharacter.hasMatch(value)) { + missingConstraints.add(context.translate('special character', 'رمز خاص')); + } + + // If there are missing constraints, return a consolidated message + if (missingConstraints.isNotEmpty) { + return context.translate('At least one ${missingConstraints.join(', ')}', + '${missingConstraints.join(', ')}على الأقل واحد '); + } + + _newPassword = value; // Store for validation + return null; + } + + String? _validateConfirmPassword(String? value) { + if (value == null || value.isEmpty) { + return AppLocalizations.of(context)!.confirm_new_password; + } else if (value != _newPassword) { + // return 'Passwords do not match'; + return AppLocalizations.of(context)!.password_match; + } + _confirmPassword = value; + return null; + } + + Future updatePassword( + String userId, String oldPassword, String newPassword) async { + try { + setState(() { + isLoading = true; + }); + // Authenticate as admin + final adminAuth = await _pb.admins + .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com'); + final token = adminAuth.token; + + final headers = { + 'Authorization': 'Bearer $token', + }; + + // Update password + await _pb.collection('users').update( + userId, + body: { + 'password': newPassword, + 'passwordConfirm': newPassword, + }, + headers: headers, + ); + + setState(() { + isLoading = false; + _isPasswordUpdated = true; + }); + + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: + Text(AppLocalizations.of(context)!.password_update_successfully), + backgroundColor: Colors.green, + ), + ); + + print('_isPasswordUpdated $_isPasswordUpdated'); + + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (context) => ProfileScreen(userId: userId)), + // ); + } catch (e) { + setState(() { + isLoading = false; + }); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Failed to update password: $e'), + backgroundColor: Colors.red, + ), + ); + } + } + + final PAuthRepo _authRepo = PAuthRepo(); + + Future logout(BuildContext context) async { + final prefs = await SharedPreferences.getInstance(); + await _authRepo.logout(); + prefs.clear(); + context.go('/login'); // Redirect to login after logout + } + + @override + Widget build(BuildContext context) { + double screenHeight = MediaQuery.of(context).size.height; + double screenWidth = MediaQuery.of(context).size.width; + + ref.listen>(connectivityProvider, (previous, hasInternet) { + if (hasInternet.value == false) { + context.push('/internetcheck'); + } + }); + + return Scaffold( + backgroundColor: Colors.white, + body: Stack(children: [ + SingleChildScrollView( + child: SafeArea( + child: _isPasswordUpdated + ? _buildSuccessContent(screenHeight, screenWidth) + : _buildPasswordForm(ref, screenHeight, screenWidth), + ), + ), + if (isLoading) + Container( + color: Color(0x98FFFCE5), // Semi-transparent background + 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 + ), + ), + ], + ), + ) + ])); + } + + Widget _buildPasswordForm( + WidgetRef ref, double screenHeight, double screenWidth) { + final passwordLocale = ref.watch(localeProvider); + return Form( + key: _formKey, + child: Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + children: [ + // SizedBox(height: screenHeight / 7), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + onTap: () { + context.pop(); + }, + child: Container( + margin: EdgeInsets.only( + top: 10, + left: 1, + bottom: 16, + right: 1), // Add margin for positioning + child: Icon( + // Icons.close, + Icons.arrow_back_ios, + size: 24, // Icon size + color: Colors.black, // Icon color + ), + ), + ), // Left icon + Consumer( + builder: (context, ref, _) { + final locale = ref.watch(localeProvider); // Current locale + return MyToggle( + isOn: locale?.languageCode == 'en', + knobTextWhenOn: 'ع', + knobTextWhenOff: 'EN', + pathColorWhenOn: Colors.grey.shade300, + pathColorWhenOff: Colors.grey.shade300, + onTap: () { + print('has validated $hasValidated'); + final formState = _formKey.currentState; + ref.read(localeProvider.notifier).toggleLocale(); + Future.delayed(Duration(milliseconds: 100), () { + if (hasValidated && formState?.validate() == false) { + print('has validated $hasValidated'); + formState?.validate(); + } + }); + }, + ); + }, + ), // Right icon + ], + ), + SizedBox(height: screenHeight / 30), + + Text( + // "Create New Password", + AppLocalizations.of(context)!.create_new_password, + style: TextStyle( + fontSize: passwordLocale?.languageCode == 'ar' ? 24 : 26, + fontWeight: FontWeight.w300, + ), + ), + SizedBox(height: 10), + + Text.rich( + TextSpan( + children: [ + TextSpan( + text: context.translate( + 'Your New Password Must Be Different\n', + 'يجب أن تكون كلمة المرور الجديدة مختلفة\n', + ), + style: TextStyle( + fontSize: + passwordLocale?.languageCode == 'ar' ? 14 : 15, + fontWeight: FontWeight.w700, + color: Color(0xFF898C81)), + ), + TextSpan( + text: context.translate('from Previously Used Password', + 'عن كلمة المرور المستخدمة سابقًا'), + style: TextStyle( + fontSize: + passwordLocale?.languageCode == 'ar' ? 14 : 15, + color: Color(0xFF898C81), + fontWeight: FontWeight.w700), + ), + ], + ), + textAlign: TextAlign.center, + ), + SizedBox(height: 20), + TextFormField( + obscureText: _obscureNewPassword, + decoration: InputDecoration( + // hintText: 'Enter new password', + hintText: AppLocalizations.of(context)!.enter_new_password, + // prefixIcon: Icon(Icons.lock, color: Colors.blue), + hintStyle: TextStyle( + color: Color(0xFFC3C6CB), + fontWeight: FontWeight.w400, + fontFamily: 'Roboto'), + prefixIconConstraints: const BoxConstraints( + maxWidth: 25 + 16 + 10, + maxHeight: 25 + (8 * 2), + ), + prefixIcon: Padding( + padding: const EdgeInsetsDirectional.only( + start: 16, + end: 10, + ), + child: Image.asset( + MiscIconAssetPath.lock, + fit: BoxFit.fitHeight, + height: 25, + width: 25, + color: MyTheme.topicColor(IndicatorTopic.economy).shade300, + ), + ), + + suffixIcon: IconButton( + // icon: Icon( + // _obscureNewPassword + // ? Icons.visibility_off + // : Icons.visibility, + // color: Colors.blue, + // ), + + icon: Image.asset( + _obscureNewPassword + ? MiscIconAssetPath.visibilityOff + : MiscIconAssetPath.visibleOn, + + color: Color( + 0xFF9EA2A9), // Apply color if needed, but keep in mind `Image.asset` might not support color directly. + width: 24, + height: 24, + ), + + onPressed: () { + setState(() { + _obscureNewPassword = !_obscureNewPassword; + }); + }, + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide( + color: + MyTheme.topicColor(IndicatorTopic.economy).shade400, + width: 1), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide( + color: Colors.deepPurple, width: 2), // Focused border + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide( + color: Color(0xFFb22222), width: 1), // Error border + ), + focusedErrorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide( + color: Color(0xFFb22222), + width: 1), // Match the error color + ), + ), + validator: _validateNewPassword, + ), + SizedBox(height: screenHeight / 35), + TextFormField( + obscureText: _obscureConfirmPassword, + decoration: InputDecoration( + // hintText: 'Confirm new password', + hintText: AppLocalizations.of(context)!.confirm_new_password, + hintStyle: TextStyle( + color: Color(0xFFC3C6CB), + fontWeight: FontWeight.w400, + fontFamily: 'Roboto'), + prefixIconConstraints: const BoxConstraints( + maxWidth: 25 + 16 + 10, + maxHeight: 25 + (8 * 2), + ), + prefixIcon: Padding( + padding: const EdgeInsetsDirectional.only( + start: 16, + end: 10, + ), + child: Image.asset( + MiscIconAssetPath.lock, + fit: BoxFit.fitHeight, + height: 25, + width: 25, + color: MyTheme.topicColor(IndicatorTopic.economy).shade300, + ), + ), + + suffixIcon: IconButton( + // icon: Icon( + // _obscureConfirmPassword + // ? Icons.visibility_off + // : Icons.visibility, + // color: Colors.blue, + // ), + + icon: Image.asset( + _obscureConfirmPassword + ? MiscIconAssetPath.visibilityOff + : MiscIconAssetPath.visibleOn, + + color: Color( + 0xFF9EA2A9), // Apply color if needed, but keep in mind `Image.asset` might not support color directly. + width: 24, + height: 24, + ), + onPressed: () { + setState(() { + _obscureConfirmPassword = !_obscureConfirmPassword; + }); + }, + ), + // border: OutlineInputBorder(), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide( + color: + MyTheme.topicColor(IndicatorTopic.economy).shade400, + width: 1), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide( + color: Colors.deepPurple, width: 2), // Focused border + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide( + color: Color(0xFFb22222), width: 1), // Error border + ), + focusedErrorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide( + color: Color(0xFFb22222), + width: 1), // Match the error color + ), + ), + validator: _validateConfirmPassword, + ), + SizedBox(height: screenHeight / 35), + SizedBox( + width: screenWidth / 1.1, + child: ElevatedButton( + onPressed: () async { + setState(() { + hasValidated = true; + }); + if ((_formKey.currentState?.validate() ?? false)) { + await updatePassword( + widget.userId, + _oldPassword ?? '', + _newPassword ?? '', + ); + } + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.brown[300], + padding: EdgeInsets.symmetric(horizontal: 80, vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + AppLocalizations.of(context)!.save, + style: TextStyle( + fontSize: + passwordLocale?.languageCode == 'ar' ? 14 : 18, + color: Colors.white), + ), + SizedBox(width: 2), + Icon( + Icons.arrow_forward_ios, + color: Colors.white, + size: 18, + ), + ], + ), + ), + ), + SizedBox(height: screenHeight / 5), + Center( + // child: Container( + // height: screenHeight / 16, + // width: screenWidth / 2.5, + // decoration: BoxDecoration( + // image: DecorationImage( + // image: AssetImage("assets/splash_screen/logo.png"), + // fit: BoxFit.fill, + // ), + // ), + // ), + child: fcscBanner, + ), + ], + ), + ), + ); + } + + Widget _buildSuccessContent(double screenHeight, double screenWidth) { + print('IN'); + return Padding( + padding: EdgeInsets.all(20.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: screenHeight / 8), + Icon(Icons.check_circle_outlined, color: Color(0xFF8AC681), size: 80), + SizedBox(height: 20), + Text( + // "Password Changed Successfully", + AppLocalizations.of(context)!.password_changed_successfully, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: Color(0xFF414042), + ), + ), + SizedBox(height: 20), + ElevatedButton( + onPressed: () => logout(context), + style: ButtonStyle( + shape: WidgetStatePropertyAll( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + padding: WidgetStatePropertyAll( + EdgeInsets.symmetric(vertical: 10.5), + ), + textStyle: WidgetStatePropertyAll( + TextStyle( + fontFamily: context.translate( + 'Roboto', + 'NotoKufi', + ), + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + backgroundColor: WidgetStatePropertyAll( + MyTheme.topicColor(IndicatorTopic.social), + ), + // surfaceTintColor: MaterialStatePropertyAll( + // MyTheme.economy[800], + // ), + foregroundColor: WidgetStatePropertyAll( + Colors.white, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + context.translate( + 'Login', + 'تسجيل الدخول', + ), + style: TextStyle( + fontFamily: context.translate( + 'Roboto', + 'Roboto', + ), + fontSize: 17, + fontWeight: FontWeight.w500, + ), + ), + 6.horizontalSpace, + Icon(Icons.chevron_right_outlined, color: Colors.white), + ], + ), + ), + SizedBox(height: screenHeight / 2.2), + // Spacer(), + Center( + // child: Container( + // height: 40, + // width: screenWidth / 2.5, + // decoration: BoxDecoration( + // image: DecorationImage( + // image: AssetImage("assets/splash_screen/logo.png"), + // fit: BoxFit.fill, + // ), + // ), + // ), + child: fcscBanner, + ), + ], + ), + ); + } +} diff --git a/lib/presentation/Screens/auth_verification/registration.dart b/lib/presentation/Screens/auth_verification/registration.dart index c012f619..1f9bdce1 100644 --- a/lib/presentation/Screens/auth_verification/registration.dart +++ b/lib/presentation/Screens/auth_verification/registration.dart @@ -24,6 +24,7 @@ class RegisterScreen extends ConsumerStatefulWidget { } class _RegisterScreenState extends ConsumerState { + bool isLoading = false; final _formKey = GlobalKey(); final _usernameController = TextEditingController(); final _emailController = TextEditingController(); @@ -225,6 +226,7 @@ class _RegisterScreenState extends ConsumerState { if (_formKey.currentState?.validate() ?? false) { if (isChecked) { setState(() { + isLoading = true; isRegistering = true; // Disable the button }); try { @@ -254,6 +256,7 @@ class _RegisterScreenState extends ConsumerState { registrationSuccess = true; registrationFailed = false; // Show success message on success isRegistering = false; + isLoading = false; }); // Navigate to ProfileScreen after successful registration @@ -263,9 +266,14 @@ class _RegisterScreenState extends ConsumerState { // builder: (context) => ProfileScreen(userId: response.id)), // ); } else { + isLoading = false; throw Exception('User registration failed: missing user ID'); } } catch (e) { + setState(() { + isLoading = false; + }); + // Check if the error is due to an invalid or already used email if (e .toString() @@ -382,119 +390,57 @@ class _RegisterScreenState extends ConsumerState { }, child: SafeArea( child: Scaffold( - backgroundColor: Colors.white, - body: SingleChildScrollView( - child: Column( - children: [ - Align( - alignment: AlignmentDirectional.topEnd, - child: Padding( - padding: EdgeInsets.only(top: 16.0, right: 16.0), - child: Consumer( - builder: (context, ref, _) { - final locale = - ref.watch(localeProvider); // Current locale - return MyToggle( - isOn: locale?.languageCode == 'en', - knobTextWhenOn: 'ع', - knobTextWhenOff: 'EN', - pathColorWhenOn: Colors.grey.shade300, - pathColorWhenOff: Colors.grey.shade300, - onTap: () { - final formState = _formKey.currentState; - ref.read(localeProvider.notifier).toggleLocale(); - Future.delayed(Duration(milliseconds: 100), () { - if (hasValidated && - formState?.validate() == false) { - formState?.validate(); - } - }); + backgroundColor: Colors.white, + body: Stack(children: [ + SingleChildScrollView( + child: Column( + children: [ + Align( + alignment: AlignmentDirectional.topEnd, + child: Padding( + padding: EdgeInsets.only(top: 16.0, right: 16.0), + child: Consumer( + builder: (context, ref, _) { + final locale = + ref.watch(localeProvider); // Current locale + return MyToggle( + isOn: locale?.languageCode == 'en', + knobTextWhenOn: 'ع', + knobTextWhenOff: 'EN', + pathColorWhenOn: Colors.grey.shade300, + pathColorWhenOff: Colors.grey.shade300, + onTap: () { + final formState = _formKey.currentState; + ref + .read(localeProvider.notifier) + .toggleLocale(); + Future.delayed(Duration(milliseconds: 100), () { + if (hasValidated && + formState?.validate() == false) { + formState?.validate(); + } + }); + }, + ); }, - ); - }, + ), + ), ), - ), - ), - Column(children: [ - registrationSuccess - ? Padding( - padding: const EdgeInsets.all(20.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox(height: screenheight / 4), - buildIconContainer( - Icons.report, Color(0xFF7DAFBC)), - SizedBox(height: 20), - Text( - context.translate( - 'Your registration is pending for verification.', - 'تسجيلك في انتظار التحقق.'), - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Color(0xFF414042)), - ), - SizedBox(height: 10), - Text( - context.translate( - 'Kindly verify your mail to proceed further.', - 'يرجى التحقق من البريد الخاص بك للمضي قدما.'), - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14, - color: Color(0xFF898C81), - ), - ), - SizedBox(height: 10), - ElevatedButton( - onPressed: () => {context.go('/login')}, - style: ElevatedButton.styleFrom( - backgroundColor: - Color(0xFFAA8E83), // Background color - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - 12), // Border radius - ), - ), - child: Text( - context.translate( - 'Go to Login', 'اذهب إلى تسجيل الدخول'), - style: TextStyle( - color: Colors.white, // Text color - ), - ), - ), - SizedBox(height: screenheight / 5), - Center( - child: Container( - height: screenheight / 16, - width: screenwidth / 2.5, - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage( - "assets/splash_screen/logo.png"), // Background image asset - fit: BoxFit.fill, - ), - ), - )) - ], - ), - ) - : registrationFailed + Column(children: [ + registrationSuccess ? Padding( padding: const EdgeInsets.all(20.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - SizedBox(height: screenheight / 8), - buildIconContainer(Icons.report, Colors.red), + SizedBox(height: screenheight / 4), + buildIconContainer( + Icons.report, Color(0xFF7DAFBC)), SizedBox(height: 20), Text( context.translate( - 'Sorry ${_usernameController.text}!', - 'آسف ${_usernameController.text}!'), + 'Your registration is pending for verification.', + 'تسجيلك في انتظار التحقق.'), textAlign: TextAlign.center, style: TextStyle( fontSize: 16, @@ -504,8 +450,8 @@ class _RegisterScreenState extends ConsumerState { SizedBox(height: 10), Text( context.translate( - 'Your registration process failed. For further assistance, please contact support.', - 'فشلت عملية التسجيل الخاصة بك. لمزيد من المساعدة، يرجى الاتصال بالدعم.'), + 'Kindly verify your mail to proceed further.', + 'يرجى التحقق من البريد الخاص بك للمضي قدما.'), textAlign: TextAlign.center, style: TextStyle( fontSize: 14, @@ -514,20 +460,21 @@ class _RegisterScreenState extends ConsumerState { ), SizedBox(height: 10), ElevatedButton( - onPressed: () => { - setState(() { - registrationFailed = false; - registrationSuccess = false; - _usernameController.clear(); - _emailController.clear(); - _passwordController.clear(); - _confirmpasswordController.clear(); - isChecked = false; - }) - }, + onPressed: () => {context.go('/login')}, + style: ElevatedButton.styleFrom( + backgroundColor: + Color(0xFFAA8E83), // Background color + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 12), // Border radius + ), + ), child: Text( - context.translate( - 'Retry', 'أعد المحاولة'), + context.translate('Go to Login', + 'اذهب إلى تسجيل الدخول'), + style: TextStyle( + color: Colors.white, // Text color + ), ), ), SizedBox(height: screenheight / 5), @@ -546,574 +493,582 @@ class _RegisterScreenState extends ConsumerState { ], ), ) - : Form( - key: _formKey, - child: SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox(height: screenheight / 60), - Text( - // 'Register', - AppLocalizations.of(context)! - .register_title, - style: TextStyle( - fontSize: 32, - fontWeight: FontWeight.w400), - ), - SizedBox(height: 10), - Text( - AppLocalizations.of(context)! - .register_details, - style: TextStyle( - fontSize: context.translate(18.0, 14.0), - color: const Color(0xff898C81), - fontWeight: FontWeight.w600, + : registrationFailed + ? Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox(height: screenheight / 8), + buildIconContainer( + Icons.report, Colors.red), + SizedBox(height: 20), + Text( + context.translate( + 'Sorry ${_usernameController.text}!', + 'آسف ${_usernameController.text}!'), + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: Color(0xFF414042)), ), - ), - - SizedBox(height: 20), - // Display this if registration is pending approval - Padding( - padding: const EdgeInsets.only( - top: 5.0, - bottom: 10, - left: 30, - right: 30), - child: TextFormField( - controller: _usernameController, - // focusNode: _focusNodes[0], - decoration: InputDecoration( - // hintText: _showHints[0] ? 'Username' : null, - hintText: _showHints[0] - ? AppLocalizations.of(context)! - .register_name - : null, - prefixIconConstraints: - const BoxConstraints( - maxWidth: 25 + 16 + 10, - maxHeight: 25 + (8 * 2), - ), - prefixIcon: Padding( - padding: const EdgeInsetsDirectional - .only( - start: 16, - end: 10, - ), - child: Image.asset( - MiscIconAssetPath.person, - fit: BoxFit.fitHeight, - height: 25, - width: 25, - color: MyTheme.topicColor( - IndicatorTopic.economy) - .shade300, - ), - ), - - // prefixIcon: Icon( - // Icons.person, - // color: Color(0xFF90B0D5), - // - // ), - // border: OutlineInputBorder(), - enabledBorder: OutlineInputBorder( - borderRadius: - BorderRadius.circular(10), - borderSide: BorderSide( - color: MyTheme.topicColor( - IndicatorTopic.economy) - .shade400, - width: 1), // Enabled border - ), - focusedBorder: OutlineInputBorder( - borderRadius: - BorderRadius.circular(10), - borderSide: BorderSide( - color: Colors.deepPurple, - width: 2), // Focused border - ), - errorBorder: OutlineInputBorder( - borderRadius: - BorderRadius.circular(10), - borderSide: BorderSide( - color: Color(0xFFb22222), - width: 1), // Error border - ), - focusedErrorBorder: - OutlineInputBorder( - borderRadius: - BorderRadius.circular(10), - borderSide: BorderSide( - color: Color(0xFFb22222), - width: - 1), // Match the error color - ), - counterText: '', - hintStyle: TextStyle( - color: Color(0xFFC3C6CB), - fontSize: - registerLocale?.languageCode == - 'ar' - ? 14 - : 16, + SizedBox(height: 10), + Text( + context.translate( + 'Your registration process failed. For further assistance, please contact support.', + 'فشلت عملية التسجيل الخاصة بك. لمزيد من المساعدة، يرجى الاتصال بالدعم.'), + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14, + color: Color(0xFF898C81), + ), + ), + SizedBox(height: 10), + ElevatedButton( + onPressed: () => { + setState(() { + registrationFailed = false; + registrationSuccess = false; + _usernameController.clear(); + _emailController.clear(); + _passwordController.clear(); + _confirmpasswordController.clear(); + isChecked = false; + }) + }, + child: Text( + context.translate( + 'Retry', 'أعد المحاولة'), + ), + ), + SizedBox(height: screenheight / 5), + Center( + child: Container( + height: screenheight / 16, + width: screenwidth / 2.5, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage( + "assets/splash_screen/logo.png"), // Background image asset + fit: BoxFit.fill, ), ), - validator: _validateUsername, - maxLength: - 40, // Set the maximum length to 20 characters - maxLengthEnforcement: - MaxLengthEnforcement.enforced, - ), - ), - SizedBox(height: 15), - Padding( - padding: const EdgeInsets.only( - top: 5.0, - bottom: 10, - left: 30, - right: 30), - child: TextFormField( - controller: _emailController, - // focusNode: _focusNodes[1], - decoration: InputDecoration( - // hintText: 'Enter your email', - hintText: _showHints[1] - ? AppLocalizations.of(context)! - .enter_your_email - : null, - // _showHints[1] ? 'Enter your email' : null, - prefixIconConstraints: - const BoxConstraints( - maxWidth: 25 + 16 + 10, - maxHeight: 25 + (8 * 2), - ), - prefixIcon: Padding( - padding: const EdgeInsetsDirectional - .only( - start: 16, - end: 10, - ), - child: Image.asset( - MiscIconAssetPath.vector, - fit: BoxFit.fitHeight, - height: 20, - width: 25, - color: MyTheme.topicColor( - IndicatorTopic.economy) - .shade100, - ), - ), - - // border: OutlineInputBorder(), - enabledBorder: OutlineInputBorder( - borderRadius: - BorderRadius.circular(10), - borderSide: BorderSide( - color: MyTheme.topicColor( - IndicatorTopic.economy) - .shade400, - width: 1), - ), - focusedBorder: OutlineInputBorder( - borderRadius: - BorderRadius.circular(10), - borderSide: BorderSide( - color: Colors.deepPurple, - width: 2), // Focused border - ), - errorBorder: OutlineInputBorder( - borderRadius: - BorderRadius.circular(10), - borderSide: BorderSide( - color: Color(0xFFb22222), - width: 1), // Error border - ), - focusedErrorBorder: - OutlineInputBorder( - borderRadius: - BorderRadius.circular(10), - borderSide: BorderSide( - color: Color(0xFFb22222), - width: - 1), // Match the error color - ), - counterText: '', - hintStyle: TextStyle( - color: Color(0xFFC3C6CB), + )) + ], + ), + ) + : Form( + key: _formKey, + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + SizedBox(height: screenheight / 60), + Text( + // 'Register', + AppLocalizations.of(context)! + .register_title, + style: TextStyle( + fontSize: 32, + fontWeight: FontWeight.w400), + ), + SizedBox(height: 10), + Text( + AppLocalizations.of(context)! + .register_details, + style: TextStyle( fontSize: - registerLocale?.languageCode == - 'ar' - ? 14 - : 16, + context.translate(18.0, 14.0), + color: const Color(0xff898C81), + fontWeight: FontWeight.w600, ), ), - validator: _validateEmail, - maxLength: 320, - maxLengthEnforcement: - MaxLengthEnforcement.enforced, - inputFormatters: [ - LengthLimitingTextInputFormatter( - 320), // Limit to 320 characters - ], - ), - ), - SizedBox(height: 15), - Padding( - padding: const EdgeInsets.only( - top: 5.0, - bottom: 10, - left: 30, - right: 30), - child: TextFormField( - controller: _passwordController, - // focusNode: _focusNodes[2], - obscureText: _obscurePassword, - decoration: InputDecoration( - hintText: _showHints[2] - ? AppLocalizations.of(context)! - .enter_your_password - : null, - // _showHints[2] ? 'Enter your password' : null, - prefixIconConstraints: - const BoxConstraints( - maxWidth: 25 + 16 + 10, - maxHeight: 25 + (8 * 2), - ), - prefixIcon: Padding( - padding: const EdgeInsetsDirectional - .only( - start: 16, - end: 10, - ), - child: Image.asset( - MiscIconAssetPath.lock, - fit: BoxFit.fitHeight, - height: 25, - width: 25, - color: MyTheme.topicColor( - IndicatorTopic.economy) - .shade300, - ), - ), - // prefixIcon: Icon( - // Icons.lock, - // color: Color(0xFF90B0D5), - // ), - suffixIcon: IconButton( - // icon: Icon( - // _obscurePassword - // ? Icons.visibility_off - // : Icons.visibility, - // color: Color(0xFF9EA2A9), - // ), - - icon: Image.asset( - _obscurePassword - ? MiscIconAssetPath - .visibilityOff - : MiscIconAssetPath.visibleOn, - - color: Color( - 0xFF9EA2A9), // Apply color if needed, but keep in mind `Image.asset` might not support color directly. - width: 24, - height: 24, - ), - - onPressed: () { - setState(() { - _obscurePassword = - !_obscurePassword; - }); - }, - ), - // border: OutlineInputBorder(), - enabledBorder: OutlineInputBorder( - borderRadius: - BorderRadius.circular(10), - borderSide: BorderSide( - color: MyTheme.topicColor( - IndicatorTopic.economy) - .shade400, - width: 1), - ), - focusedBorder: OutlineInputBorder( - borderRadius: - BorderRadius.circular(10), - borderSide: BorderSide( - color: Colors.deepPurple, - width: 2), // Focused border - ), - errorBorder: OutlineInputBorder( - borderRadius: - BorderRadius.circular(10), - borderSide: BorderSide( - color: Color(0xFFb22222), - width: 1), // Error border - ), - focusedErrorBorder: - OutlineInputBorder( - borderRadius: - BorderRadius.circular(10), - borderSide: BorderSide( - color: Color(0xFFb22222), - width: - 1), // Match the error color - ), - - counterText: '', - hintStyle: TextStyle( - color: Color(0xFFC3C6CB), - fontSize: - registerLocale?.languageCode == - 'ar' - ? 14 - : 16, - ), - ), - validator: _validatePassword, - maxLength: 40, - maxLengthEnforcement: - MaxLengthEnforcement.enforced, - ), - ), - SizedBox(height: 15), - Padding( - padding: const EdgeInsets.only( - top: 5.0, - bottom: 10, - left: 30, - right: 30), - child: TextFormField( - controller: _confirmpasswordController, - // focusNode: _focusNodes[3], - obscureText: _obscureConfirmPassword, - decoration: InputDecoration( - hintText: _showHints[3] - ? AppLocalizations.of(context)! - .register_Confirm_password - : null, - // _showHints[3] ? 'Confirm password' : null, - prefixIconConstraints: - const BoxConstraints( - maxWidth: 25 + 16 + 10, - maxHeight: 25 + (8 * 2), - ), - prefixIcon: Padding( - padding: const EdgeInsetsDirectional - .only( - start: 16, - end: 10, - ), - child: Image.asset( - MiscIconAssetPath.lock, - fit: BoxFit.fitHeight, - height: 25, - width: 25, - color: MyTheme.topicColor( - IndicatorTopic.economy) - .shade300, - ), - ), - - suffixIcon: IconButton( - icon: Image.asset( - _obscureConfirmPassword - ? MiscIconAssetPath - .visibilityOff - : MiscIconAssetPath.visibleOn, - - color: Color( - 0xFF9EA2A9), // Apply color if needed, but keep in mind `Image.asset` might not support color directly. - width: 24, - height: 24, - ), - onPressed: () { - setState(() { - _obscureConfirmPassword = - !_obscureConfirmPassword; - }); - }, - ), - // border: OutlineInputBorder( - // borderSide: BorderSide(color: Colors.blue, width: 2), // Default border color - // ), - enabledBorder: OutlineInputBorder( - borderRadius: - BorderRadius.circular(10), - borderSide: BorderSide( - color: MyTheme.topicColor( - IndicatorTopic.economy) - .shade400, - width: 1), - ), - focusedBorder: OutlineInputBorder( - borderRadius: - BorderRadius.circular(10), - borderSide: BorderSide( - color: Colors.deepPurple, - width: 2), // Focused border - ), - errorBorder: OutlineInputBorder( - borderRadius: - BorderRadius.circular(10), - borderSide: BorderSide( - color: Color(0xFFb22222), - width: 1), // Error border - ), - focusedErrorBorder: - OutlineInputBorder( - borderRadius: - BorderRadius.circular(10), - borderSide: BorderSide( - color: Color(0xFFb22222), - width: - 1), // Match the error color - ), - counterText: '', - hintStyle: TextStyle( - color: Color(0xFFC3C6CB), - fontSize: - registerLocale?.languageCode == - 'ar' - ? 14 - : 16, - ), - ), - validator: _validateConfirmPassword, - maxLength: 40, - maxLengthEnforcement: - MaxLengthEnforcement.enforced, - inputFormatters: [ - LengthLimitingTextInputFormatter( - 64), // Limit to 40 characters - ], - ), - ), - SizedBox(height: 10), - Padding( - padding: const EdgeInsets.only( - top: 0, - bottom: 10, - left: 15.5, - right: 30), - child: Row( - mainAxisAlignment: - MainAxisAlignment.center, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Checkbox( - value: isChecked, - onChanged: (value) { - setState(() { - isChecked = value ?? false; - showError = false; - }); - }, - side: BorderSide( - color: showError - ? Color(0xFFb22222) - : MyTheme.topicColor( + SizedBox(height: 20), + // Display this if registration is pending approval + Padding( + padding: const EdgeInsets.only( + top: 5.0, + bottom: 10, + left: 30, + right: 30), + child: TextFormField( + controller: _usernameController, + // focusNode: _focusNodes[0], + decoration: InputDecoration( + // hintText: _showHints[0] ? 'Username' : null, + hintText: _showHints[0] + ? AppLocalizations.of( + context)! + .register_name + : null, + prefixIconConstraints: + const BoxConstraints( + maxWidth: 25 + 16 + 10, + maxHeight: 25 + (8 * 2), + ), + prefixIcon: Padding( + padding: + const EdgeInsetsDirectional + .only( + start: 16, + end: 10, + ), + child: Image.asset( + MiscIconAssetPath.person, + fit: BoxFit.fitHeight, + height: 25, + width: 25, + color: MyTheme.topicColor( IndicatorTopic .economy) - .shade400, - width: 1, - ), - ), - Expanded( - child: Padding( - padding: const EdgeInsets.only( - top: 0, + .shade300, + ), ), - child: Text.rich( - TextSpan( - text: AppLocalizations.of( - context, - )! - .agree, - // text: 'I agree to ', - style: TextStyle( - fontWeight: FontWeight.w500, - fontSize: registerLocale - ?.languageCode == - 'ar' - ? 12 - : 14, - color: Color( - 0xFF898C81, - ), // Change to your desired color + + // prefixIcon: Icon( + // Icons.person, + // color: Color(0xFF90B0D5), + // + // ), + // border: OutlineInputBorder(), + enabledBorder: OutlineInputBorder( + borderRadius: + BorderRadius.circular(10), + borderSide: BorderSide( + color: MyTheme.topicColor( + IndicatorTopic + .economy) + .shade400, + width: 1), // Enabled border + ), + focusedBorder: OutlineInputBorder( + borderRadius: + BorderRadius.circular(10), + borderSide: BorderSide( + color: Colors.deepPurple, + width: 2), // Focused border + ), + errorBorder: OutlineInputBorder( + borderRadius: + BorderRadius.circular(10), + borderSide: BorderSide( + color: Color(0xFFb22222), + width: 1), // Error border + ), + focusedErrorBorder: + OutlineInputBorder( + borderRadius: + BorderRadius.circular(10), + borderSide: BorderSide( + color: Color(0xFFb22222), + width: + 1), // Match the error color + ), + counterText: '', + hintStyle: TextStyle( + color: Color(0xFFC3C6CB), + fontSize: registerLocale + ?.languageCode == + 'ar' + ? 14 + : 16, + ), + ), + validator: _validateUsername, + maxLength: + 40, // Set the maximum length to 20 characters + maxLengthEnforcement: + MaxLengthEnforcement.enforced, + ), + ), + SizedBox(height: 15), + Padding( + padding: const EdgeInsets.only( + top: 5.0, + bottom: 10, + left: 30, + right: 30), + child: TextFormField( + controller: _emailController, + // focusNode: _focusNodes[1], + decoration: InputDecoration( + // hintText: 'Enter your email', + hintText: _showHints[1] + ? AppLocalizations.of( + context)! + .enter_your_email + : null, + // _showHints[1] ? 'Enter your email' : null, + prefixIconConstraints: + const BoxConstraints( + maxWidth: 25 + 16 + 10, + maxHeight: 25 + (8 * 2), + ), + prefixIcon: Padding( + padding: + const EdgeInsetsDirectional + .only( + start: 16, + end: 10, + ), + child: Image.asset( + MiscIconAssetPath.vector, + fit: BoxFit.fitHeight, + height: 20, + width: 25, + color: MyTheme.topicColor( + IndicatorTopic + .economy) + .shade100, + ), + ), + + // border: OutlineInputBorder(), + enabledBorder: OutlineInputBorder( + borderRadius: + BorderRadius.circular(10), + borderSide: BorderSide( + color: MyTheme.topicColor( + IndicatorTopic + .economy) + .shade400, + width: 1), + ), + focusedBorder: OutlineInputBorder( + borderRadius: + BorderRadius.circular(10), + borderSide: BorderSide( + color: Colors.deepPurple, + width: 2), // Focused border + ), + errorBorder: OutlineInputBorder( + borderRadius: + BorderRadius.circular(10), + borderSide: BorderSide( + color: Color(0xFFb22222), + width: 1), // Error border + ), + focusedErrorBorder: + OutlineInputBorder( + borderRadius: + BorderRadius.circular(10), + borderSide: BorderSide( + color: Color(0xFFb22222), + width: + 1), // Match the error color + ), + counterText: '', + hintStyle: TextStyle( + color: Color(0xFFC3C6CB), + fontSize: registerLocale + ?.languageCode == + 'ar' + ? 14 + : 16, + ), + ), + validator: _validateEmail, + maxLength: 320, + maxLengthEnforcement: + MaxLengthEnforcement.enforced, + inputFormatters: [ + LengthLimitingTextInputFormatter( + 320), // Limit to 320 characters + ], + ), + ), + SizedBox(height: 15), + Padding( + padding: const EdgeInsets.only( + top: 5.0, + bottom: 10, + left: 30, + right: 30), + child: TextFormField( + controller: _passwordController, + // focusNode: _focusNodes[2], + obscureText: _obscurePassword, + decoration: InputDecoration( + hintText: _showHints[2] + ? AppLocalizations.of( + context)! + .enter_your_password + : null, + // _showHints[2] ? 'Enter your password' : null, + prefixIconConstraints: + const BoxConstraints( + maxWidth: 25 + 16 + 10, + maxHeight: 25 + (8 * 2), + ), + prefixIcon: Padding( + padding: + const EdgeInsetsDirectional + .only( + start: 16, + end: 10, + ), + child: Image.asset( + MiscIconAssetPath.lock, + fit: BoxFit.fitHeight, + height: 25, + width: 25, + color: MyTheme.topicColor( + IndicatorTopic + .economy) + .shade300, + ), + ), + + // prefixIcon: Icon( + // Icons.lock, + // color: Color(0xFF90B0D5), + // ), + suffixIcon: IconButton( + // icon: Icon( + // _obscurePassword + // ? Icons.visibility_off + // : Icons.visibility, + // color: Color(0xFF9EA2A9), + // ), + + icon: Image.asset( + _obscurePassword + ? MiscIconAssetPath + .visibilityOff + : MiscIconAssetPath + .visibleOn, + + color: Color( + 0xFF9EA2A9), // Apply color if needed, but keep in mind `Image.asset` might not support color directly. + width: 24, + height: 24, + ), + + onPressed: () { + setState(() { + _obscurePassword = + !_obscurePassword; + }); + }, + ), + // border: OutlineInputBorder(), + enabledBorder: OutlineInputBorder( + borderRadius: + BorderRadius.circular(10), + borderSide: BorderSide( + color: MyTheme.topicColor( + IndicatorTopic + .economy) + .shade400, + width: 1), + ), + focusedBorder: OutlineInputBorder( + borderRadius: + BorderRadius.circular(10), + borderSide: BorderSide( + color: Colors.deepPurple, + width: 2), // Focused border + ), + errorBorder: OutlineInputBorder( + borderRadius: + BorderRadius.circular(10), + borderSide: BorderSide( + color: Color(0xFFb22222), + width: 1), // Error border + ), + focusedErrorBorder: + OutlineInputBorder( + borderRadius: + BorderRadius.circular(10), + borderSide: BorderSide( + color: Color(0xFFb22222), + width: + 1), // Match the error color + ), + + counterText: '', + hintStyle: TextStyle( + color: Color(0xFFC3C6CB), + fontSize: registerLocale + ?.languageCode == + 'ar' + ? 14 + : 16, + ), + ), + validator: _validatePassword, + maxLength: 40, + maxLengthEnforcement: + MaxLengthEnforcement.enforced, + ), + ), + SizedBox(height: 15), + Padding( + padding: const EdgeInsets.only( + top: 5.0, + bottom: 10, + left: 30, + right: 30), + child: TextFormField( + controller: + _confirmpasswordController, + // focusNode: _focusNodes[3], + obscureText: + _obscureConfirmPassword, + decoration: InputDecoration( + hintText: _showHints[3] + ? AppLocalizations.of( + context)! + .register_Confirm_password + : null, + // _showHints[3] ? 'Confirm password' : null, + prefixIconConstraints: + const BoxConstraints( + maxWidth: 25 + 16 + 10, + maxHeight: 25 + (8 * 2), + ), + prefixIcon: Padding( + padding: + const EdgeInsetsDirectional + .only( + start: 16, + end: 10, + ), + child: Image.asset( + MiscIconAssetPath.lock, + fit: BoxFit.fitHeight, + height: 25, + width: 25, + color: MyTheme.topicColor( + IndicatorTopic + .economy) + .shade300, + ), + ), + + suffixIcon: IconButton( + icon: Image.asset( + _obscureConfirmPassword + ? MiscIconAssetPath + .visibilityOff + : MiscIconAssetPath + .visibleOn, + + color: Color( + 0xFF9EA2A9), // Apply color if needed, but keep in mind `Image.asset` might not support color directly. + width: 24, + height: 24, + ), + onPressed: () { + setState(() { + _obscureConfirmPassword = + !_obscureConfirmPassword; + }); + }, + ), + // border: OutlineInputBorder( + // borderSide: BorderSide(color: Colors.blue, width: 2), // Default border color + // ), + enabledBorder: OutlineInputBorder( + borderRadius: + BorderRadius.circular(10), + borderSide: BorderSide( + color: MyTheme.topicColor( + IndicatorTopic + .economy) + .shade400, + width: 1), + ), + focusedBorder: OutlineInputBorder( + borderRadius: + BorderRadius.circular(10), + borderSide: BorderSide( + color: Colors.deepPurple, + width: 2), // Focused border + ), + errorBorder: OutlineInputBorder( + borderRadius: + BorderRadius.circular(10), + borderSide: BorderSide( + color: Color(0xFFb22222), + width: 1), // Error border + ), + focusedErrorBorder: + OutlineInputBorder( + borderRadius: + BorderRadius.circular(10), + borderSide: BorderSide( + color: Color(0xFFb22222), + width: + 1), // Match the error color + ), + counterText: '', + hintStyle: TextStyle( + color: Color(0xFFC3C6CB), + fontSize: registerLocale + ?.languageCode == + 'ar' + ? 14 + : 16, + ), + ), + validator: _validateConfirmPassword, + maxLength: 40, + maxLengthEnforcement: + MaxLengthEnforcement.enforced, + inputFormatters: [ + LengthLimitingTextInputFormatter( + 64), // Limit to 40 characters + ], + ), + ), + SizedBox(height: 10), + Padding( + padding: const EdgeInsets.only( + top: 0, + bottom: 10, + left: 15.5, + right: 30), + child: Row( + mainAxisAlignment: + MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Checkbox( + value: isChecked, + onChanged: (value) { + setState(() { + isChecked = value ?? false; + showError = false; + }); + }, + side: BorderSide( + color: showError + ? Color(0xFFb22222) + : MyTheme.topicColor( + IndicatorTopic + .economy) + .shade400, + width: 1, + ), + ), + Expanded( + child: Padding( + padding: + const EdgeInsets.only( + top: 0, ), - children: [ - TextSpan( - recognizer: - TapGestureRecognizer() - ..onTap = () => - context.push( - '/terms&conditions'), - text: AppLocalizations.of( - context)! - .terms_conditions, - // text: 'Terms & Conditions', - style: TextStyle( - fontSize: registerLocale - ?.languageCode == - 'ar' - ? 12 - : 14, - fontWeight: - FontWeight.bold, - color: - Color(0xFF985400), - // Makes the text bold - // decoration: - // TextDecoration.underline, - decorationColor: - Color(0xFF985400), - decorationThickness: - 1), - ), + child: Text.rich( TextSpan( text: AppLocalizations.of( - context)! - .t_and, - style: TextStyle( - color: Color( - 0xFF898C81), // Change to your desired color - ), - ), - TextSpan( - recognizer: - TapGestureRecognizer() - ..onTap = () => - context.push( - '/privacy_policy'), - text: AppLocalizations.of( - context)! - .privacy_policy, - style: TextStyle( - fontWeight: - FontWeight.bold, - fontSize: registerLocale - ?.languageCode == - 'ar' - ? 12 - : 14, - fontFamily: 'Roboto', - color: - Color(0xFF985400), - // decoration: - // TextDecoration.underline, - decorationColor: - Color(0xFF648CBA), - decorationThickness: - 1), - ), - TextSpan( - text: AppLocalizations.of( - context)! - .conditions, + context, + )! + .agree, + // text: 'I agree to ', style: TextStyle( fontWeight: FontWeight.w500, @@ -1123,185 +1078,298 @@ class _RegisterScreenState extends ConsumerState { ? 12 : 14, color: Color( - 0xFF898C81), // Change to your desired color + 0xFF898C81, + ), // Change to your desired color ), + children: [ + TextSpan( + recognizer: + TapGestureRecognizer() + ..onTap = () => + context.push( + '/terms&conditions'), + text: AppLocalizations + .of(context)! + .terms_conditions, + // text: 'Terms & Conditions', + style: TextStyle( + fontSize: + registerLocale + ?.languageCode == + 'ar' + ? 12 + : 14, + fontWeight: + FontWeight + .bold, + color: Color( + 0xFF985400), + // Makes the text bold + // decoration: + // TextDecoration.underline, + decorationColor: + Color( + 0xFF985400), + decorationThickness: + 1), + ), + TextSpan( + text: AppLocalizations + .of(context)! + .t_and, + style: TextStyle( + color: Color( + 0xFF898C81), // Change to your desired color + ), + ), + TextSpan( + recognizer: + TapGestureRecognizer() + ..onTap = () => + context.push( + '/privacy_policy'), + text: AppLocalizations + .of(context)! + .privacy_policy, + style: TextStyle( + fontWeight: + FontWeight + .bold, + fontSize: + registerLocale + ?.languageCode == + 'ar' + ? 12 + : 14, + fontFamily: + 'Roboto', + color: Color( + 0xFF985400), + // decoration: + // TextDecoration.underline, + decorationColor: + Color( + 0xFF648CBA), + decorationThickness: + 1), + ), + TextSpan( + text: AppLocalizations + .of(context)! + .conditions, + style: TextStyle( + fontWeight: + FontWeight.w500, + fontSize: registerLocale + ?.languageCode == + 'ar' + ? 12 + : 14, + color: Color( + 0xFF898C81), // Change to your desired color + ), + ), + ], ), - ], + ), ), ), - ), + ], ), - ], - ), - ), - if (showError) - Row( - mainAxisAlignment: - MainAxisAlignment.start, - children: [ - Padding( - padding: EdgeInsets.only( - left: registerLocale - ?.languageCode == - 'ar' - ? 0 - : 40.0, - right: registerLocale - ?.languageCode == - 'ar' - ? 45 - : 0.0, - ), - child: Text( - context.translate( - 'Required', 'مطلوب'), - style: TextStyle( - color: Color(0xFFb22222), - fontSize: registerLocale - ?.languageCode == - 'ar' - ? 12 - : 12, - ), - ), - ), - ], - ), - SizedBox(height: 20), - Padding( - padding: const EdgeInsets.only( - top: 5.0, - bottom: 10, - left: 30, - right: 30), - child: SizedBox( - width: screenwidth / 1, - child: ElevatedButton( - onPressed: isRegistering - ? null - : _registerUser, - style: ElevatedButton.styleFrom( - backgroundColor: Color( - 0xFFA7887A), // Brownish color for Register - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(10), - ), - ), - child: Row( - mainAxisSize: MainAxisSize.min, + ), + if (showError) + Row( + mainAxisAlignment: + MainAxisAlignment.start, children: [ - Text( - // "Register", - AppLocalizations.of(context)! - .register_title, - style: TextStyle( + Padding( + padding: EdgeInsets.only( + left: registerLocale + ?.languageCode == + 'ar' + ? 0 + : 40.0, + right: registerLocale + ?.languageCode == + 'ar' + ? 45 + : 0.0, + ), + child: Text( + context.translate( + 'Required', 'مطلوب'), + style: TextStyle( + color: Color(0xFFb22222), fontSize: registerLocale ?.languageCode == 'ar' - ? 14 - : 16, - color: Colors.white), + ? 12 + : 12, + ), + ), ), - SizedBox(width: 8), - const Icon( - Icons.chevron_right_outlined, - color: Colors - .white, // Set your desired color here - ) ], ), - ), - ), - ), - SizedBox(height: 15), - Text( - AppLocalizations.of(context)! - .account_confirmation, - style: TextStyle( - fontWeight: FontWeight.w500, - fontSize: - registerLocale?.languageCode == 'ar' - ? 12 - : 14, - color: Color(0xFF898C81), - ), - ), - SizedBox(height: 15), - Padding( - padding: const EdgeInsets.only( - top: 5.0, - bottom: 10, - left: 30, - right: 30), - child: SizedBox( - width: screenwidth / 1, - child: ElevatedButton( - onPressed: () { - context.pop(); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Color( - 0xFF82AFCB), // Blueish color for Login - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(10), + SizedBox(height: 20), + Padding( + padding: const EdgeInsets.only( + top: 5.0, + bottom: 10, + left: 30, + right: 30), + child: SizedBox( + width: screenwidth / 1, + child: ElevatedButton( + onPressed: isRegistering + ? null + : _registerUser, + style: ElevatedButton.styleFrom( + backgroundColor: Color( + 0xFFA7887A), // Brownish color for Register + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(10), + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + // "Register", + AppLocalizations.of( + context)! + .register_title, + style: TextStyle( + fontSize: registerLocale + ?.languageCode == + 'ar' + ? 14 + : 16, + color: Colors.white), + ), + SizedBox(width: 8), + const Icon( + Icons + .chevron_right_outlined, + color: Colors + .white, // Set your desired color here + ) + ], + ), ), ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - AppLocalizations.of(context)! - .login_title, - style: TextStyle( - fontSize: 16, - color: Colors.white), - ), - SizedBox(width: 8), - const Icon( - Icons.chevron_right_outlined, - color: Colors - .white, // Set your desired color here - ) - ], + ), + SizedBox(height: 15), + Text( + AppLocalizations.of(context)! + .account_confirmation, + style: TextStyle( + fontWeight: FontWeight.w500, + fontSize: + registerLocale?.languageCode == + 'ar' + ? 12 + : 14, + color: Color(0xFF898C81), ), ), - ), + SizedBox(height: 15), + Padding( + padding: const EdgeInsets.only( + top: 5.0, + bottom: 10, + left: 30, + right: 30), + child: SizedBox( + width: screenwidth / 1, + child: ElevatedButton( + onPressed: () { + context.pop(); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Color( + 0xFF82AFCB), // Blueish color for Login + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(10), + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + AppLocalizations.of( + context)! + .login_title, + style: TextStyle( + fontSize: 16, + color: Colors.white), + ), + SizedBox(width: 8), + const Icon( + Icons + .chevron_right_outlined, + color: Colors + .white, // Set your desired color here + ) + ], + ), + ), + ), + ), + SizedBox( + height: 10, + ), + Center( + // child: Container( + // height: screenheight / 16, + // width: screenwidth / 3, + // decoration: BoxDecoration( + // image: DecorationImage( + // image: AssetImage( + // "assets/splash_screen/logo.png"), // Background image asset + // fit: BoxFit.fill, + // ), + // ), + // ) + child: fcscBanner, + ), + SizedBox( + height: 20, + ), + ], ), - SizedBox( - height: 10, - ), - Center( - // child: Container( - // height: screenheight / 16, - // width: screenwidth / 3, - // decoration: BoxDecoration( - // image: DecorationImage( - // image: AssetImage( - // "assets/splash_screen/logo.png"), // Background image asset - // fit: BoxFit.fill, - // ), - // ), - // ) - child: fcscBanner, - ), - SizedBox( - height: 20, - ), - ], + ), ), - ), - ), - ]), - // Container( - // height: MediaQuery.of(context).size.height / 1.2, - // child: - // ), - ], - ), - ), - ), + ]), + // Container( + // height: MediaQuery.of(context).size.height / 1.2, + // child: + // ), + ], + ), + ), + if (isLoading) + Container( + color: Color(0x98FFFCE5), // Semi-transparent background + 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 + ), + ), + ], + ), + ) + ])), ), ); } diff --git a/lib/presentation/Screens/charts/screens/chart_screen.dart b/lib/presentation/Screens/charts/screens/chart_screen.dart index f1dc1490..9c98a13f 100644 --- a/lib/presentation/Screens/charts/screens/chart_screen.dart +++ b/lib/presentation/Screens/charts/screens/chart_screen.dart @@ -2110,6 +2110,10 @@ class _ChartScreen1State extends ConsumerState { ref.listen(localeProvider, (previous, next) { final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null + setState(() { + isLoading = true; + print('isLoadingref $isLoading'); + }); // fetchChartData(widget.dataSets, localeCode); print('Saving currentTab: $tabWiseKpi before locale change'); @@ -2174,28 +2178,29 @@ class _ChartScreen1State extends ConsumerState { canPop: false, // Allow back navigation only if not login screen onPopInvokedWithResult: (didPop, result) { if (didPop) return; - context.go('/uaenumbers'); // Show exit confirmation dialog + context.pop(); + // context.go('/uaenumbers'); // Show exit confirmation dialog }, child: BaseScaffold( - key: _scaffoldKey, - title: Text( - context.translate( - 'UAE Numbers', - 'أرقام الإمارات', + key: _scaffoldKey, + title: Text( + context.translate( + 'UAE Numbers', + 'أرقام الإمارات', + ), ), - ), - // appbarColor: Color(int.parse(widget.bgColor)), // Example color - appbarColor: Color(int.parse( - (chartScreenData['header_color'] ?? '#ffffff') - .replaceFirst('#', '0xff'))), - // Example color - showBackButton: true, - colorChange: true, - navBackArrow: Text(widget.keyParam ?? 'Default Value'), - body: isLoading - ? Center(child: CircularProgressIndicator()) - : Container( + // appbarColor: Color(int.parse(widget.bgColor)), // Example color + appbarColor: Color(int.parse( + (chartScreenData['header_color'] ?? '#ffffff') + .replaceFirst('#', '0xff'))), + // Example color + showBackButton: true, + colorChange: true, + navBackArrow: Text(widget.keyParam ?? 'Default Value'), + body: Stack(children: [ + if (!isLoading) + Container( color: color, child: Column( children: [ @@ -2599,7 +2604,27 @@ class _ChartScreen1State extends ConsumerState { ], ), ), - ), + if (isLoading) + Container( + color: Color(0x98FFFCE5), // Semi-transparent background + 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 + ), + ), + ], + ), + ) + ])), ); } 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 42d611d6..68174edc 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 @@ -230,7 +230,8 @@ class _MyHomePageState extends ConsumerState { MainAxisAlignment.spaceEvenly, // Space buttons evenly children: [ SizedBox( - width: 120, // Set button width + width: + MediaQuery.of(context).size.width * 0.3, // Set button width child: TextButton( onPressed: () => Navigator.pop(dialogContext), style: TextButton.styleFrom( @@ -252,7 +253,8 @@ class _MyHomePageState extends ConsumerState { ), ), SizedBox( - width: 120, // Set button width + width: + MediaQuery.of(context).size.width * 0.3, // Set button width child: TextButton( onPressed: () => logout(context), style: TextButton.styleFrom( @@ -262,11 +264,16 @@ class _MyHomePageState extends ConsumerState { borderRadius: BorderRadius.circular(10.0), ), ), + child: FittedBox( + fit: BoxFit + .scaleDown, // Prevents wrapping while adjusting text size child: Text( context.translate('Log Out', 'تسجيل الخروج'), style: TextStyle(color: Colors.white, fontSize: 16), + textAlign: TextAlign.center, ), ), + ), ), ], ), @@ -1147,6 +1154,9 @@ class EconomyStatsState extends ConsumerState { Widget build(BuildContext context) { ref.listen(localeProvider, (previous, next) { final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null + setState(() { + isLoading = true; + }); fetchData(localeCode); }); @@ -1163,7 +1173,24 @@ class EconomyStatsState extends ConsumerState { double mywidth = MediaQuery.of(context).size.width; if (isLoading) { - return Center(child: CircularProgressIndicator()); + return Container( + color: Color(0x98FFFCE5), // Semi-transparent background + 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 + ), + ), + ], + ), + ); } else if (data.isEmpty) { return Center(child: Text("No data available")); } @@ -1469,28 +1496,39 @@ class InfoCard extends StatelessWidget { mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ - Flexible( - fit: FlexFit.loose, - child: FittedBox( - fit: BoxFit.contain, // Adjust text to fit within the bounds - child: Padding( - // padding: const EdgeInsets.all(4.0), - padding: const EdgeInsets.only(top: 4, bottom: 0), - child: Text( - title, - style: const TextStyle( - // fontSize: 11, - fontSize: 11 * 1.1, - fontWeight: FontWeight.w400, - // color: Colors.black, - fontFamily: 'Roboto', - color: Color(0xFF000000), - ), - ), - ), + // Flexible( + // fit: FlexFit.loose, + // child: FittedBox( + // fit: BoxFit.contain, // Adjust text to fit within the bounds + // child: Padding( + // // padding: const EdgeInsets.all(4.0), + // padding: const EdgeInsets.only(top: 4, bottom: 0), + // child: Text( + // title, + // style: const TextStyle( + // // fontSize: 11, + // fontSize: 12 * 1.1, + // fontWeight: FontWeight.w500, + // // color: Colors.black, + // fontFamily: 'Roboto', + // color: Color(0xFF000000), + // ), + // ), + // ), + // ), + // ), + Text( + title, + style: const TextStyle( + // fontSize: 11, + fontSize: 12, + fontWeight: FontWeight.w400, + // color: Colors.black, + fontFamily: 'Roboto', + color: Color(0xFF000000), ), ), - const SizedBox(height: 0.5), + const SizedBox(height: 0.3), Flexible( fit: FlexFit.loose, @@ -1503,7 +1541,7 @@ class InfoCard extends StatelessWidget { textAlign: TextAlign.center, style: const TextStyle( // fontSize: 11, - fontSize: 11 * 1.1, + fontSize: 12 * 1.1, fontFamily: 'Roboto', fontWeight: FontWeight.w400, color: Color(0xFF8E8E8E), diff --git a/lib/presentation/routes/drawer_routes/Drawer Items/bookmark.dart b/lib/presentation/routes/drawer_routes/Drawer Items/bookmark.dart index f819202e..6b365650 100644 --- a/lib/presentation/routes/drawer_routes/Drawer Items/bookmark.dart +++ b/lib/presentation/routes/drawer_routes/Drawer Items/bookmark.dart @@ -187,6 +187,9 @@ class _BookMarkState extends ConsumerState { Future removeBookmark(bookmarkId) async { try { + setState(() { + isLoading = true; + }); final adminAuth = await _pb.admins .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com'); final adminToken = adminAuth.token; @@ -219,6 +222,10 @@ class _BookMarkState extends ConsumerState { duration: Duration(seconds: 2), ); print("Error removing bookmark: $e"); + } finally { + setState(() { + isLoading = false; + }); } } @@ -342,6 +349,9 @@ class _BookMarkState extends ConsumerState { ref.listen(localeProvider, (previous, next) { final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null + setState(() { + isLoading = true; + }); fetchBookmarks(localeCode); }); @@ -365,154 +375,179 @@ class _BookMarkState extends ConsumerState { title: Text( AppLocalizations.of(context)!.bookmarks, ), - body: Column( - children: [ - TabBarHeader( - tabs: tabs, - selectedIndex: selectedTabIndex, - onTabSelected: (index) { - setState(() { - selectedTabIndex = index; - }); - }, - ), - Expanded( - child: selectedTabIndex == 0 - ? (bookmarks.isNotEmpty - ? SingleChildScrollView( - child: Column( - children: List.generate(transformedList.length, (i) { - final mainTopic = transformedList[i]; - print('oustside1 $mainTopic'); - final list = List.from(mainTopic['SubTopic'] ?? []); - print('oustside2 $list'); - return CustomExpandableTile( - index: i, - isExpanded: expandedIndex == i, - onTap: (int index) { - // 🔹 Expecting an index - setState(() { - expandedIndex = - (expandedIndex == index) ? null : index; - }); - }, - title: mainTopic['main_topic'] ?? 'No Topic', - childWidget: Container( - decoration: BoxDecoration( - color: Colors.white, - // borderRadius: BorderRadius.all(Radius.circular(20)) - ), - padding: const EdgeInsets.all(16), - child: GridView.builder( - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - gridDelegate: - const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - crossAxisSpacing: 10.0, - mainAxisSpacing: 10.0, - mainAxisExtent: 100, - ), - itemCount: list.length, - itemBuilder: (context, index) { - return _buildBoxes( - mainTopic['SubTopic'][index], - context, - mainTopic['valueColor']); - }, - ), + body: isLoading + ? Container( + color: Color(0x98FFFCE5), // Semi-transparent background + 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 + ), + ), + ], + ), + ) + : Column( + children: [ + TabBarHeader( + tabs: tabs, + selectedIndex: selectedTabIndex, + onTabSelected: (index) { + setState(() { + selectedTabIndex = index; + }); + }, + ), + Expanded( + child: selectedTabIndex == 0 + ? (bookmarks.isNotEmpty + ? SingleChildScrollView( + child: Column( + children: + List.generate(transformedList.length, (i) { + final mainTopic = transformedList[i]; + print('oustside1 $mainTopic'); + final list = + List.from(mainTopic['SubTopic'] ?? []); + print('oustside2 $list'); + return CustomExpandableTile( + index: i, + isExpanded: expandedIndex == i, + onTap: (int index) { + // 🔹 Expecting an index + setState(() { + expandedIndex = (expandedIndex == index) + ? null + : index; + }); + }, + title: + mainTopic['main_topic'] ?? 'No Topic', + childWidget: Container( + decoration: BoxDecoration( + color: Colors.white, + // borderRadius: BorderRadius.all(Radius.circular(20)) + ), + padding: const EdgeInsets.all(16), + child: GridView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + crossAxisSpacing: 10.0, + mainAxisSpacing: 10.0, + mainAxisExtent: 100, + ), + itemCount: list.length, + itemBuilder: (context, index) { + return _buildBoxes( + mainTopic['SubTopic'][index], + context, + mainTopic['valueColor']); + }, + ), + ), + filteredBookmarks: + List.from(mainTopic['SubTopic'] ?? []), + titleBackgroundColor: + mainTopic['valueColor'], + // Ensure it's a new list + ); + }), ), - filteredBookmarks: - List.from(mainTopic['SubTopic'] ?? []), - titleBackgroundColor: mainTopic['valueColor'], - // Ensure it's a new list - ); - }), - ), - ) - : Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.info_outline, - size: 100, color: Colors.grey[400]), - SizedBox(height: 16), - Text( - context.translate('No BookMark Added', - 'لم يتم إضافة أي علامة مرجعية'), - style: - TextStyle(fontSize: 16, color: Colors.grey), - ), - ], - ), - )) - : filteredBookmarks.isEmpty - ? Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.info_outline, - size: 100, color: Colors.grey[400]), - SizedBox(height: 16), - Text( - 'No Bookmark is added', - style: - TextStyle(fontSize: 16, color: Colors.grey), - ), - ], - ), - ) - : Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - Container( - decoration: BoxDecoration( - color: tabs[selectedTabIndex]['color'], - borderRadius: - BorderRadius.all(Radius.circular(35))), - padding: const EdgeInsets.only( - left: 16, bottom: 5, top: 5, right: 10), - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, + ) + : Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, children: [ + Icon(Icons.info_outline, + size: 100, color: Colors.grey[400]), + SizedBox(height: 16), Text( - tabs[selectedTabIndex]['title'], + context.translate('No BookMark Added', + 'لم يتم إضافة أي علامة مرجعية'), style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.w600, - fontSize: 20, + fontSize: 16, color: Colors.grey), + ), + ], + ), + )) + : filteredBookmarks.isEmpty + ? Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.info_outline, + size: 100, color: Colors.grey[400]), + SizedBox(height: 16), + Text( + 'No Bookmark is added', + style: TextStyle( + fontSize: 16, color: Colors.grey), + ), + ], + ), + ) + : Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + Container( + decoration: BoxDecoration( + color: tabs[selectedTabIndex]['color'], + borderRadius: BorderRadius.all( + Radius.circular(35))), + padding: const EdgeInsets.only( + left: 16, bottom: 5, top: 5, right: 10), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + tabs[selectedTabIndex]['title'], + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.w600, + fontSize: 20, + ), + ), + ], + ), + ), + SizedBox( + height: 20, + ), + Expanded( + child: GridView.builder( + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + crossAxisSpacing: 10.0, + mainAxisSpacing: 10.0, + mainAxisExtent: 100, + ), + itemCount: filteredBookmarks.length, + itemBuilder: (context, index) { + return _buildBox( + filteredBookmarks[index], context); + }, ), ), ], ), ), - SizedBox( - height: 20, - ), - Expanded( - child: GridView.builder( - gridDelegate: - const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - crossAxisSpacing: 10.0, - mainAxisSpacing: 10.0, - mainAxisExtent: 100, - ), - itemCount: filteredBookmarks.length, - itemBuilder: (context, index) { - return _buildBox( - filteredBookmarks[index], context); - }, - ), - ), - ], - ), - ), - ), - ], - ), + ), + ], + ), ); } diff --git a/lib/presentation/routes/drawer_routes/Drawer Items/edit_profile.dart b/lib/presentation/routes/drawer_routes/Drawer Items/edit_profile.dart index f38fc5fa..965e96b8 100644 --- a/lib/presentation/routes/drawer_routes/Drawer Items/edit_profile.dart +++ b/lib/presentation/routes/drawer_routes/Drawer Items/edit_profile.dart @@ -31,7 +31,6 @@ class _EditProfileState extends ConsumerState { final _pb = PocketBase(apiUrl); // final _pb = PocketBase('http://127.0.0.1:8090'); bool _isProfileCompleted = false; - bool _isLoading = false; // Add focus nodes and hint states final List _focusNodes = List.generate(4, (_) => FocusNode()); @@ -85,7 +84,7 @@ class _EditProfileState extends ConsumerState { final _picker = ImagePicker(); File? _profileImage; String _avatarUrl = ''; - bool isPageLoad = false; + bool isLoader = true; // Regular expression to validate Full Name (no special characters) final RegExp _nameRegExp = RegExp(r'^[a-zA-Z\s]+$'); @@ -174,10 +173,6 @@ class _EditProfileState extends ConsumerState { Future _fetchUserData() async { try { - setState(() { - isPageLoad = true; // Show loader - print('Im isPageLoad'); - }); print('EDIT PROFILE isPageLoad'); final adminAuth = await _pb.admins .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com'); @@ -229,7 +224,7 @@ class _EditProfileState extends ConsumerState { } else { _avatarUrl = ''; // Reset to default or empty } - isPageLoad = false; + isLoader = false; }); } catch (e) { print('Error fetching user details: $e'); @@ -261,7 +256,7 @@ class _EditProfileState extends ConsumerState { await _picker.pickImage(source: ImageSource.gallery); if (pickedFile != null) { setState(() { - _isLoading = true; // Start loading + isLoader = true; // Start loading }); print(pickedFile); final String fileExtension = @@ -272,7 +267,7 @@ class _EditProfileState extends ConsumerState { fileExtension == 'png' || fileExtension == 'heic') { setState(() { - _isLoading = false; + isLoader = false; _profileImage = File(pickedFile.path); }); } else { @@ -283,7 +278,7 @@ class _EditProfileState extends ConsumerState { } setState(() { - _isLoading = false; // Hide loader + isLoader = false; // Hide loader }); } @@ -356,6 +351,9 @@ class _EditProfileState extends ConsumerState { } void showConfirmationDialog(BuildContext context) async { + setState(() { + isLoader = true; + }); // final result = await showDialog( // context: context, // builder: (context) => const ConfirmationDialog(), @@ -404,12 +402,18 @@ class _EditProfileState extends ConsumerState { // Handle response if (response.statusCode == 200) { _resetFormFields(); + setState(() { + isLoader = false; + }); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Profile updated successfully!')), ); // print('ShowConfirmation userData - $userData '); context.go('/myhomepage'); } else { + setState(() { + isLoader = false; + }); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Failed to update profile: ${response.statusCode}'), @@ -417,6 +421,9 @@ class _EditProfileState extends ConsumerState { ); } } catch (error) { + setState(() { + isLoader = false; + }); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Failed to update profile: $error')), ); @@ -424,6 +431,7 @@ class _EditProfileState extends ConsumerState { } else { // Show an error if the country is invalid setState(() { + isLoader = false; showError = true; }); } @@ -477,23 +485,40 @@ class _EditProfileState extends ConsumerState { AppLocalizations.of(context)!.my_profile, style: TextStyle(color: Color(0xFF985400)), ), - 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), - padding: const EdgeInsets.only( - top: 20.0, bottom: 20.0, left: 25, right: 25), - child: isPageLoad - ? Center( - child: CircularProgressIndicator(), - ) - : Column( + body: isLoader + ? Container( + color: Color(0x98FFFCE5), // Semi-transparent background + 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 + ), + ), + ], + ), + ) + : 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), + padding: const EdgeInsets.only( + top: 20.0, bottom: 20.0, left: 25, right: 25), + child: Column( children: [ // CircleAvatar( // radius: 50, @@ -643,16 +668,6 @@ class _EditProfileState extends ConsumerState { ), ), ), - if (_isLoading) - Positioned( - child: CircularProgressIndicator( - strokeWidth: 2, - valueColor: - AlwaysStoppedAnimation( - Colors.grey, - ), - ), - ), ], ), @@ -1150,13 +1165,13 @@ class _EditProfileState extends ConsumerState { ) ], ), - ), + ), + ), + ), + ], ), ), - ], - ), - ), - ), + ), //bottomNavigationBar: MyBottomNavBar(), )); } diff --git a/lib/presentation/routes/drawer_routes/Drawer Items/feedback.dart b/lib/presentation/routes/drawer_routes/Drawer Items/feedback.dart index 8aad2a5c..48e17de2 100644 --- a/lib/presentation/routes/drawer_routes/Drawer Items/feedback.dart +++ b/lib/presentation/routes/drawer_routes/Drawer Items/feedback.dart @@ -32,6 +32,7 @@ class FeedbackForm extends ConsumerStatefulWidget { 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 @@ -277,17 +278,37 @@ class _FeedbackFormState extends ConsumerState // ), // ], // ), - body: Padding( - padding: const EdgeInsets.only( - top: 8.0, bottom: 16.0, left: 23.0, right: 23.0), - child: SingleChildScrollView( - child: _isFeedbackSubmitted - ? _buildThankYouMessage(userName) - : _isFeedbackFailed - ? _buildFailureMessage() - : _buildFeedbackForm(), - ), - ), + body: isLoading + ? Container( + color: Color(0x98FFFCE5), // Semi-transparent background + 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) + : _isFeedbackFailed + ? _buildFailureMessage() + : _buildFeedbackForm(), + ), + ), )); } @@ -747,6 +768,9 @@ class _FeedbackFormState extends ConsumerState } Future _submitFeedback() async { + setState(() { + isLoading = true; + }); if (_selectedEmojiIndex == null) { setState(() { _isSmileySelected = false; @@ -802,6 +826,7 @@ class _FeedbackFormState extends ConsumerState _isFeedbackSubmitted = true; _isFeedbackFailed = false; _resetFeedbackForm(); + isLoading = false; }); } } catch (e) { diff --git a/lib/presentation/routes/drawer_routes/Drawer Items/manage_users.dart b/lib/presentation/routes/drawer_routes/Drawer Items/manage_users.dart index 4761e519..8e712ef1 100644 --- a/lib/presentation/routes/drawer_routes/Drawer Items/manage_users.dart +++ b/lib/presentation/routes/drawer_routes/Drawer Items/manage_users.dart @@ -57,7 +57,6 @@ class _ManageUserRouterState extends ConsumerState { ); setState(() { - isLoading = true; userData = result.map((record) { final createdDate = DateTime.parse(record.created) .add(Duration(hours: 4)); // Parse the created date @@ -82,6 +81,7 @@ class _ManageUserRouterState extends ConsumerState { isLoading = false; }); } catch (e) { + isLoading = false; print('Error fetching unverified users: $e'); } } @@ -123,6 +123,43 @@ class _ManageUserRouterState extends ConsumerState { }); } + Future changeStatus(userID, newStatus) async { + try { + setState(() { + isLoading = true; + }); + // Update status in PocketBase + await _pb.collection('users').update( + userID, // User's unique ID + body: { + 'status': newStatus, // Update status + 'verified': newStatus == 'Approved' ? true : false, + 'reviewed': true, + }, + ); + + // Update local state + // setState(() { + // user.status = newStatus; + // }); + fetchUnverifiedUsers(); + setState(() { + isLoading = false; + }); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Status updated successfully!')), + ); + } catch (e) { + setState(() { + isLoading = false; + }); + Navigator.of(context).pop(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Error updating status: $e')), + ); + } + } + //Method to show a confirmation dialog when status is changed Future _showConfirmationDialog(User user, String newStatus) async { print('user $user'); @@ -219,32 +256,9 @@ class _ManageUserRouterState extends ConsumerState { width: 100, // Set the desired width child: TextButton( onPressed: () async { - try { - // Update status in PocketBase - await _pb.collection('users').update( - user.id, // User's unique ID - body: { - 'status': newStatus, // Update status - 'verified': newStatus == 'Approved' ? true : false, - 'reviewed': true, - }, - ); - - // 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 + Navigator.of(context).pop(); + changeStatus(user.id, newStatus); + // Close dialog }, style: TextButton.styleFrom( backgroundColor: @@ -297,249 +311,273 @@ class _ManageUserRouterState extends ConsumerState { title: Text( AppLocalizations.of(context)!.manage_user, ), - 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)), - // ), - // ), - // onChanged: filterUsers, - // ), - // Container( - // height: 40, - // decoration: BoxDecoration( - // color: Colors.white, - // borderRadius: BorderRadius.circular(30.0), - // border: Border.all(width: 2, color: Color(0xFFAA8E83)), - // ), - // child: TextField( - // decoration: InputDecoration( - // hintText: AppLocalizations.of(context)!.search, - // hintStyle: TextStyle(color: Color(0xFFC3C6CB)), - // prefixIcon: Image.asset( - // MiscIconAssetPath.search, - // width: 20, - // height: 20, - // ), - // // ,prefixIcon: Icon( - // // Icons.search, - // // color: Color(0xFFAA8E83), - // // ), - // border: InputBorder.none, - // contentPadding: EdgeInsets.symmetric( - // vertical: 15.0, horizontal: 20.0), - // ), - // onChanged: filterUsers, - // ), - // ), - Container( - height: 40, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(30.0), - border: Border.all(width: 1, color: Color(0xFFAA8E83)), - ), - child: Padding( - padding: EdgeInsets.only(left: 12.0), - child: TextField( - decoration: InputDecoration( - hintText: AppLocalizations.of(context)!.search, - hintStyle: TextStyle(color: Color(0xFFAA8E83)), - // hintStyle: TextStyle(color: Color(0xFFAA8E83)), - prefixIconConstraints: - BoxConstraints(maxWidth: 42, maxHeight: 42), - prefixIcon: Container( - padding: EdgeInsets.only(right: 5), - child: SvgPicture.asset( - MiscIconAssetPath.Search, - semanticsLabel: 'Search', - colorFilter: ColorFilter.mode( - Color(0xFFAA8E83), BlendMode.srcIn), + body: isLoading + ? Container( + color: Color(0x98FFFCE5), // Semi-transparent background + 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 + ), + ), + ], + ), + ) + : 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)), + // ), + // ), + // onChanged: filterUsers, + // ), + // Container( + // height: 40, + // decoration: BoxDecoration( + // color: Colors.white, + // borderRadius: BorderRadius.circular(30.0), + // border: Border.all(width: 2, color: Color(0xFFAA8E83)), + // ), + // child: TextField( + // decoration: InputDecoration( + // hintText: AppLocalizations.of(context)!.search, + // hintStyle: TextStyle(color: Color(0xFFC3C6CB)), + // prefixIcon: Image.asset( + // MiscIconAssetPath.search, + // width: 20, + // height: 20, + // ), + // // ,prefixIcon: Icon( + // // Icons.search, + // // color: Color(0xFFAA8E83), + // // ), + // border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric( + // vertical: 15.0, horizontal: 20.0), + // ), + // onChanged: filterUsers, + // ), + // ), + Container( + height: 40, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(30.0), + border: + Border.all(width: 1, color: Color(0xFFAA8E83)), + ), + child: Padding( + padding: EdgeInsets.only(left: 12.0), + child: TextField( + decoration: InputDecoration( + hintText: AppLocalizations.of(context)!.search, + hintStyle: TextStyle(color: Color(0xFFAA8E83)), + // hintStyle: TextStyle(color: Color(0xFFAA8E83)), + prefixIconConstraints: + BoxConstraints(maxWidth: 42, maxHeight: 42), + prefixIcon: Container( + padding: EdgeInsets.only(right: 5), + child: SvgPicture.asset( + MiscIconAssetPath.Search, + semanticsLabel: 'Search', + colorFilter: ColorFilter.mode( + Color(0xFFAA8E83), BlendMode.srcIn), + ), + ), + + // prefixIcon: Image.asset( + // MiscIconAssetPath.search, + // // fit: BoxFit.contain, + // ), + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric( + vertical: 0.5, horizontal: 18.0), + ), + onChanged: filterUsers, ), ), - - // prefixIcon: Image.asset( - // MiscIconAssetPath.search, - // // fit: BoxFit.contain, - // ), - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - vertical: 0.5, horizontal: 18.0), ), - onChanged: filterUsers, - ), - ), - ), - SizedBox(height: myheight / 40), - isLoading - ? Center(child: CircularProgressIndicator()) - : filteredUserData.isEmpty - ? Center( - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox(height: myheight / 5), + SizedBox(height: myheight / 40), + filteredUserData.isEmpty + ? Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(height: myheight / 5), - // Icon(Icons.search, - // size: 60, color: Colors.grey), - Image.asset( - MiscIconAssetPath.group, - width: 60, - height: 60, - ), - - SizedBox(height: 15), - // Space between icon and text - Text( - "No results found", - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: Colors.grey[700], + // Icon(Icons.search, + // size: 60, color: Colors.grey), + Image.asset( + MiscIconAssetPath.group, + width: 60, + height: 60, ), - ), - SizedBox(height: 12), // Space between texts - FittedBox( - child: Text( - "We couldn't find anything matching your search.", + SizedBox(height: 15), + // Space between icon and text + Text( + "No results found", style: TextStyle( - fontSize: 18, - color: Color(0xFF898C81)), - textAlign: TextAlign.center, + fontSize: 24, + fontWeight: FontWeight.bold, + color: Colors.grey[700], + ), ), - ), - ], + + SizedBox(height: 12), // Space between texts + FittedBox( + child: Text( + "We couldn't find anything matching your search.", + style: TextStyle( + fontSize: 18, + color: Color(0xFF898C81)), + textAlign: TextAlign.center, + ), + ), + ], + ), ), - ), - ) - : SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: ConstrainedBox( - constraints: BoxConstraints( - minWidth: MediaQuery.of(context).size.width, - ), - child: DataTable( - sortColumnIndex: _sortColumnIndex, - sortAscending: _isAscending, - columns: [ - DataColumn( - label: Text( - AppLocalizations.of(context)!.user_name, + ) + : SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: ConstrainedBox( + constraints: BoxConstraints( + minWidth: MediaQuery.of(context).size.width, + ), + child: DataTable( + sortColumnIndex: _sortColumnIndex, + sortAscending: _isAscending, + columns: [ + DataColumn( + label: Text( + AppLocalizations.of(context)!.user_name, + ), + onSort: (columnIndex, ascending) { + _sort( + (user) => + user.userName.toLowerCase(), + columnIndex, + ascending); + }, ), - onSort: (columnIndex, ascending) { - _sort( - (user) => user.userName.toLowerCase(), + DataColumn( + label: Text( + AppLocalizations.of(context)!.email_id, + ), + onSort: (columnIndex, ascending) { + _sort( + (user) => + user.emailId.toLowerCase(), + columnIndex, + ascending); + }, + ), + DataColumn( + label: Text( + AppLocalizations.of(context)!.reg_date, + ), + onSort: (columnIndex, ascending) { + _sort( + (user) => DateFormat('dd/MM/yyyy') + .parse(user.registrationDate), columnIndex, - ascending); - }, - ), - DataColumn( - label: Text( - AppLocalizations.of(context)!.email_id, + ascending, + ); + }, ), - onSort: (columnIndex, ascending) { - _sort( - (user) => user.emailId.toLowerCase(), - columnIndex, - ascending); - }, - ), - DataColumn( - label: Text( - AppLocalizations.of(context)!.reg_date, + DataColumn( + label: Text( + AppLocalizations.of(context)!.status, + ), + onSort: (columnIndex, ascending) { + _sort( + (user) => user.status.toLowerCase(), + columnIndex, + ascending); + }, ), - onSort: (columnIndex, ascending) { - _sort( - (user) => DateFormat('dd/MM/yyyy') - .parse(user.registrationDate), - columnIndex, - ascending, - ); - }, - ), - DataColumn( - label: Text( - AppLocalizations.of(context)!.status, - ), - onSort: (columnIndex, ascending) { - _sort((user) => user.status.toLowerCase(), - columnIndex, ascending); - }, - ), - ], - rows: filteredUserData.isEmpty - ? [ - DataRow( - cells: List.generate( - 4, // Ensure it matches the number of DataColumns - (index) => DataCell( - index == 0 - ? Text( - 'No results found', - style: TextStyle( - fontStyle: - FontStyle.italic), - ) - : const Text( - ''), // Empty cells for other columns - placeholder: true, - ), - ), - ), - ] - : filteredUserData.map((user) { - return DataRow( - cells: [ - DataCell(Text(user.userName)), - DataCell(Text(user.emailId)), - DataCell( - Text(user.registrationDate)), - DataCell( - DropdownButton( - value: user.status, - items: statusOptions.entries - .map((status) { - return DropdownMenuItem< - String>( - value: status.key, - child: Text( - status.value, - style: TextStyle( - color: getStatusColor( - status.key), - ), - ), - ); - }).toList(), - onChanged: (newStatus) { - print(user); - if (newStatus != null) { - _showConfirmationDialog( - user, newStatus); - } - }, + ], + rows: filteredUserData.isEmpty + ? [ + DataRow( + cells: List.generate( + 4, // Ensure it matches the number of DataColumns + (index) => DataCell( + index == 0 + ? Text( + 'No results found', + style: TextStyle( + fontStyle: FontStyle + .italic), + ) + : const Text( + ''), // Empty cells for other columns + placeholder: true, ), ), - ], - ); - }).toList(), + ), + ] + : filteredUserData.map((user) { + return DataRow( + cells: [ + DataCell(Text(user.userName)), + DataCell(Text(user.emailId)), + DataCell( + Text(user.registrationDate)), + DataCell( + DropdownButton( + value: user.status, + items: statusOptions.entries + .map((status) { + return DropdownMenuItem< + String>( + value: status.key, + child: Text( + status.value, + style: TextStyle( + color: getStatusColor( + status.key), + ), + ), + ); + }).toList(), + onChanged: (newStatus) { + print(user); + if (newStatus != null) { + _showConfirmationDialog( + user, newStatus); + } + }, + ), + ), + ], + ); + }).toList(), + ), ), ), - ), - ], - ), - ), - ), + ], + ), + ), + ), ), ); } diff --git a/lib/presentation/routes/drawer_routes/Drawer Items/notification/notification.dart b/lib/presentation/routes/drawer_routes/Drawer Items/notification/notification.dart index 4f5afc62..c1970296 100644 --- a/lib/presentation/routes/drawer_routes/Drawer Items/notification/notification.dart +++ b/lib/presentation/routes/drawer_routes/Drawer Items/notification/notification.dart @@ -22,6 +22,7 @@ class NotificationPage extends ConsumerStatefulWidget { class _NotificationPageState extends ConsumerState { // Example notification data final _pb = PocketBase(apiUrl); + bool isLoading = true; List> notifications = []; List pushedNotification = []; @@ -109,15 +110,25 @@ class _NotificationPageState extends ConsumerState { notifications = List>.from( jsonResponse['data']); // Assign decoded data print('notifications $notifications'); + isLoading = false; }); } else { + setState(() { + isLoading = false; + }); throw Exception('Failed to load data'); } } else { + setState(() { + isLoading = false; + }); throw Exception( 'Failed to load data with status code ${response.statusCode}'); } } catch (e) { + setState(() { + isLoading = false; + }); print('Error fetching data: $e'); } } @@ -132,6 +143,9 @@ class _NotificationPageState extends ConsumerState { ref.listen(localeProvider, (previous, next) { final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null + setState(() { + isLoading = true; + }); fetchNotifications(localeCode); }); final List> tabs = [ @@ -164,60 +178,83 @@ class _NotificationPageState extends ConsumerState { 'إشعار', ), ), - body: Column( - children: [ - TabBarHeader( - tabs: tabs.map((tab) => tab['title'] as String).toList(), - selectedIndex: selectedTabIndex, - onTabSelected: (index) { - setState(() { - selectedTabIndex = index; - }); - }, - ), - Expanded( - child: filteredNotifications.isEmpty - ? Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.info_outline, - size: 100, color: Colors.grey[400]), - SizedBox(height: 16), - Text( - 'No notifications available.', - style: TextStyle(fontSize: 16, color: Colors.grey), - ), - ], + body: isLoading + ? Container( + color: Color(0x98FFFCE5), // Semi-transparent background + 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 ), - ) - : ListView.builder( - itemCount: filteredNotifications.length, - itemBuilder: (context, index) { - final notification = filteredNotifications[index]; - return Container( - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: Color(0xFFDEDEDE), - width: 1), // Bottom border only - ), - ), - child: NotificationTile( - title: notification['title'] ?? 'No Title', - date: formatDate(notification['created'] ?? ''), - category: notification['category'] ?? 'Unknown', - message: notification['message'] ?? '', - id: notification['id'], - pushedNotification: pushedNotification, - userID: userID, - ), - ); - }, ), - ), - ], - ), + ], + ), + ) + : Column( + children: [ + TabBarHeader( + tabs: tabs.map((tab) => tab['title'] as String).toList(), + selectedIndex: selectedTabIndex, + onTabSelected: (index) { + setState(() { + selectedTabIndex = index; + }); + }, + ), + Expanded( + child: filteredNotifications.isEmpty + ? Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.info_outline, + size: 100, color: Colors.grey[400]), + SizedBox(height: 16), + Text( + 'No notifications available.', + style: TextStyle( + fontSize: 16, color: Colors.grey), + ), + ], + ), + ) + : ListView.builder( + itemCount: filteredNotifications.length, + itemBuilder: (context, index) { + final notification = filteredNotifications[index]; + return Container( + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Color(0xFFDEDEDE), + width: 1), // Bottom border only + ), + ), + child: NotificationTile( + title: notification['title'] ?? 'No Title', + date: + formatDate(notification['created'] ?? ''), + category: + notification['category'] ?? 'Unknown', + message: notification['message'] ?? '', + id: notification['id'], + pushedNotification: pushedNotification, + userID: userID, + ), + ); + }, + ), + ), + ], + ), ), ); } diff --git a/lib/presentation/routes/drawer_routes/Drawer Items/notification/notification_details.dart b/lib/presentation/routes/drawer_routes/Drawer Items/notification/notification_details.dart index 3e698687..a23b4b43 100644 --- a/lib/presentation/routes/drawer_routes/Drawer Items/notification/notification_details.dart +++ b/lib/presentation/routes/drawer_routes/Drawer Items/notification/notification_details.dart @@ -37,6 +37,7 @@ class _NotificationDetailsState extends ConsumerState { // Example notification data final _pb = PocketBase(apiUrl); Map? notification; + bool isLoading = true; // Current selected tab index int selectedTabIndex = 0; @@ -77,6 +78,7 @@ class _NotificationDetailsState extends ConsumerState { setState(() { final data = jsonResponse['data']; notification = data; + isLoading = false; // if (data is Map) { // notification = data; // } else { @@ -86,13 +88,22 @@ class _NotificationDetailsState extends ConsumerState { print('notification $notification'); }); } else { + setState(() { + isLoading = false; + }); throw Exception('Failed to load data'); } } else { + setState(() { + isLoading = false; + }); throw Exception( 'Failed to load data with status code ${response.statusCode}'); } } catch (e) { + setState(() { + isLoading = false; + }); print('Error fetching data: $e'); } } @@ -107,6 +118,9 @@ class _NotificationDetailsState extends ConsumerState { ref.listen(localeProvider, (previous, next) { final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null + setState(() { + isLoading = true; + }); fetchNotifications(localeCode); }); @@ -125,53 +139,73 @@ class _NotificationDetailsState extends ConsumerState { ), showBackButton: true, navBackArrow: Text(widget.backNavigation ?? 'Default Value'), - body: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - notification?['title'] ?? '', - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.w500, - color: Color(0xFF414042)), - ), - const SizedBox(height: 20), - Center( + body: isLoading + ? Container( + color: Color(0x98FFFCE5), // Semi-transparent background child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Image.asset( - notification?['category'] == 'App updates' - ? 'assets/backgrounds/Notification/App-Update.png' - : 'assets/backgrounds/Notification/Update_notific.png', - height: 200, + 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.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + notification?['title'] ?? '', + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + color: Color(0xFF414042)), + ), + const SizedBox(height: 20), + Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset( + notification?['category'] == 'App updates' + ? 'assets/backgrounds/Notification/App-Update.png' + : 'assets/backgrounds/Notification/Update_notific.png', + height: 200, + ), + ], + ), + ), + const SizedBox(height: 20), + Text( + notification?['message'] ?? + "No additional details available.", + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: Color(0xFF414042)), + ), + const SizedBox(height: 10), + Text( + "Date: ${notification != null ? formatDate(notification!['created'] ?? '') : 'N/A'}", + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.w400, + color: Color(0xFF8E8E8E), + ), ), ], ), ), - const SizedBox(height: 20), - Text( - notification?['message'] ?? - "No additional details available.", - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.w400, - color: Color(0xFF414042)), - ), - const SizedBox(height: 10), - Text( - "Date: ${notification != null ? formatDate(notification!['created'] ?? '') : 'N/A'}", - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.w400, - color: Color(0xFF8E8E8E), - ), - ), - ], - ), - ), )); } } diff --git a/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart b/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart index ebf6158a..1bfaa193 100644 --- a/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart +++ b/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart @@ -1244,9 +1244,14 @@ void _showLogoutConfirmationDialog(BuildContext context) { borderRadius: BorderRadius.circular(10.0), ), ), - child: Text( - context.translate('Log Out', 'تسجيل الخروج'), - style: TextStyle(color: Colors.white, fontSize: 16), + child: FittedBox( + fit: BoxFit + .scaleDown, // Prevents wrapping while adjusting text size + child: Text( + context.translate('Log Out', 'تسجيل الخروج'), + style: TextStyle(color: Colors.white, fontSize: 16), + textAlign: TextAlign.center, + ), ), ), ), diff --git a/pubspec.yaml b/pubspec.yaml index dd76f936..6d61e24f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: uae_stat description: "View statistics about the UAE from the Federal Center for Statistics and Competitiveness." publish_to: "none" -version: 1.0.35+36 +version: 1.0.36+37 environment: sdk: ">=3.2.3 <4.0.0"