loader added all page
This commit is contained in:
parent
ef761a8d0a
commit
f6de75f0c8
@ -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'
|
||||
}
|
||||
}
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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,
|
||||
|
||||
632
lib/presentation/Screens/auth_verification/forgot_password.dart
Normal file
632
lib/presentation/Screens/auth_verification/forgot_password.dart
Normal 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
@ -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
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
])),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -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(),
|
||||
));
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -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,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -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),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@ -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"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user