loader added all page

This commit is contained in:
venbaittech 2025-03-15 17:32:05 +05:30
parent ef761a8d0a
commit f6de75f0c8
15 changed files with 2461 additions and 1440 deletions

View File

@ -20,12 +20,12 @@ if (project.hasProperty('google-services.json')) {
def flutterVersionCode = localProperties.getProperty("flutter.versionCode") def flutterVersionCode = localProperties.getProperty("flutter.versionCode")
if (flutterVersionCode == null) { if (flutterVersionCode == null) {
flutterVersionCode = "36" flutterVersionCode = "37"
} }
def flutterVersionName = localProperties.getProperty("flutter.versionName") def flutterVersionName = localProperties.getProperty("flutter.versionName")
if (flutterVersionName == null) { if (flutterVersionName == null) {
flutterVersionName = "1.0.35" flutterVersionName = "1.0.36"
} }
def keystorePropertiesFile = rootProject.file("key.properties") def keystorePropertiesFile = rootProject.file("key.properties")
@ -72,6 +72,7 @@ android {
// Signing with the debug keys for now, so `flutter run --release` works. // Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.release signingConfig signingConfigs.release
minifyEnabled true // Enable code shrinking for smaller APKs minifyEnabled true // Enable code shrinking for smaller APKs
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
} }

View File

@ -182,12 +182,38 @@ class _UaenumberWidgetState extends ConsumerState<uaenumberWidget> {
ref.listen<Locale?>(localeProvider, (previous, next) { ref.listen<Locale?>(localeProvider, (previous, next) {
final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null
setState(() {
isLoading = true;
});
fetchData(localeCode); fetchData(localeCode);
}); });
double myheight = MediaQuery.of(context).size.height; double myheight = MediaQuery.of(context).size.height;
double mywidth = MediaQuery.of(context).size.width; double mywidth = MediaQuery.of(context).size.width;
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>(
Color(0xFFAA8E83)), // Loader color
),
),
],
),
);
} else if (filteredData.isEmpty) {
return Center(child: Text("No data available"));
}
return Padding( return Padding(
padding: padding:
const EdgeInsets.only(left: 16.0, right: 16.0, top: 5.0, bottom: 5.0), const EdgeInsets.only(left: 16.0, right: 16.0, top: 5.0, bottom: 5.0),

View File

@ -3,10 +3,12 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:pocketbase/pocketbase.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/api_config.dart';
import 'package:uae_stat/config/connectivity_provider.dart'; import 'package:uae_stat/config/connectivity_provider.dart';
import 'package:uae_stat/config/my_router.dart'; import 'package:uae_stat/config/my_router.dart';
import 'package:uae_stat/domain/use_cases/language.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/banner_asset_path.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/misc_icon_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/Screens/profilepage.dart';
@ -34,6 +36,7 @@ class CreateNewPw extends ConsumerStatefulWidget {
class _CreateNewPwState extends ConsumerState<CreateNewPw> { class _CreateNewPwState extends ConsumerState<CreateNewPw> {
final _pb = PocketBase(apiUrl); final _pb = PocketBase(apiUrl);
bool isLoading = false;
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
bool _obscureOldPassword = true; bool _obscureOldPassword = true;
bool _obscureNewPassword = true; bool _obscureNewPassword = true;
@ -139,6 +142,9 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
Future<void> updatePassword( Future<void> updatePassword(
String userId, String oldPassword, String newPassword) async { String userId, String oldPassword, String newPassword) async {
try { try {
setState(() {
isLoading = true;
});
// Authenticate as admin // Authenticate as admin
final adminAuth = await _pb.admins final adminAuth = await _pb.admins
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com'); .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
@ -164,6 +170,11 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
headers: headers, headers: headers,
); );
setState(() {
isLoading = false;
_isPasswordUpdated = true;
});
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text( content: Text(
@ -172,9 +183,7 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
), ),
); );
setState(() { print('_isPasswordUpdated $_isPasswordUpdated');
_isPasswordUpdated = true; // Toggle UI on success
});
// Navigator.push( // Navigator.push(
// context, // context,
@ -183,6 +192,7 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
// ); // );
} else { } else {
setState(() { setState(() {
isLoading = false;
_formKey.currentState?.validate(); _formKey.currentState?.validate();
}); });
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
@ -195,6 +205,9 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
return; return;
} }
} catch (e) { } catch (e) {
setState(() {
isLoading = false;
});
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text('Failed to update password: $e'), content: Text('Failed to update password: $e'),
@ -204,6 +217,15 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
} }
} }
final PAuthRepo _authRepo = PAuthRepo();
Future<void> logout(BuildContext context) async {
final prefs = await SharedPreferences.getInstance();
await _authRepo.logout();
prefs.clear();
context.go('/login'); // Redirect to login after logout
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
double screenHeight = MediaQuery.of(context).size.height; double screenHeight = MediaQuery.of(context).size.height;
@ -216,15 +238,36 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
}); });
return Scaffold( return Scaffold(
backgroundColor: Colors.white, backgroundColor: Colors.white,
body: SingleChildScrollView( body: Stack(children: [
child: SafeArea( SingleChildScrollView(
child: _isPasswordUpdated child: SafeArea(
? _buildSuccessContent(screenHeight, screenWidth) child: _isPasswordUpdated
: _buildPasswordForm(ref, screenHeight, screenWidth), ? _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>(
Color(0xFFAA8E83)), // Loader color
),
),
],
),
)
]));
} }
Widget _buildPasswordForm( Widget _buildPasswordForm(
@ -626,8 +669,9 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
} }
Widget _buildSuccessContent(double screenHeight, double screenWidth) { Widget _buildSuccessContent(double screenHeight, double screenWidth) {
print('IN');
return Padding( return Padding(
padding: const EdgeInsets.all(20.0), padding: EdgeInsets.all(20.0),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
@ -647,16 +691,14 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
), ),
SizedBox(height: 20), SizedBox(height: 20),
ElevatedButton( ElevatedButton(
onPressed: () async { onPressed: () => logout(context),
context.go('/login');
},
style: ButtonStyle( style: ButtonStyle(
shape: WidgetStatePropertyAll( shape: WidgetStatePropertyAll(
RoundedRectangleBorder( RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
), ),
padding: const WidgetStatePropertyAll( padding: WidgetStatePropertyAll(
EdgeInsets.symmetric(vertical: 10.5), EdgeInsets.symmetric(vertical: 10.5),
), ),
textStyle: WidgetStatePropertyAll( textStyle: WidgetStatePropertyAll(
@ -675,7 +717,7 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
// surfaceTintColor: MaterialStatePropertyAll( // surfaceTintColor: MaterialStatePropertyAll(
// MyTheme.economy[800], // MyTheme.economy[800],
// ), // ),
foregroundColor: const WidgetStatePropertyAll( foregroundColor: WidgetStatePropertyAll(
Colors.white, Colors.white,
), ),
), ),
@ -697,12 +739,12 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
), ),
), ),
6.horizontalSpace, 6.horizontalSpace,
const Icon(Icons.chevron_right_outlined, color: Colors.white), Icon(Icons.chevron_right_outlined, color: Colors.white),
], ],
), ),
), ),
SizedBox(height: screenHeight / 2.2), SizedBox(height: screenHeight / 2.2),
Spacer(), // Spacer(),
Center( Center(
// child: Container( // child: Container(
// height: 40, // height: 40,

View File

@ -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<ForgotPassword> createState() => _ForgotPasswordState();
}
class _ForgotPasswordState extends ConsumerState<ForgotPassword> {
final _pb = PocketBase(apiUrl);
bool isLoading = false;
final _formKey = GlobalKey<FormState>();
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<String> 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<void> 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<void> 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<AsyncValue<bool>>(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>(
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,
),
],
),
);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -2110,6 +2110,10 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
ref.listen<Locale?>(localeProvider, (previous, next) { ref.listen<Locale?>(localeProvider, (previous, next) {
final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null
setState(() {
isLoading = true;
print('isLoadingref $isLoading');
});
// fetchChartData(widget.dataSets, localeCode); // fetchChartData(widget.dataSets, localeCode);
print('Saving currentTab: $tabWiseKpi before locale change'); print('Saving currentTab: $tabWiseKpi before locale change');
@ -2174,28 +2178,29 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
canPop: false, // Allow back navigation only if not login screen canPop: false, // Allow back navigation only if not login screen
onPopInvokedWithResult: (didPop, result) { onPopInvokedWithResult: (didPop, result) {
if (didPop) return; if (didPop) return;
context.go('/uaenumbers'); // Show exit confirmation dialog context.pop();
// context.go('/uaenumbers'); // Show exit confirmation dialog
}, },
child: BaseScaffold( child: BaseScaffold(
key: _scaffoldKey, key: _scaffoldKey,
title: Text( title: Text(
context.translate( context.translate(
'UAE Numbers', 'UAE Numbers',
'أرقام الإمارات', 'أرقام الإمارات',
),
), ),
), // appbarColor: Color(int.parse(widget.bgColor)), // Example color
// appbarColor: Color(int.parse(widget.bgColor)), // Example color appbarColor: Color(int.parse(
appbarColor: Color(int.parse( (chartScreenData['header_color'] ?? '#ffffff')
(chartScreenData['header_color'] ?? '#ffffff') .replaceFirst('#', '0xff'))),
.replaceFirst('#', '0xff'))), // Example color
// Example color showBackButton: true,
showBackButton: true, colorChange: true,
colorChange: true, navBackArrow: Text(widget.keyParam ?? 'Default Value'),
navBackArrow: Text(widget.keyParam ?? 'Default Value'), body: Stack(children: [
body: isLoading if (!isLoading)
? Center(child: CircularProgressIndicator()) Container(
: Container(
color: color, color: color,
child: Column( child: Column(
children: [ children: [
@ -2599,7 +2604,27 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
], ],
), ),
), ),
), 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>(
Color(0xFFAA8E83)), // Loader color
),
),
],
),
)
])),
); );
} }

View File

@ -230,7 +230,8 @@ class _MyHomePageState extends ConsumerState<MyHomePage> {
MainAxisAlignment.spaceEvenly, // Space buttons evenly MainAxisAlignment.spaceEvenly, // Space buttons evenly
children: [ children: [
SizedBox( SizedBox(
width: 120, // Set button width width:
MediaQuery.of(context).size.width * 0.3, // Set button width
child: TextButton( child: TextButton(
onPressed: () => Navigator.pop(dialogContext), onPressed: () => Navigator.pop(dialogContext),
style: TextButton.styleFrom( style: TextButton.styleFrom(
@ -252,7 +253,8 @@ class _MyHomePageState extends ConsumerState<MyHomePage> {
), ),
), ),
SizedBox( SizedBox(
width: 120, // Set button width width:
MediaQuery.of(context).size.width * 0.3, // Set button width
child: TextButton( child: TextButton(
onPressed: () => logout(context), onPressed: () => logout(context),
style: TextButton.styleFrom( style: TextButton.styleFrom(
@ -262,11 +264,16 @@ class _MyHomePageState extends ConsumerState<MyHomePage> {
borderRadius: BorderRadius.circular(10.0), borderRadius: BorderRadius.circular(10.0),
), ),
), ),
child: FittedBox(
fit: BoxFit
.scaleDown, // Prevents wrapping while adjusting text size
child: Text( child: Text(
context.translate('Log Out', 'تسجيل الخروج'), context.translate('Log Out', 'تسجيل الخروج'),
style: TextStyle(color: Colors.white, fontSize: 16), style: TextStyle(color: Colors.white, fontSize: 16),
textAlign: TextAlign.center,
), ),
), ),
),
), ),
], ],
), ),
@ -1147,6 +1154,9 @@ class EconomyStatsState extends ConsumerState<EconomyStatsWidget> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
ref.listen<Locale?>(localeProvider, (previous, next) { ref.listen<Locale?>(localeProvider, (previous, next) {
final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null
setState(() {
isLoading = true;
});
fetchData(localeCode); fetchData(localeCode);
}); });
@ -1163,7 +1173,24 @@ class EconomyStatsState extends ConsumerState<EconomyStatsWidget> {
double mywidth = MediaQuery.of(context).size.width; double mywidth = MediaQuery.of(context).size.width;
if (isLoading) { 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>(
Color(0xFFAA8E83)), // Loader color
),
),
],
),
);
} else if (data.isEmpty) { } else if (data.isEmpty) {
return Center(child: Text("No data available")); return Center(child: Text("No data available"));
} }
@ -1469,28 +1496,39 @@ class InfoCard extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Flexible( // Flexible(
fit: FlexFit.loose, // fit: FlexFit.loose,
child: FittedBox( // child: FittedBox(
fit: BoxFit.contain, // Adjust text to fit within the bounds // fit: BoxFit.contain, // Adjust text to fit within the bounds
child: Padding( // child: Padding(
// padding: const EdgeInsets.all(4.0), // // padding: const EdgeInsets.all(4.0),
padding: const EdgeInsets.only(top: 4, bottom: 0), // padding: const EdgeInsets.only(top: 4, bottom: 0),
child: Text( // child: Text(
title, // title,
style: const TextStyle( // style: const TextStyle(
// fontSize: 11, // // fontSize: 11,
fontSize: 11 * 1.1, // fontSize: 12 * 1.1,
fontWeight: FontWeight.w400, // fontWeight: FontWeight.w500,
// color: Colors.black, // // color: Colors.black,
fontFamily: 'Roboto', // fontFamily: 'Roboto',
color: Color(0xFF000000), // 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( Flexible(
fit: FlexFit.loose, fit: FlexFit.loose,
@ -1503,7 +1541,7 @@ class InfoCard extends StatelessWidget {
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: const TextStyle( style: const TextStyle(
// fontSize: 11, // fontSize: 11,
fontSize: 11 * 1.1, fontSize: 12 * 1.1,
fontFamily: 'Roboto', fontFamily: 'Roboto',
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
color: Color(0xFF8E8E8E), color: Color(0xFF8E8E8E),

View File

@ -187,6 +187,9 @@ class _BookMarkState extends ConsumerState<BookMark> {
Future<void> removeBookmark(bookmarkId) async { Future<void> removeBookmark(bookmarkId) async {
try { try {
setState(() {
isLoading = true;
});
final adminAuth = await _pb.admins final adminAuth = await _pb.admins
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com'); .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
final adminToken = adminAuth.token; final adminToken = adminAuth.token;
@ -219,6 +222,10 @@ class _BookMarkState extends ConsumerState<BookMark> {
duration: Duration(seconds: 2), duration: Duration(seconds: 2),
); );
print("Error removing bookmark: $e"); print("Error removing bookmark: $e");
} finally {
setState(() {
isLoading = false;
});
} }
} }
@ -342,6 +349,9 @@ class _BookMarkState extends ConsumerState<BookMark> {
ref.listen<Locale?>(localeProvider, (previous, next) { ref.listen<Locale?>(localeProvider, (previous, next) {
final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null
setState(() {
isLoading = true;
});
fetchBookmarks(localeCode); fetchBookmarks(localeCode);
}); });
@ -365,154 +375,179 @@ class _BookMarkState extends ConsumerState<BookMark> {
title: Text( title: Text(
AppLocalizations.of(context)!.bookmarks, AppLocalizations.of(context)!.bookmarks,
), ),
body: Column( body: isLoading
children: [ ? Container(
TabBarHeader( color: Color(0x98FFFCE5), // Semi-transparent background
tabs: tabs, child: Column(
selectedIndex: selectedTabIndex, mainAxisAlignment: MainAxisAlignment.center,
onTabSelected: (index) { children: [
setState(() { Container(
selectedTabIndex = index; margin: EdgeInsets.symmetric(
}); horizontal: 40), // Left & Right space
}, child: LinearProgressIndicator(
), minHeight: 5, // Adjust thickness
Expanded( backgroundColor:
child: selectedTabIndex == 0 Colors.grey[100], // Optional: Background color
? (bookmarks.isNotEmpty valueColor: AlwaysStoppedAnimation<Color>(
? SingleChildScrollView( Color(0xFFAA8E83)), // Loader color
child: Column( ),
children: List.generate(transformedList.length, (i) { ),
final mainTopic = transformedList[i]; ],
print('oustside1 $mainTopic'); ),
final list = List.from(mainTopic['SubTopic'] ?? []); )
print('oustside2 $list'); : Column(
return CustomExpandableTile( children: [
index: i, TabBarHeader(
isExpanded: expandedIndex == i, tabs: tabs,
onTap: (int index) { selectedIndex: selectedTabIndex,
// 🔹 Expecting an index onTabSelected: (index) {
setState(() { setState(() {
expandedIndex = selectedTabIndex = index;
(expandedIndex == index) ? null : index; });
}); },
}, ),
title: mainTopic['main_topic'] ?? 'No Topic', Expanded(
childWidget: Container( child: selectedTabIndex == 0
decoration: BoxDecoration( ? (bookmarks.isNotEmpty
color: Colors.white, ? SingleChildScrollView(
// borderRadius: BorderRadius.all(Radius.circular(20)) child: Column(
), children:
padding: const EdgeInsets.all(16), List.generate(transformedList.length, (i) {
child: GridView.builder( final mainTopic = transformedList[i];
shrinkWrap: true, print('oustside1 $mainTopic');
physics: NeverScrollableScrollPhysics(), final list =
gridDelegate: List.from(mainTopic['SubTopic'] ?? []);
const SliverGridDelegateWithFixedCrossAxisCount( print('oustside2 $list');
crossAxisCount: 2, return CustomExpandableTile(
crossAxisSpacing: 10.0, index: i,
mainAxisSpacing: 10.0, isExpanded: expandedIndex == i,
mainAxisExtent: 100, onTap: (int index) {
), // 🔹 Expecting an index
itemCount: list.length, setState(() {
itemBuilder: (context, index) { expandedIndex = (expandedIndex == index)
return _buildBoxes( ? null
mainTopic['SubTopic'][index], : index;
context, });
mainTopic['valueColor']); },
}, 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'] ?? []), : Center(
titleBackgroundColor: mainTopic['valueColor'], child: Column(
// Ensure it's a new list mainAxisAlignment: MainAxisAlignment.center,
);
}),
),
)
: 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,
children: [ children: [
Icon(Icons.info_outline,
size: 100, color: Colors.grey[400]),
SizedBox(height: 16),
Text( Text(
tabs[selectedTabIndex]['title'], context.translate('No BookMark Added',
'لم يتم إضافة أي علامة مرجعية'),
style: TextStyle( style: TextStyle(
color: Colors.white, fontSize: 16, color: Colors.grey),
fontWeight: FontWeight.w600, ),
fontSize: 20, ],
),
))
: 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);
},
),
),
],
),
),
),
],
),
); );
} }

View File

@ -31,7 +31,6 @@ class _EditProfileState extends ConsumerState<EditProfile> {
final _pb = PocketBase(apiUrl); final _pb = PocketBase(apiUrl);
// final _pb = PocketBase('http://127.0.0.1:8090'); // final _pb = PocketBase('http://127.0.0.1:8090');
bool _isProfileCompleted = false; bool _isProfileCompleted = false;
bool _isLoading = false;
// Add focus nodes and hint states // Add focus nodes and hint states
final List<FocusNode> _focusNodes = List.generate(4, (_) => FocusNode()); final List<FocusNode> _focusNodes = List.generate(4, (_) => FocusNode());
@ -85,7 +84,7 @@ class _EditProfileState extends ConsumerState<EditProfile> {
final _picker = ImagePicker(); final _picker = ImagePicker();
File? _profileImage; File? _profileImage;
String _avatarUrl = ''; String _avatarUrl = '';
bool isPageLoad = false; bool isLoader = true;
// Regular expression to validate Full Name (no special characters) // Regular expression to validate Full Name (no special characters)
final RegExp _nameRegExp = RegExp(r'^[a-zA-Z\s]+$'); final RegExp _nameRegExp = RegExp(r'^[a-zA-Z\s]+$');
@ -174,10 +173,6 @@ class _EditProfileState extends ConsumerState<EditProfile> {
Future<void> _fetchUserData() async { Future<void> _fetchUserData() async {
try { try {
setState(() {
isPageLoad = true; // Show loader
print('Im isPageLoad');
});
print('EDIT PROFILE isPageLoad'); print('EDIT PROFILE isPageLoad');
final adminAuth = await _pb.admins final adminAuth = await _pb.admins
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com'); .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
@ -229,7 +224,7 @@ class _EditProfileState extends ConsumerState<EditProfile> {
} else { } else {
_avatarUrl = ''; // Reset to default or empty _avatarUrl = ''; // Reset to default or empty
} }
isPageLoad = false; isLoader = false;
}); });
} catch (e) { } catch (e) {
print('Error fetching user details: $e'); print('Error fetching user details: $e');
@ -261,7 +256,7 @@ class _EditProfileState extends ConsumerState<EditProfile> {
await _picker.pickImage(source: ImageSource.gallery); await _picker.pickImage(source: ImageSource.gallery);
if (pickedFile != null) { if (pickedFile != null) {
setState(() { setState(() {
_isLoading = true; // Start loading isLoader = true; // Start loading
}); });
print(pickedFile); print(pickedFile);
final String fileExtension = final String fileExtension =
@ -272,7 +267,7 @@ class _EditProfileState extends ConsumerState<EditProfile> {
fileExtension == 'png' || fileExtension == 'png' ||
fileExtension == 'heic') { fileExtension == 'heic') {
setState(() { setState(() {
_isLoading = false; isLoader = false;
_profileImage = File(pickedFile.path); _profileImage = File(pickedFile.path);
}); });
} else { } else {
@ -283,7 +278,7 @@ class _EditProfileState extends ConsumerState<EditProfile> {
} }
setState(() { setState(() {
_isLoading = false; // Hide loader isLoader = false; // Hide loader
}); });
} }
@ -356,6 +351,9 @@ class _EditProfileState extends ConsumerState<EditProfile> {
} }
void showConfirmationDialog(BuildContext context) async { void showConfirmationDialog(BuildContext context) async {
setState(() {
isLoader = true;
});
// final result = await showDialog<bool>( // final result = await showDialog<bool>(
// context: context, // context: context,
// builder: (context) => const ConfirmationDialog(), // builder: (context) => const ConfirmationDialog(),
@ -404,12 +402,18 @@ class _EditProfileState extends ConsumerState<EditProfile> {
// Handle response // Handle response
if (response.statusCode == 200) { if (response.statusCode == 200) {
_resetFormFields(); _resetFormFields();
setState(() {
isLoader = false;
});
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Profile updated successfully!')), SnackBar(content: Text('Profile updated successfully!')),
); );
// print('ShowConfirmation userData - $userData '); // print('ShowConfirmation userData - $userData ');
context.go('/myhomepage'); context.go('/myhomepage');
} else { } else {
setState(() {
isLoader = false;
});
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text('Failed to update profile: ${response.statusCode}'), content: Text('Failed to update profile: ${response.statusCode}'),
@ -417,6 +421,9 @@ class _EditProfileState extends ConsumerState<EditProfile> {
); );
} }
} catch (error) { } catch (error) {
setState(() {
isLoader = false;
});
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to update profile: $error')), SnackBar(content: Text('Failed to update profile: $error')),
); );
@ -424,6 +431,7 @@ class _EditProfileState extends ConsumerState<EditProfile> {
} else { } else {
// Show an error if the country is invalid // Show an error if the country is invalid
setState(() { setState(() {
isLoader = false;
showError = true; showError = true;
}); });
} }
@ -477,23 +485,40 @@ class _EditProfileState extends ConsumerState<EditProfile> {
AppLocalizations.of(context)!.my_profile, AppLocalizations.of(context)!.my_profile,
style: TextStyle(color: Color(0xFF985400)), style: TextStyle(color: Color(0xFF985400)),
), ),
body: SingleChildScrollView( body: isLoader
child: Padding( ? Container(
padding: const EdgeInsets.only(left: 10.0, right: 10.0, top: 3.0), color: Color(0x98FFFCE5), // Semi-transparent background
child: Column( child: Column(
children: [ mainAxisAlignment: MainAxisAlignment.center,
SingleChildScrollView( children: [
child: Form( Container(
key: _formKey, margin: EdgeInsets.symmetric(
child: Padding( horizontal: 40), // Left & Right space
// padding: const EdgeInsets.all(20.0), child: LinearProgressIndicator(
padding: const EdgeInsets.only( minHeight: 5, // Adjust thickness
top: 20.0, bottom: 20.0, left: 25, right: 25), backgroundColor:
child: isPageLoad Colors.grey[100], // Optional: Background color
? Center( valueColor: AlwaysStoppedAnimation<Color>(
child: CircularProgressIndicator(), Color(0xFFAA8E83)), // Loader color
) ),
: Column( ),
],
),
)
: 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: [ children: [
// CircleAvatar( // CircleAvatar(
// radius: 50, // radius: 50,
@ -643,16 +668,6 @@ class _EditProfileState extends ConsumerState<EditProfile> {
), ),
), ),
), ),
if (_isLoading)
Positioned(
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor:
AlwaysStoppedAnimation<Color>(
Colors.grey,
),
),
),
], ],
), ),
@ -1150,13 +1165,13 @@ class _EditProfileState extends ConsumerState<EditProfile> {
) )
], ],
), ),
), ),
),
),
],
), ),
), ),
], ),
),
),
),
//bottomNavigationBar: MyBottomNavBar(), //bottomNavigationBar: MyBottomNavBar(),
)); ));
} }

View File

@ -32,6 +32,7 @@ class FeedbackForm extends ConsumerStatefulWidget {
class _FeedbackFormState extends ConsumerState<FeedbackForm> class _FeedbackFormState extends ConsumerState<FeedbackForm>
with WidgetsBindingObserver { with WidgetsBindingObserver {
final _pb = PocketBase(apiUrl); // Initialize PocketBase client final _pb = PocketBase(apiUrl); // Initialize PocketBase client
bool isLoading = false;
// final _pb = // final _pb =
// PocketBase('http://127.0.0.1:8090'); // Initialize PocketBase client // PocketBase('http://127.0.0.1:8090'); // Initialize PocketBase client
@ -277,17 +278,37 @@ class _FeedbackFormState extends ConsumerState<FeedbackForm>
// ), // ),
// ], // ],
// ), // ),
body: Padding( body: isLoading
padding: const EdgeInsets.only( ? Container(
top: 8.0, bottom: 16.0, left: 23.0, right: 23.0), color: Color(0x98FFFCE5), // Semi-transparent background
child: SingleChildScrollView( child: Column(
child: _isFeedbackSubmitted mainAxisAlignment: MainAxisAlignment.center,
? _buildThankYouMessage(userName) children: [
: _isFeedbackFailed Container(
? _buildFailureMessage() margin: EdgeInsets.symmetric(
: _buildFeedbackForm(), horizontal: 40), // Left & Right space
), child: LinearProgressIndicator(
), minHeight: 5, // Adjust thickness
backgroundColor:
Colors.grey[100], // Optional: Background color
valueColor: AlwaysStoppedAnimation<Color>(
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<FeedbackForm>
} }
Future<void> _submitFeedback() async { Future<void> _submitFeedback() async {
setState(() {
isLoading = true;
});
if (_selectedEmojiIndex == null) { if (_selectedEmojiIndex == null) {
setState(() { setState(() {
_isSmileySelected = false; _isSmileySelected = false;
@ -802,6 +826,7 @@ class _FeedbackFormState extends ConsumerState<FeedbackForm>
_isFeedbackSubmitted = true; _isFeedbackSubmitted = true;
_isFeedbackFailed = false; _isFeedbackFailed = false;
_resetFeedbackForm(); _resetFeedbackForm();
isLoading = false;
}); });
} }
} catch (e) { } catch (e) {

View File

@ -57,7 +57,6 @@ class _ManageUserRouterState extends ConsumerState<ManageUserRouter> {
); );
setState(() { setState(() {
isLoading = true;
userData = result.map((record) { userData = result.map((record) {
final createdDate = DateTime.parse(record.created) final createdDate = DateTime.parse(record.created)
.add(Duration(hours: 4)); // Parse the created date .add(Duration(hours: 4)); // Parse the created date
@ -82,6 +81,7 @@ class _ManageUserRouterState extends ConsumerState<ManageUserRouter> {
isLoading = false; isLoading = false;
}); });
} catch (e) { } catch (e) {
isLoading = false;
print('Error fetching unverified users: $e'); print('Error fetching unverified users: $e');
} }
} }
@ -123,6 +123,43 @@ class _ManageUserRouterState extends ConsumerState<ManageUserRouter> {
}); });
} }
Future<void> 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 //Method to show a confirmation dialog when status is changed
Future<void> _showConfirmationDialog(User user, String newStatus) async { Future<void> _showConfirmationDialog(User user, String newStatus) async {
print('user $user'); print('user $user');
@ -219,32 +256,9 @@ class _ManageUserRouterState extends ConsumerState<ManageUserRouter> {
width: 100, // Set the desired width width: 100, // Set the desired width
child: TextButton( child: TextButton(
onPressed: () async { onPressed: () async {
try { Navigator.of(context).pop();
// Update status in PocketBase changeStatus(user.id, newStatus);
await _pb.collection('users').update( // Close dialog
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
}, },
style: TextButton.styleFrom( style: TextButton.styleFrom(
backgroundColor: backgroundColor:
@ -297,249 +311,273 @@ class _ManageUserRouterState extends ConsumerState<ManageUserRouter> {
title: Text( title: Text(
AppLocalizations.of(context)!.manage_user, AppLocalizations.of(context)!.manage_user,
), ),
body: SingleChildScrollView( body: isLoading
child: Padding( ? Container(
padding: const EdgeInsets.only(left: 10.0, right: 10.0, top: 3.0), color: Color(0x98FFFCE5), // Semi-transparent background
child: Column( child: Column(
children: [ mainAxisAlignment: MainAxisAlignment.center,
// TextField( children: [
// decoration: InputDecoration( Container(
// prefixIcon: const Icon(Icons.search), margin: EdgeInsets.symmetric(
// hintText: 'Search', horizontal: 40), // Left & Right space
// border: OutlineInputBorder( child: LinearProgressIndicator(
// borderRadius: BorderRadius.all(Radius.circular(20)), minHeight: 5, // Adjust thickness
// ), backgroundColor:
// ), Colors.grey[100], // Optional: Background color
// onChanged: filterUsers, valueColor: AlwaysStoppedAnimation<Color>(
// ), Color(0xFFAA8E83)), // Loader color
// Container( ),
// height: 40, ),
// decoration: BoxDecoration( ],
// color: Colors.white, ),
// borderRadius: BorderRadius.circular(30.0), )
// border: Border.all(width: 2, color: Color(0xFFAA8E83)), : SingleChildScrollView(
// ), child: Padding(
// child: TextField( padding:
// decoration: InputDecoration( const EdgeInsets.only(left: 10.0, right: 10.0, top: 3.0),
// hintText: AppLocalizations.of(context)!.search, child: Column(
// hintStyle: TextStyle(color: Color(0xFFC3C6CB)), children: [
// prefixIcon: Image.asset( // TextField(
// MiscIconAssetPath.search, // decoration: InputDecoration(
// width: 20, // prefixIcon: const Icon(Icons.search),
// height: 20, // hintText: 'Search',
// ), // border: OutlineInputBorder(
// // ,prefixIcon: Icon( // borderRadius: BorderRadius.all(Radius.circular(20)),
// // Icons.search, // ),
// // color: Color(0xFFAA8E83), // ),
// // ), // onChanged: filterUsers,
// border: InputBorder.none, // ),
// contentPadding: EdgeInsets.symmetric( // Container(
// vertical: 15.0, horizontal: 20.0), // height: 40,
// ), // decoration: BoxDecoration(
// onChanged: filterUsers, // color: Colors.white,
// ), // borderRadius: BorderRadius.circular(30.0),
// ), // border: Border.all(width: 2, color: Color(0xFFAA8E83)),
Container( // ),
height: 40, // child: TextField(
decoration: BoxDecoration( // decoration: InputDecoration(
color: Colors.white, // hintText: AppLocalizations.of(context)!.search,
borderRadius: BorderRadius.circular(30.0), // hintStyle: TextStyle(color: Color(0xFFC3C6CB)),
border: Border.all(width: 1, color: Color(0xFFAA8E83)), // prefixIcon: Image.asset(
), // MiscIconAssetPath.search,
child: Padding( // width: 20,
padding: EdgeInsets.only(left: 12.0), // height: 20,
child: TextField( // ),
decoration: InputDecoration( // // ,prefixIcon: Icon(
hintText: AppLocalizations.of(context)!.search, // // Icons.search,
hintStyle: TextStyle(color: Color(0xFFAA8E83)), // // color: Color(0xFFAA8E83),
// hintStyle: TextStyle(color: Color(0xFFAA8E83)), // // ),
prefixIconConstraints: // border: InputBorder.none,
BoxConstraints(maxWidth: 42, maxHeight: 42), // contentPadding: EdgeInsets.symmetric(
prefixIcon: Container( // vertical: 15.0, horizontal: 20.0),
padding: EdgeInsets.only(right: 5), // ),
child: SvgPicture.asset( // onChanged: filterUsers,
MiscIconAssetPath.Search, // ),
semanticsLabel: 'Search', // ),
colorFilter: ColorFilter.mode( Container(
Color(0xFFAA8E83), BlendMode.srcIn), 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),
), filteredUserData.isEmpty
), ? Center(
), child: Padding(
SizedBox(height: myheight / 40), padding: const EdgeInsets.all(16.0),
isLoading child: Column(
? Center(child: CircularProgressIndicator()) mainAxisSize: MainAxisSize.min,
: filteredUserData.isEmpty children: [
? Center( SizedBox(height: myheight / 5),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: myheight / 5),
// Icon(Icons.search, // Icon(Icons.search,
// size: 60, color: Colors.grey), // size: 60, color: Colors.grey),
Image.asset( Image.asset(
MiscIconAssetPath.group, MiscIconAssetPath.group,
width: 60, width: 60,
height: 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],
), ),
),
SizedBox(height: 12), // Space between texts SizedBox(height: 15),
FittedBox( // Space between icon and text
child: Text( Text(
"We couldn't find anything matching your search.", "No results found",
style: TextStyle( style: TextStyle(
fontSize: 18, fontSize: 24,
color: Color(0xFF898C81)), fontWeight: FontWeight.bold,
textAlign: TextAlign.center, 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(
: SingleChildScrollView( scrollDirection: Axis.horizontal,
scrollDirection: Axis.horizontal, child: ConstrainedBox(
child: ConstrainedBox( constraints: BoxConstraints(
constraints: BoxConstraints( minWidth: MediaQuery.of(context).size.width,
minWidth: MediaQuery.of(context).size.width, ),
), child: DataTable(
child: DataTable( sortColumnIndex: _sortColumnIndex,
sortColumnIndex: _sortColumnIndex, sortAscending: _isAscending,
sortAscending: _isAscending, columns: [
columns: [ DataColumn(
DataColumn( label: Text(
label: Text( AppLocalizations.of(context)!.user_name,
AppLocalizations.of(context)!.user_name, ),
onSort: (columnIndex, ascending) {
_sort(
(user) =>
user.userName.toLowerCase(),
columnIndex,
ascending);
},
), ),
onSort: (columnIndex, ascending) { DataColumn(
_sort( label: Text(
(user) => user.userName.toLowerCase(), 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, columnIndex,
ascending); ascending,
}, );
), },
DataColumn(
label: Text(
AppLocalizations.of(context)!.email_id,
), ),
onSort: (columnIndex, ascending) { DataColumn(
_sort( label: Text(
(user) => user.emailId.toLowerCase(), AppLocalizations.of(context)!.status,
columnIndex, ),
ascending); onSort: (columnIndex, ascending) {
}, _sort(
), (user) => user.status.toLowerCase(),
DataColumn( columnIndex,
label: Text( ascending);
AppLocalizations.of(context)!.reg_date, },
), ),
onSort: (columnIndex, ascending) { ],
_sort( rows: filteredUserData.isEmpty
(user) => DateFormat('dd/MM/yyyy') ? [
.parse(user.registrationDate), DataRow(
columnIndex, cells: List<DataCell>.generate(
ascending, 4, // Ensure it matches the number of DataColumns
); (index) => DataCell(
}, index == 0
), ? Text(
DataColumn( 'No results found',
label: Text( style: TextStyle(
AppLocalizations.of(context)!.status, fontStyle: FontStyle
), .italic),
onSort: (columnIndex, ascending) { )
_sort((user) => user.status.toLowerCase(), : const Text(
columnIndex, ascending); ''), // Empty cells for other columns
}, placeholder: true,
),
],
rows: filteredUserData.isEmpty
? [
DataRow(
cells: List<DataCell>.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<String>(
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(), : filteredUserData.map((user) {
return DataRow(
cells: [
DataCell(Text(user.userName)),
DataCell(Text(user.emailId)),
DataCell(
Text(user.registrationDate)),
DataCell(
DropdownButton<String>(
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(),
),
), ),
), ),
), ],
], ),
), ),
), ),
),
), ),
); );
} }

View File

@ -22,6 +22,7 @@ class NotificationPage extends ConsumerStatefulWidget {
class _NotificationPageState extends ConsumerState<NotificationPage> { class _NotificationPageState extends ConsumerState<NotificationPage> {
// Example notification data // Example notification data
final _pb = PocketBase(apiUrl); final _pb = PocketBase(apiUrl);
bool isLoading = true;
List<Map<String, dynamic>> notifications = []; List<Map<String, dynamic>> notifications = [];
List<String> pushedNotification = []; List<String> pushedNotification = [];
@ -109,15 +110,25 @@ class _NotificationPageState extends ConsumerState<NotificationPage> {
notifications = List<Map<String, dynamic>>.from( notifications = List<Map<String, dynamic>>.from(
jsonResponse['data']); // Assign decoded data jsonResponse['data']); // Assign decoded data
print('notifications $notifications'); print('notifications $notifications');
isLoading = false;
}); });
} else { } else {
setState(() {
isLoading = false;
});
throw Exception('Failed to load data'); throw Exception('Failed to load data');
} }
} else { } else {
setState(() {
isLoading = false;
});
throw Exception( throw Exception(
'Failed to load data with status code ${response.statusCode}'); 'Failed to load data with status code ${response.statusCode}');
} }
} catch (e) { } catch (e) {
setState(() {
isLoading = false;
});
print('Error fetching data: $e'); print('Error fetching data: $e');
} }
} }
@ -132,6 +143,9 @@ class _NotificationPageState extends ConsumerState<NotificationPage> {
ref.listen<Locale?>(localeProvider, (previous, next) { ref.listen<Locale?>(localeProvider, (previous, next) {
final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null
setState(() {
isLoading = true;
});
fetchNotifications(localeCode); fetchNotifications(localeCode);
}); });
final List<Map<String, dynamic>> tabs = [ final List<Map<String, dynamic>> tabs = [
@ -164,60 +178,83 @@ class _NotificationPageState extends ConsumerState<NotificationPage> {
'إشعار', 'إشعار',
), ),
), ),
body: Column( body: isLoading
children: [ ? Container(
TabBarHeader( color: Color(0x98FFFCE5), // Semi-transparent background
tabs: tabs.map((tab) => tab['title'] as String).toList(), child: Column(
selectedIndex: selectedTabIndex, mainAxisAlignment: MainAxisAlignment.center,
onTabSelected: (index) { children: [
setState(() { Container(
selectedTabIndex = index; margin: EdgeInsets.symmetric(
}); horizontal: 40), // Left & Right space
}, child: LinearProgressIndicator(
), minHeight: 5, // Adjust thickness
Expanded( backgroundColor:
child: filteredNotifications.isEmpty Colors.grey[100], // Optional: Background color
? Center( valueColor: AlwaysStoppedAnimation<Color>(
child: Column( Color(0xFFAA8E83)), // Loader color
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,
),
);
},
), ),
), ],
], ),
), )
: 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,
),
);
},
),
),
],
),
), ),
); );
} }

View File

@ -37,6 +37,7 @@ class _NotificationDetailsState extends ConsumerState<NotificationDetails> {
// Example notification data // Example notification data
final _pb = PocketBase(apiUrl); final _pb = PocketBase(apiUrl);
Map<String, dynamic>? notification; Map<String, dynamic>? notification;
bool isLoading = true;
// Current selected tab index // Current selected tab index
int selectedTabIndex = 0; int selectedTabIndex = 0;
@ -77,6 +78,7 @@ class _NotificationDetailsState extends ConsumerState<NotificationDetails> {
setState(() { setState(() {
final data = jsonResponse['data']; final data = jsonResponse['data'];
notification = data; notification = data;
isLoading = false;
// if (data is Map<String, dynamic>) { // if (data is Map<String, dynamic>) {
// notification = data; // notification = data;
// } else { // } else {
@ -86,13 +88,22 @@ class _NotificationDetailsState extends ConsumerState<NotificationDetails> {
print('notification $notification'); print('notification $notification');
}); });
} else { } else {
setState(() {
isLoading = false;
});
throw Exception('Failed to load data'); throw Exception('Failed to load data');
} }
} else { } else {
setState(() {
isLoading = false;
});
throw Exception( throw Exception(
'Failed to load data with status code ${response.statusCode}'); 'Failed to load data with status code ${response.statusCode}');
} }
} catch (e) { } catch (e) {
setState(() {
isLoading = false;
});
print('Error fetching data: $e'); print('Error fetching data: $e');
} }
} }
@ -107,6 +118,9 @@ class _NotificationDetailsState extends ConsumerState<NotificationDetails> {
ref.listen<Locale?>(localeProvider, (previous, next) { ref.listen<Locale?>(localeProvider, (previous, next) {
final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null
setState(() {
isLoading = true;
});
fetchNotifications(localeCode); fetchNotifications(localeCode);
}); });
@ -125,53 +139,73 @@ class _NotificationDetailsState extends ConsumerState<NotificationDetails> {
), ),
showBackButton: true, showBackButton: true,
navBackArrow: Text(widget.backNavigation ?? 'Default Value'), navBackArrow: Text(widget.backNavigation ?? 'Default Value'),
body: Padding( body: isLoading
padding: const EdgeInsets.all(16.0), ? Container(
child: Column( color: Color(0x98FFFCE5), // Semi-transparent background
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( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Image.asset( Container(
notification?['category'] == 'App updates' margin: EdgeInsets.symmetric(
? 'assets/backgrounds/Notification/App-Update.png' horizontal: 40), // Left & Right space
: 'assets/backgrounds/Notification/Update_notific.png', child: LinearProgressIndicator(
height: 200, minHeight: 5, // Adjust thickness
backgroundColor:
Colors.grey[100], // Optional: Background color
valueColor: AlwaysStoppedAnimation<Color>(
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),
),
),
],
),
),
)); ));
} }
} }

View File

@ -1244,9 +1244,14 @@ void _showLogoutConfirmationDialog(BuildContext context) {
borderRadius: BorderRadius.circular(10.0), borderRadius: BorderRadius.circular(10.0),
), ),
), ),
child: Text( child: FittedBox(
context.translate('Log Out', 'تسجيل الخروج'), fit: BoxFit
style: TextStyle(color: Colors.white, fontSize: 16), .scaleDown, // Prevents wrapping while adjusting text size
child: Text(
context.translate('Log Out', 'تسجيل الخروج'),
style: TextStyle(color: Colors.white, fontSize: 16),
textAlign: TextAlign.center,
),
), ),
), ),
), ),

View File

@ -1,7 +1,7 @@
name: uae_stat name: uae_stat
description: "View statistics about the UAE from the Federal Center for Statistics and Competitiveness." description: "View statistics about the UAE from the Federal Center for Statistics and Competitiveness."
publish_to: "none" publish_to: "none"
version: 1.0.35+36 version: 1.0.36+37
environment: environment:
sdk: ">=3.2.3 <4.0.0" sdk: ">=3.2.3 <4.0.0"