notification bug fix

This commit is contained in:
venbaittech 2025-03-06 18:51:53 +05:30
parent b5d0b38ac7
commit 44c21defa7
8 changed files with 529 additions and 307 deletions

View File

@ -22,7 +22,7 @@ class LoadingOverlay {
color: Colors.black.withOpacity(0.5), dismissible: false), color: Colors.black.withOpacity(0.5), dismissible: false),
Center( Center(
child: Image.asset( child: Image.asset(
"assets/images/loading.gif", // Make sure to add the GIF in assets "assets/FCSC-GIF.gif", // Make sure to add the GIF in assets
width: 100, width: 100,
height: 100, height: 100,
), ),

View File

@ -530,6 +530,8 @@ final GoRouter router = GoRouter(
message: data['message'] ?? '', message: data['message'] ?? '',
date: data['date'] ?? '', date: data['date'] ?? '',
category: data['category'] ?? '', category: data['category'] ?? '',
id: data['id'] ?? '',
backNavigation: data['backNavigation'] ?? '',
); );
}, },
), ),
@ -650,16 +652,17 @@ final GoRouter router = GoRouter(
), ),
], ],
redirect: (context, state) async { redirect: (context, state) async {
if (state.uri.path == '/SessionCheckScreen' || state.uri.path == '/login') { if (state.uri.path == '/SessionCheckScreen' ||
state.uri.path == '/login') {
return null; return null;
} }
final session = await PAuthRepo().fetchLocallyStored(); final session = await PAuthRepo().fetchLocallyStored();
if (session == null || session.freshness == SessionFreshness.expired) { if (session == null || session.freshness == SessionFreshness.expired) {
print('Session expired, redirecting to login with intendedPath: ${state.uri.path}'); print(
'Session expired, redirecting to login with intendedPath: ${state.uri.path}');
return '/login${state.uri.path != '/' ? '?intendedPath=${state.uri.path}' : ''}'; return '/login${state.uri.path != '/' ? '?intendedPath=${state.uri.path}' : ''}';
} }
return null; return null;
} });
);

View File

@ -45,6 +45,13 @@
"account_confirmation": "هل لديك حساب؟ سجل الدخول", "account_confirmation": "هل لديك حساب؟ سجل الدخول",
"login_title": "تسجيل الدخول", "login_title": "تسجيل الدخول",
"all_updates": "جميع التحديثات",
"app_updates": "تحديثات التطبيق",
"notificationSocial": "اجتماعي",
"notificationEconomy": "اقتصاد",
"notificationEnvironment": "بيئة",
"notificationFavourites": "المفضلة",
"profile_title": "نموذج الملاحظات", "profile_title": "نموذج الملاحظات",
"my_profile": "ملفي الشخصي", "my_profile": "ملفي الشخصي",
"logout": "تسجيل الخروج", "logout": "تسجيل الخروج",

View File

@ -1,8 +1,8 @@
{ {
"all_bookmarks":"All Bookmarks", "all_bookmarks": "All Bookmarks",
"economy_title":"Economy", "economy_title": "Economy",
"social_title":"Social", "social_title": "Social",
"environment_title":"Environment", "environment_title": "Environment",
"bookmarks": "Bookmarks", "bookmarks": "Bookmarks",
"feedback_title": "Feedback", "feedback_title": "Feedback",
@ -41,7 +41,12 @@
"account_confirmation": "Already have an account?", "account_confirmation": "Already have an account?",
"login_title": "Login", "login_title": "Login",
"all_updates": "All updates",
"app_updates": "App updates",
"notificationSocial": "Social",
"notificationEconomy": "Economy",
"notificationEnvironment": "Environment",
"notificationFavourites": "Favourites",
"profile_title": "Edit Profile", "profile_title": "Edit Profile",
"my_profile": "My Profile", "my_profile": "My Profile",

View File

@ -65,7 +65,19 @@ class _EditProfileState extends State<EditProfile> {
'India', 'India',
'Canada', 'Canada',
]; ];
final List<String> preferred_language = ['English', 'Arabic'];
final Map<String, String> languageMap = {
"en": "English",
"ar": "Arabic",
};
// Reverse mapping for saving back to API
final Map<String, String> reverseLanguageMap = {
"English": "en",
"Arabic": "ar",
};
String? _selectedCountry; String? _selectedCountry;
String? _selectedLanguage;
bool isChecked = false; bool isChecked = false;
bool showError = false; bool showError = false;
final _picker = ImagePicker(); final _picker = ImagePicker();
@ -82,6 +94,7 @@ class _EditProfileState extends State<EditProfile> {
dynamic role; dynamic role;
bool _isHoveringDropdown = false; bool _isHoveringDropdown = false;
bool _isHoveringPreferredLang = false;
@override @override
void initState() { void initState() {
@ -177,10 +190,16 @@ class _EditProfileState extends State<EditProfile> {
print('userDetails: $userDetailsResponse'); print('userDetails: $userDetailsResponse');
setState(() { setState(() {
setState(() {
_selectedLanguage =
languageMap[userDetailsResponse.data['language']] ??
'English'; // Default to English if not found
});
_usernameController.text = userDetailsResponse.data['uname'] ?? ''; _usernameController.text = userDetailsResponse.data['uname'] ?? '';
_emailController.text = userDetailsResponse.data['email'] ?? ''; _emailController.text = userDetailsResponse.data['email'] ?? '';
_fullNameController.text = userDetailsResponse.data['full_name'] ?? ''; _fullNameController.text = userDetailsResponse.data['full_name'] ?? '';
_selectedCountry = userDetailsResponse.data['country_region'] ?? ''; _selectedCountry = userDetailsResponse.data['country_region'] ?? '';
// _selectedLanguage = userDetailsResponse.data['language'] ?? '';
_isProfileCompleted = _isProfileCompleted =
userDetailsResponse.data['is_profile_completed'] ?? false; userDetailsResponse.data['is_profile_completed'] ?? false;
//_isProfileCompleted = true ; //_isProfileCompleted = true ;
@ -321,6 +340,13 @@ class _EditProfileState extends State<EditProfile> {
return null; return null;
} }
String? _validatePreferredLang(String? value) {
if (value == null || value.isEmpty) {
return context.translate('Required', 'مطلوب');
}
return null;
}
void _toggleCheckbox(bool? value) { void _toggleCheckbox(bool? value) {
setState(() { setState(() {
isChecked = value ?? false; isChecked = value ?? false;
@ -335,7 +361,8 @@ class _EditProfileState extends State<EditProfile> {
// if (result == true) { // if (result == true) {
// Validate only the country field // Validate only the country field
if (_validateDropdown(_selectedCountry) == null) { if (_validateDropdown(_selectedCountry) == null &&
_validatePreferredLang(_selectedLanguage) == null) {
try { try {
String userID = userId; String userID = userId;
@ -343,6 +370,7 @@ class _EditProfileState extends State<EditProfile> {
// Retrieve data from the country/region field // Retrieve data from the country/region field
String countryRegion = String countryRegion =
_selectedCountry ?? ''; // Ensure the country is selected _selectedCountry ?? ''; // Ensure the country is selected
String preferredLang = _selectedLanguage ?? '';
// Create a multipart request // Create a multipart request
final uri = final uri =
@ -352,6 +380,7 @@ class _EditProfileState extends State<EditProfile> {
// Add fields to the request // Add fields to the request
request.fields['country_region'] = request.fields['country_region'] =
countryRegion; // Only update country here countryRegion; // Only update country here
request.fields['language'] = preferredLang == 'English' ? 'en' : 'ar';
// If profile image exists, add it // If profile image exists, add it
if (_profileImage != null) { if (_profileImage != null) {
@ -406,6 +435,7 @@ class _EditProfileState extends State<EditProfile> {
_fullNameController.clear(); _fullNameController.clear();
_dateController.clear(); _dateController.clear();
_selectedCountry = null; _selectedCountry = null;
_selectedLanguage = null;
isChecked = false; isChecked = false;
// Reset profile image // Reset profile image
@ -829,6 +859,73 @@ class _EditProfileState extends State<EditProfile> {
validator: _validateDropdown, validator: _validateDropdown,
), ),
), ),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
AppLocalizations.of(context)!
.preferredLang,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500),
),
],
),
SizedBox(height: 10),
MouseRegion(
onEnter: (_) => setState(
() => _isHoveringPreferredLang = true),
onExit: (_) => setState(
() => _isHoveringPreferredLang = false),
child: DropdownButtonFormField<String>(
style: TextStyle(
fontFamily: 'Roboto',
fontSize: 16,
fontWeight: FontWeight.normal,
color: Color(0xFF544C4C),
),
value: _selectedLanguage,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderRadius:
BorderRadius.circular(10),
borderSide: BorderSide(
color: MyTheme.topicColor(
IndicatorTopic.economy)
.shade400,
width: 1), // Enabled border
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFF7296BE),
),
borderRadius:
BorderRadius.circular(8),
),
labelText: 'Select',
),
icon: Icon(
Icons.keyboard_arrow_down_sharp,
color: _isHoveringPreferredLang
? Colors.black
: Colors.grey),
items: preferred_language
.map(
(item) => DropdownMenuItem<String>(
value: item,
child: Text(item),
),
)
.toList(),
onChanged: (String? newValue) {
setState(() {
_selectedLanguage = newValue;
});
},
validator: _validatePreferredLang,
),
),
SizedBox(height: 20), SizedBox(height: 20),
if (!_isProfileCompleted) // Conditional rendering if (!_isProfileCompleted) // Conditional rendering
Column( Column(

View File

@ -10,6 +10,7 @@ import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart'; import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
import '../../custom_drawer_routes.dart'; import '../../custom_drawer_routes.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class NotificationPage extends ConsumerStatefulWidget { class NotificationPage extends ConsumerStatefulWidget {
@override @override
@ -66,6 +67,7 @@ class _NotificationPageState extends ConsumerState<NotificationPage> {
setState(() { setState(() {
notifications = List<Map<String, dynamic>>.from( notifications = List<Map<String, dynamic>>.from(
jsonResponse['data']); // Assign decoded data jsonResponse['data']); // Assign decoded data
print('notifications $notifications');
}); });
} else { } else {
throw Exception('Failed to load data'); throw Exception('Failed to load data');
@ -100,13 +102,21 @@ class _NotificationPageState extends ConsumerState<NotificationPage> {
final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null
fetchNotifications(localeCode); fetchNotifications(localeCode);
}); });
final List<Map<String, dynamic>> tabs = [
{'title': AppLocalizations.of(context)!.all_updates},
{'title': AppLocalizations.of(context)!.app_updates},
{'title': AppLocalizations.of(context)!.notificationSocial},
{'title': AppLocalizations.of(context)!.notificationEconomy},
{'title': AppLocalizations.of(context)!.notificationEnvironment},
{'title': AppLocalizations.of(context)!.notificationFavourites},
];
// Filter notifications based on the selected tab // Filter notifications based on the selected tab
List<Map<String, dynamic>> filteredNotifications = selectedTabIndex == 0 List<Map<String, dynamic>> filteredNotifications = selectedTabIndex == 0
? notifications ? notifications
: notifications.where((notification) { : notifications.where((notification) {
print( // print("Comparing: '${notification["category"]}' with '${tabs[selectedTabIndex]}'");
"Comparing: '${notification["category"]}' with '${tabs[selectedTabIndex]}'"); return notification["category"] == tabs[selectedTabIndex]['title'];
return notification["category"] == tabs[selectedTabIndex];
}).toList(); }).toList();
return PopScope( return PopScope(
@ -125,7 +135,7 @@ class _NotificationPageState extends ConsumerState<NotificationPage> {
body: Column( body: Column(
children: [ children: [
TabBarHeader( TabBarHeader(
tabs: tabs, tabs: tabs.map((tab) => tab['title'] as String).toList(),
selectedIndex: selectedTabIndex, selectedIndex: selectedTabIndex,
onTabSelected: (index) { onTabSelected: (index) {
setState(() { setState(() {
@ -165,7 +175,9 @@ class _NotificationPageState extends ConsumerState<NotificationPage> {
title: notification['title'] ?? 'No Title', title: notification['title'] ?? 'No Title',
date: formatDate(notification['created'] ?? ''), date: formatDate(notification['created'] ?? ''),
category: notification['category'] ?? 'Unknown', category: notification['category'] ?? 'Unknown',
message: notification['message'] ?? ''), message: notification['message'] ?? '',
id: notification['id'],
),
); );
}, },
), ),
@ -240,23 +252,28 @@ class NotificationTile extends StatelessWidget {
final String message; final String message;
final String date; final String date;
final String category; final String category;
final String id;
const NotificationTile({ const NotificationTile({
required this.title, required this.title,
required this.message, required this.message,
required this.date, required this.date,
required this.category, required this.category,
required this.id,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
final encodedKey = Uri.encodeQueryComponent('notification');
context.go('/notification_details', extra: { context.go('/notification_details', extra: {
'title': title, 'title': title,
'message': message, 'message': message,
'date': date, 'date': date,
'category': category, 'category': category,
'id': id,
'backNavigation': encodedKey
}); });
}, },
child: ListTile( child: ListTile(

View File

@ -1,14 +1,22 @@
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart';
import 'package:pocketbase/pocketbase.dart';
import 'package:uae_stat/config/api_config.dart';
import 'package:uae_stat/domain/use_cases/language.dart'; import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
import '../../custom_drawer_routes.dart';
import 'package:http/http.dart' as http;
class NotificationDetails extends ConsumerWidget { class NotificationDetails extends ConsumerStatefulWidget {
final String title; final String title;
final String message; final String message;
final String date; final String date;
final String category; final String category;
final String id;
final String backNavigation;
const NotificationDetails({ const NotificationDetails({
super.key, super.key,
@ -16,10 +24,85 @@ class NotificationDetails extends ConsumerWidget {
required this.message, required this.message,
required this.date, required this.date,
required this.category, required this.category,
required this.id,
required this.backNavigation,
}); });
@override @override
Widget build(BuildContext context, WidgetRef ref) { _NotificationDetailsState createState() => _NotificationDetailsState();
}
class _NotificationDetailsState extends ConsumerState<NotificationDetails> {
// Example notification data
final _pb = PocketBase(apiUrl);
Map<String, dynamic>? notification;
// Current selected tab index
int selectedTabIndex = 0;
late final Locale locale;
dynamic notificationID;
@override
void initState() {
super.initState();
print(widget.id);
locale = ref.read(localeProvider) ?? const Locale('en');
fetchNotifications(locale?.languageCode ?? 'en');
}
String formatDate(String dateString) {
try {
DateTime dateTime =
DateTime.parse(dateString); // Convert API date to DateTime
return DateFormat('dd MMM yyyy hh:mm a')
.format(dateTime); // Format DateTime
} catch (e) {
return 'Invalid Date'; // Handle parsing errors
}
}
Future<void> fetchNotifications(locale) async {
notificationID = widget.id;
final baseUrl = apiUrl + '/api/getNotification';
try {
final response = await http
.get(Uri.parse('$baseUrl?language=$locale&id=$notificationID'));
if (response.statusCode == 200) {
final Map<String, dynamic> jsonResponse =
jsonDecode(response.body); // Decode JSON
if (jsonResponse['message'] == 'Success') {
setState(() {
final data = jsonResponse['data'];
notification = data;
// if (data is Map<String, dynamic>) {
// notification = data;
// } else {
// notification = null; // Default to null if data is not valid
// }
print('notification $notification');
});
} else {
throw Exception('Failed to load data');
}
} else {
throw Exception(
'Failed to load data with status code ${response.statusCode}');
}
} catch (e) {
print('Error fetching data: $e');
}
}
@override
Widget build(BuildContext context) {
ref.listen<Locale?>(localeProvider, (previous, next) {
final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null
fetchNotifications(localeCode);
});
return PopScope( return PopScope(
canPop: false, canPop: false,
onPopInvokedWithResult: (didPop, result) { onPopInvokedWithResult: (didPop, result) {
@ -34,13 +117,14 @@ class NotificationDetails extends ConsumerWidget {
), ),
), ),
showBackButton: true, showBackButton: true,
navBackArrow: Text(widget.backNavigation ?? 'Default Value'),
body: Padding( body: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
title, notification?['title'] ?? '',
style: const TextStyle( style: const TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -52,7 +136,7 @@ class NotificationDetails extends ConsumerWidget {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Image.asset( Image.asset(
category == 'App updates' notification?['category'] == 'App updates'
? 'assets/backgrounds/Notification/App-Update.png' ? 'assets/backgrounds/Notification/App-Update.png'
: 'assets/backgrounds/Notification/Update_notific.png', : 'assets/backgrounds/Notification/Update_notific.png',
height: 200, height: 200,
@ -62,9 +146,8 @@ class NotificationDetails extends ConsumerWidget {
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
Text( Text(
message.isNotEmpty notification?['message'] ??
? message "No additional details available.",
: "No additional details available.",
style: const TextStyle( style: const TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
@ -72,11 +155,12 @@ class NotificationDetails extends ConsumerWidget {
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
Text( Text(
"Date: $date", "Date: ${notification != null ? formatDate(notification!['created'] ?? '') : 'N/A'}",
style: const TextStyle( style: const TextStyle(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
color: Color(0xFF8E8E8E)), color: Color(0xFF8E8E8E),
),
), ),
], ],
), ),

View File

@ -67,6 +67,7 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
String? userEmail; String? userEmail;
String? userAvatar; String? userAvatar;
String? role; String? role;
String? preferredLanguage;
String? userNames; String? userNames;
String? userEmails; String? userEmails;
String? userAvatars; String? userAvatars;
@ -654,17 +655,23 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
userEmail = userDetailsResponse.data['email'] ?? ''; userEmail = userDetailsResponse.data['email'] ?? '';
userAvatar = userDetailsResponse.data['avatar'] ?? ''; userAvatar = userDetailsResponse.data['avatar'] ?? '';
role = userDetailsResponse.data['role'] ?? ''; role = userDetailsResponse.data['role'] ?? '';
preferredLanguage = userDetailsResponse.data['language'] ?? '';
String recordId = userId; String recordId = userId;
String collectionId = String collectionId =
userDetailsResponse.data['collectionId'] ?? '_pb_users_auth_'; userDetailsResponse.data['collectionId'] ?? '_pb_users_auth_';
if (userAvatar!.isNotEmpty && recordId.isNotEmpty) { if (userAvatar!.isNotEmpty && recordId.isNotEmpty) {
_avatarUrl = _avatarUrl = '$apiUrl/api/files/$collectionId/$recordId/$userAvatar';
'$apiUrl/api/files/$collectionId/$recordId/$userAvatar';
} else { } else {
_avatarUrl = ''; // Reset to default or empty _avatarUrl = ''; // Reset to default or empty
} }
}); });
// **Set Default Locale in Provider**
if (preferredLanguage == 'ar') {
ref.read(localeProvider.notifier).setLocale(const Locale('ar'));
} else {
ref.read(localeProvider.notifier).setLocale(const Locale('en'));
}
} catch (e) { } catch (e) {
print('Error fetching user details: $e'); print('Error fetching user details: $e');
} }
@ -706,7 +713,9 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
title: Padding( title: Padding(
padding: EdgeInsets.only(top: 10), padding: EdgeInsets.only(top: 10),
child: Align( child: Align(
alignment: (locale?.languageCode == 'ar') ? Alignment.centerRight : Alignment.centerLeft, alignment: (locale?.languageCode == 'ar')
? Alignment.centerRight
: Alignment.centerLeft,
child: DefaultTextStyle( child: DefaultTextStyle(
style: TextStyle( style: TextStyle(
color: widget.colorChange ? Colors.white : Color(0xFF414042), color: widget.colorChange ? Colors.white : Color(0xFF414042),
@ -775,12 +784,12 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
color: widget.colorChange ? Colors.white : Colors.black, color: widget.colorChange ? Colors.white : Colors.black,
icon: const Icon(Icons.arrow_back_ios_new, size: 24), icon: const Icon(Icons.arrow_back_ios_new, size: 24),
onPressed: () { onPressed: () {
// if ((widget.navBackArrow as Text).data == 'home') { if ((widget.navBackArrow as Text).data ==
// context.go('/myhomepage'); 'notification') {
// } else { context.go('/notification');
// context.go('/uaenumbers'); } else {
// }
context.pop(); context.pop();
}
}, },
), ),
), ),
@ -1195,7 +1204,7 @@ void _showLogoutConfirmationDialog(BuildContext context) {
), ),
actions: [ actions: [
Row( Row(
mainAxisAlignment:MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
SizedBox( SizedBox(