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")
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'
}
}

View File

@ -182,12 +182,38 @@ class _UaenumberWidgetState extends ConsumerState<uaenumberWidget> {
ref.listen<Locale?>(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>(
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),

View File

@ -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<CreateNewPw> {
final _pb = PocketBase(apiUrl);
bool isLoading = false;
final _formKey = GlobalKey<FormState>();
bool _obscureOldPassword = true;
bool _obscureNewPassword = true;
@ -139,6 +142,9 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
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');
@ -164,6 +170,11 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
headers: headers,
);
setState(() {
isLoading = false;
_isPasswordUpdated = true;
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
@ -172,9 +183,7 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
),
);
setState(() {
_isPasswordUpdated = true; // Toggle UI on success
});
print('_isPasswordUpdated $_isPasswordUpdated');
// Navigator.push(
// context,
@ -183,6 +192,7 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
// );
} else {
setState(() {
isLoading = false;
_formKey.currentState?.validate();
});
ScaffoldMessenger.of(context).showSnackBar(
@ -195,6 +205,9 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
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<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
Widget build(BuildContext context) {
double screenHeight = MediaQuery.of(context).size.height;
@ -216,15 +238,36 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
});
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>(
Color(0xFFAA8E83)), // Loader color
),
),
],
),
)
]));
}
Widget _buildPasswordForm(
@ -626,8 +669,9 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
}
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<CreateNewPw> {
),
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<CreateNewPw> {
// surfaceTintColor: MaterialStatePropertyAll(
// MyTheme.economy[800],
// ),
foregroundColor: const WidgetStatePropertyAll(
foregroundColor: WidgetStatePropertyAll(
Colors.white,
),
),
@ -697,12 +739,12 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
),
),
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,

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) {
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<ChartScreen1> {
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<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
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<MyHomePage> {
),
),
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<MyHomePage> {
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<EconomyStatsWidget> {
Widget build(BuildContext context) {
ref.listen<Locale?>(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<EconomyStatsWidget> {
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>(
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),

View File

@ -187,6 +187,9 @@ class _BookMarkState extends ConsumerState<BookMark> {
Future<void> 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<BookMark> {
duration: Duration(seconds: 2),
);
print("Error removing bookmark: $e");
} finally {
setState(() {
isLoading = false;
});
}
}
@ -342,6 +349,9 @@ class _BookMarkState extends ConsumerState<BookMark> {
ref.listen<Locale?>(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<BookMark> {
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>(
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);
},
),
),
],
),
),
),
],
),
),
],
),
);
}

View File

@ -31,7 +31,6 @@ class _EditProfileState extends ConsumerState<EditProfile> {
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<FocusNode> _focusNodes = List.generate(4, (_) => FocusNode());
@ -85,7 +84,7 @@ class _EditProfileState extends ConsumerState<EditProfile> {
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<EditProfile> {
Future<void> _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<EditProfile> {
} 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<EditProfile> {
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<EditProfile> {
fileExtension == 'png' ||
fileExtension == 'heic') {
setState(() {
_isLoading = false;
isLoader = false;
_profileImage = File(pickedFile.path);
});
} else {
@ -283,7 +278,7 @@ class _EditProfileState extends ConsumerState<EditProfile> {
}
setState(() {
_isLoading = false; // Hide loader
isLoader = false; // Hide loader
});
}
@ -356,6 +351,9 @@ class _EditProfileState extends ConsumerState<EditProfile> {
}
void showConfirmationDialog(BuildContext context) async {
setState(() {
isLoader = true;
});
// final result = await showDialog<bool>(
// context: context,
// builder: (context) => const ConfirmationDialog(),
@ -404,12 +402,18 @@ class _EditProfileState extends ConsumerState<EditProfile> {
// 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<EditProfile> {
);
}
} 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<EditProfile> {
} else {
// Show an error if the country is invalid
setState(() {
isLoader = false;
showError = true;
});
}
@ -477,23 +485,40 @@ class _EditProfileState extends ConsumerState<EditProfile> {
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>(
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<EditProfile> {
),
),
),
if (_isLoading)
Positioned(
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor:
AlwaysStoppedAnimation<Color>(
Colors.grey,
),
),
),
],
),
@ -1150,13 +1165,13 @@ class _EditProfileState extends ConsumerState<EditProfile> {
)
],
),
),
),
),
),
],
),
),
],
),
),
),
),
//bottomNavigationBar: MyBottomNavBar(),
));
}

View File

@ -32,6 +32,7 @@ class FeedbackForm extends ConsumerStatefulWidget {
class _FeedbackFormState extends ConsumerState<FeedbackForm>
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<FeedbackForm>
// ),
// ],
// ),
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>(
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 {
setState(() {
isLoading = true;
});
if (_selectedEmojiIndex == null) {
setState(() {
_isSmileySelected = false;
@ -802,6 +826,7 @@ class _FeedbackFormState extends ConsumerState<FeedbackForm>
_isFeedbackSubmitted = true;
_isFeedbackFailed = false;
_resetFeedbackForm();
isLoading = false;
});
}
} catch (e) {

View File

@ -57,7 +57,6 @@ class _ManageUserRouterState extends ConsumerState<ManageUserRouter> {
);
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<ManageUserRouter> {
isLoading = false;
});
} catch (e) {
isLoading = false;
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
Future<void> _showConfirmationDialog(User user, String newStatus) async {
print('user $user');
@ -219,32 +256,9 @@ class _ManageUserRouterState extends ConsumerState<ManageUserRouter> {
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<ManageUserRouter> {
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>(
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<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);
}
},
],
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,
),
),
],
);
}).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> {
// Example notification data
final _pb = PocketBase(apiUrl);
bool isLoading = true;
List<Map<String, dynamic>> notifications = [];
List<String> pushedNotification = [];
@ -109,15 +110,25 @@ class _NotificationPageState extends ConsumerState<NotificationPage> {
notifications = List<Map<String, dynamic>>.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<NotificationPage> {
ref.listen<Locale?>(localeProvider, (previous, next) {
final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null
setState(() {
isLoading = true;
});
fetchNotifications(localeCode);
});
final List<Map<String, dynamic>> tabs = [
@ -164,60 +178,83 @@ class _NotificationPageState extends ConsumerState<NotificationPage> {
'إشعار',
),
),
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>(
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,
),
);
},
),
),
],
),
),
);
}

View File

@ -37,6 +37,7 @@ class _NotificationDetailsState extends ConsumerState<NotificationDetails> {
// Example notification data
final _pb = PocketBase(apiUrl);
Map<String, dynamic>? notification;
bool isLoading = true;
// Current selected tab index
int selectedTabIndex = 0;
@ -77,6 +78,7 @@ class _NotificationDetailsState extends ConsumerState<NotificationDetails> {
setState(() {
final data = jsonResponse['data'];
notification = data;
isLoading = false;
// if (data is Map<String, dynamic>) {
// notification = data;
// } else {
@ -86,13 +88,22 @@ class _NotificationDetailsState extends ConsumerState<NotificationDetails> {
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<NotificationDetails> {
ref.listen<Locale?>(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<NotificationDetails> {
),
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>(
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),
),
),
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,
),
),
),
),

View File

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