all bookmark categorized and arabic validation bug

This commit is contained in:
Kalonkarthik 2025-02-26 15:25:51 +05:30
parent cfe5b5bf20
commit 30db38c60b
7 changed files with 448 additions and 139 deletions

View File

@ -1,8 +1,8 @@
{
"all_bookmarks":"كل العلامات المرجعية",
"economy_title":" الاقتصاد",
"economy_title":"اقتصاد",
"social_title":"اجتماعي",
"environment_title":"البيئة",
"environment_title":"بيئة",
"bookmarks": "الإشارات المرجعية",
"feedback_title": "تعليق",
"feedback_failed_msg": "لم يتم إرسال تعليقاتك. يرجى المحاولة مرة أخرى.",

View File

@ -32,7 +32,7 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
bool _obscureNewPassword = true;
bool _obscureConfirmPassword = true;
bool _isPasswordUpdated = false; // Variable to toggle UI
bool hasValidated = false;
String? _oldPassword;
String? _newPassword;
String? _confirmPassword;
@ -242,10 +242,11 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
pathColorWhenOn: Colors.grey.shade300,
pathColorWhenOff: Colors.grey.shade300,
onTap: () {
final formState = _formKey.currentState;
ref.read(localeProvider.notifier).toggleLocale();
Future.delayed(Duration(milliseconds: 100), () {
if (formState?.validate() == false) {
if (hasValidated && formState?.validate() == false) {
formState?.validate();
}
});
@ -581,6 +582,9 @@ class _CreateNewPwState extends ConsumerState<CreateNewPw> {
width: screenWidth / 1.1,
child: ElevatedButton(
onPressed: () async {
setState(() {
});
if ((_formKey.currentState?.validate() ?? false)) {
await updatePassword(
widget.userId,

View File

@ -27,7 +27,7 @@ class _RegisterScreenState extends ConsumerState<RegisterScreen> {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _confirmpasswordController = TextEditingController();
bool hasValidated = false;
bool _obscurePassword = true;
bool _obscureConfirmPassword = true;
bool isChecked = false;
@ -219,6 +219,7 @@ class _RegisterScreenState extends ConsumerState<RegisterScreen> {
if (_formKey.currentState?.validate() ?? false) {
if (isChecked) {
setState(() {
hasValidated = true;
isRegistering = true; // Disable the button
});
try {
@ -386,7 +387,7 @@ class _RegisterScreenState extends ConsumerState<RegisterScreen> {
final formState = _formKey.currentState;
ref.read(localeProvider.notifier).toggleLocale();
Future.delayed(Duration(milliseconds: 100), () {
if (formState?.validate() == false) {
if (hasValidated && formState?.validate() == false) {
formState?.validate();
}
});

View File

@ -36,7 +36,7 @@ class ProfileScreen extends ConsumerStatefulWidget {
class _ProfileScreenState extends ConsumerState<ProfileScreen> {
final _pb = PocketBase('https://pb.venbait.in');
// final _pb = PocketBase('http://127.0.0.1:8090');
bool hasValidated = false;
// Add focus nodes and hint states
final List<FocusNode> _focusNodes = List.generate(4, (_) => FocusNode());
final List<bool> _showHints = [
@ -488,7 +488,13 @@ class _ProfileScreenState extends ConsumerState<ProfileScreen> {
pathColorWhenOn: Colors.grey.shade300,
pathColorWhenOff: Colors.grey.shade300,
onTap: () {
final formState = _formKey.currentState;
ref.read(localeProvider.notifier).toggleLocale();
Future.delayed(Duration(milliseconds: 100), () {
if (hasValidated && formState?.validate() == false) {
formState?.validate();
}
});
},
);
},
@ -947,6 +953,9 @@ class _ProfileScreenState extends ConsumerState<ProfileScreen> {
// ElevatedButton.icon(
ElevatedButton(
onPressed: () {
setState(() {
hasValidated = true;
});
if ((_formKey.currentState?.validate() ?? false) &&
(isChecked)) {
_formKey.currentState?.save();

View File

@ -125,6 +125,7 @@ class LoginRoute extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final hasValidated = useState(false);
final locale = ref.watch(localeProvider);
final emailCtl = useTextEditingController();
final pwCtl = useTextEditingController();
@ -270,6 +271,7 @@ class LoginRoute extends HookConsumerWidget {
width: double.infinity,
child: ElevatedButton(
onPressed: () async {
hasValidated.value=true;
final isValid = formKey.currentState!.validate();
if (!isValid) return;
final session = await context.loaderWithErrorDialog(
@ -677,7 +679,7 @@ class LoginRoute extends HookConsumerWidget {
final formState = formKey.currentState;
ref.read(localeProvider.notifier).toggleLocale();
Future.delayed(Duration(milliseconds: 100), () {
if (formState?.validate() == false) {
if (hasValidated.value && formState?.validate() == false) {
formState?.validate();
}
});

View File

@ -24,19 +24,11 @@ class _BookMarkState extends ConsumerState<BookMark> {
final _pb = PocketBase('https://pb.venbait.in');
List<Map<String, dynamic>> dataList = [];
bool isLoading = true;
Map<String, Map<String, dynamic>> groupedMap = {};
int? expandedIndex = 0;
// Current selected tab index
int selectedTabIndex = 0;
// Tab categories
// final List<String> tabs = [
// 'All Bookmarks',
// 'Economy',
// 'Social',
// 'Environment',
// ];
@override
void initState() {
super.initState();
@ -105,8 +97,36 @@ class _BookMarkState extends ConsumerState<BookMark> {
'id': item['id'],
};
}).toList();
print('dataList $dataList');
// print('dataList $dataList');
});
groupedMap = {};
for (var item in dataList) {
String? mainTopic = item['main_topic'];
String subtitle = item['subtitle'];
// Ignore if main_topic is null
if (mainTopic == null) continue;
// If main_topic doesn't exist in groupedMap, create a new entry
if (!groupedMap.containsKey(mainTopic)) {
groupedMap[mainTopic] = {'main_topic': mainTopic,
'valueColor': item['valueColor'],
'SubTopic': []
};
}
// Add subtitle as a key with its corresponding map value
groupedMap[mainTopic]!['SubTopic'].add ({
'subtitle': subtitle,
'isBookmark': item['isBookmark'],
'value': item['value'],
'title':item['title'],
'value_source':item['value_source'],
'data_set':item['data_set'],
'id': item['id'],
});
}
}
}
} catch (e) {
@ -267,11 +287,11 @@ class _BookMarkState extends ConsumerState<BookMark> {
@override
Widget build(BuildContext context) {
final List<String> tabs = [
AppLocalizations.of(context)!.all_bookmarks,
AppLocalizations.of(context)!.economy_title,
AppLocalizations.of(context)!.social_title,
AppLocalizations.of(context)!.environment_title,
final List<Map<String, dynamic>> tabs = [
{ 'title':AppLocalizations.of(context)!.all_bookmarks, 'color': Colors.black},
{ 'title': AppLocalizations.of(context)!.economy_title, 'color': Colors.black},
{ 'title':AppLocalizations.of(context)!.social_title, 'color': Colors.black},
{ 'title': AppLocalizations.of(context)!.environment_title, 'color': Colors.black},
];
ref.listen<Locale?>(localeProvider, (previous, next) {
@ -281,11 +301,19 @@ class _BookMarkState extends ConsumerState<BookMark> {
List<Map<String, dynamic>> bookmarks = dataList.where((item) => item['isBookmark'] == true).toList();
List<Map<String, dynamic>> filteredBookmarks = dataList
.where((bookmark) =>
bookmark['main_topic'].toString().trim().toLowerCase() == tabs[selectedTabIndex].toString().trim().toLowerCase())
.toList();
.where((bookmark) {
String mainTopic = bookmark['main_topic'].toString().trim().toLowerCase();
String selectedTabTitle = tabs[selectedTabIndex]['title'].toString().trim().toLowerCase();
print('filtered $filteredBookmarks');
// Debugging prints
print("main_topic: '$mainTopic', selected_tab_title: '$selectedTabTitle'");
return mainTopic == selectedTabTitle;
})
.toList();
// print( 'filtered one :${filteredBookmarks['main_topic']} , Tabs : ${tabs[selectedTabIndex]["title"]}');
List<Map<String, dynamic>> transformedList = groupedMap.values.toList();
// print('filtered $filteredBookmarks');
return BaseScaffold(
title: Text(AppLocalizations.of(context)!.bookmarks,),
body: Column(
@ -301,23 +329,68 @@ class _BookMarkState extends ConsumerState<BookMark> {
),
Expanded(
child: selectedTabIndex == 0
? Padding(
padding: const EdgeInsets.all(8.0),
child: GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10.0,
mainAxisSpacing: 10.0,
mainAxisExtent: 100,
),
itemCount: bookmarks.length,
itemBuilder: (context, index) {
return _buildBox(bookmarks[index], context);
},
? (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.isEmpty
)
: 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,
@ -348,7 +421,6 @@ class _BookMarkState extends ConsumerState<BookMark> {
return _buildBox(filteredBookmarks[index], context);
},
),
),
),
],
@ -371,7 +443,7 @@ class _BookMarkState extends ConsumerState<BookMark> {
print(hexColor); // Prints: 0xFFAA8E83
debugPrint("Box clicked: ${data['title']}");
context.go(
context.push(
'/chartScreen/$data_set?bgColor=$hexColor&mainTopic=$encodedMainTopic&title=$encodedTitle');
},
child: Container(
@ -456,10 +528,110 @@ class _BookMarkState extends ConsumerState<BookMark> {
),
);
}
Widget _buildBoxes(Map<String, dynamic> data, BuildContext context, Color styleColor) {
// Color borderColor = data['valueColor'];
print('inside function $data');
return GestureDetector(
onTap: () {
final data_set = data['data_set'];
// final Color colorPattern = data['valueColor'];
final encodedMainTopic = data['main_topic'];
final encodedTitle = data['title'];
String hexColor = colorToHex(styleColor);
debugPrint( '/chartScreen/$data_set?bgColor=$hexColor&mainTopic=$encodedMainTopic&title=$encodedTitle');
// String hexColor = colorToHex(colorPattern); // Get hex string
// print(hexColor); // Prints: 0xFFAA8E83
// debugPrint("Box clicked: ${data['title']}");
context.push(
'/chartScreen/$data_set?bgColor=$hexColor&mainTopic=$encodedMainTopic&title=$encodedTitle');
},
child: Container(
width: MediaQuery.of(context).size.width * 0.4,
height: 100,
padding: const EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
border: Border.all(color: styleColor),
),
child:
Align(
alignment: Alignment.center,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Align(
alignment: Alignment.center,
child: Text(
data['title'] ?? '',
style: const TextStyle(fontWeight: FontWeight.w500),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
),
GestureDetector(
onTap: () {
debugPrint("Icon clicked in: ${data['title']}");
removeBookmark(data['id']);
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(
// content: Text(
// context.translate('Removed From Bookmark','تمت الإزالة من العلامات المرجعية'),
// // style: TextStyle(color: Color(0xFF2F692C),
// // fontWeight: FontWeight.w500),
// ),
// // backgroundColor: Color(0xFFD6E9C6),
// duration: Duration(seconds: 2),
// ),
// );
},
child: SvgPicture.asset(
BookmarkAssetPath.delete,
semanticsLabel: 'share',
width:23,
height: 20,
)
),
],
),
Text(
data['value_source'] ?? '',
style: const TextStyle(
fontWeight: FontWeight.w500, color: Colors.grey),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
Text(
data['value'] ?? '',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: styleColor,
),
),
],
),
),
),
);
}
}
class TabBarHeader extends StatelessWidget {
final List<String> tabs;
final List<Map<String, dynamic>> tabs;
final int selectedIndex;
final ValueChanged<int> onTabSelected;
@ -480,7 +652,7 @@ class TabBarHeader extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.start,
children: tabs.asMap().entries.map((entry) {
int index = entry.key;
String text = entry.value;
String text = entry.value['title'];
return Row(
children: [
TextButton(
@ -505,7 +677,7 @@ class TabBarHeader extends StatelessWidget {
}
Widget buildDivider() {
return Container(
return SizedBox(
height: 20,
child: const VerticalDivider(
color: Colors.grey,
@ -514,4 +686,124 @@ class TabBarHeader extends StatelessWidget {
),
);
}
}
}
class CustomExpandableTile extends StatefulWidget {
final String title;
final Color titleBackgroundColor;
final List filteredBookmarks;
final int index;
final bool isExpanded;
final ValueChanged<int> onTap;
final Widget childWidget;
const CustomExpandableTile({
required this.title,
required this.titleBackgroundColor,
required this.filteredBookmarks,
required this.index,
required this.isExpanded,
required this.onTap,
required this.childWidget,
});
@override
_CustomExpandableTileState createState() => _CustomExpandableTileState();
}
class _CustomExpandableTileState extends State<CustomExpandableTile> {
@override
void initState() {
super.initState(); // Initialize isExpanded based on widget's property
}
@override
Widget build(BuildContext context) {
double myheight = MediaQuery.of(context).size.height;
// print(widget.filteredBookmarks);
return Card(
elevation: 0,
child: Column(
children: [
GestureDetector(
onTap: () => widget.onTap(widget.index),
child: Container(
decoration: BoxDecoration(
color: Colors.white, // Ensuring the background outside the rounded container is white
),
child: Container(
decoration: BoxDecoration(
color: widget.titleBackgroundColor,
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(
widget.title,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 20,
),
),
Icon(
widget.isExpanded
? Icons.keyboard_arrow_up
: Icons.keyboard_arrow_down,
color: Colors.white,
size: 28.0,
),
],
),
),
),
),
ClipRRect(
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
width: double.infinity,
color: Colors.white,
height:widget.isExpanded ? myheight * 0.4 : 0,
child:widget.isExpanded
? SingleChildScrollView(
child: Column(
children: [
widget.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: widget.filteredBookmarks.length,
// itemBuilder: (context, index) {
// return _buildBox(widget.filteredBookmarks[index], context,widget.titleBackgroundColor);
// },
// ),
// ),
],
),
)
: null,
),
),
],
),
);
}
}

View File

@ -7,7 +7,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class Userguide extends StatefulWidget {
const Userguide({super.key});
Userguide({super.key});
@override
State<Userguide> createState() => _UserguideState();
@ -20,6 +20,7 @@ class _UserguideState extends State<Userguide> {
{'routePath':'faq','color': Color(0xFF7296BE), 'text': 'FAQs', 'icon': 'mdi:faq', 'text-ar': 'لأسئلة الشائعة'},
];
@override
Widget buildDynamicWidget(dynamic icons, Color color) {
if (icons is IconData) {
return Icon(
@ -46,41 +47,41 @@ class _UserguideState extends State<Userguide> {
}
}
@override
Widget build(BuildContext context) {
return PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, result) {
if (didPop) return;
context.go('/myhomepage');
},
child: BaseScaffold(
showDivider: true,
bottomColor: Colors.white,
dividerColor: Colors.grey[300],
appbarColor: Colors.white,
title: Text(AppLocalizations.of(context)!.guide_title) ,
body: Container(
color: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 16 , vertical: 20),
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, // Two columns
crossAxisSpacing: 10.0,
mainAxisSpacing: 10.0,
mainAxisExtent:MediaQuery.of(context).size.height* 0.15 ,
canPop: false,
onPopInvokedWithResult: (didPop, result) {
if (didPop) return;
context.go('/myhomepage');
},
child: BaseScaffold(
showDivider: true,
bottomColor: Colors.white,
dividerColor: Colors.grey[300],
appbarColor: Colors.white,
title: Text(AppLocalizations.of(context)!.guide_title) ,
body: Container(
color: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 16 , vertical: 20),
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10.0,
mainAxisSpacing: 10.0,
childAspectRatio: 1.7,
// mainAxisExtent:MediaQuery.of(context).size.height* 0.15 ,
),
itemCount: guideList.length,
itemBuilder: (context, index) {
return HoverContainer(
iconName: guideList[index]['icon'],
title: context.translate(guideList[index]['text'], guideList[index]['text-ar']),
routePath: guideList[index]['routePath'],
);
},
),
itemCount: guideList.length,
itemBuilder: (context, index) {
return HoverContainer(
iconName: guideList[index]['icon'],
title: context.translate(guideList[index]['text'], guideList[index]['text-ar']),
routePath: guideList[index]['routePath'],
);
},
),
),
),
);
}
@ -108,69 +109,69 @@ class _HoverContainerState extends State<HoverContainer> {
return GestureDetector(
onTap: () { context.push('/user-guide/${widget.routePath}');},
child: MouseRegion(
onEnter: (_) => setState(() => isHovered = true),
onExit: (_) => setState(() => isHovered = true),
child : Container(
padding: EdgeInsets.only(top: 4, right: 4, left: 4, bottom: 6),
height: screenHeight* 0.23 ,
width: screenWidth*0.30,
margin: EdgeInsets.all(1),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: isHovered
? [
BoxShadow(
color: Colors.black.withValues(alpha:0.2),
blurRadius:2,
spreadRadius: 1,
offset: Offset(0, 4),
),
]
: [],
border: Border.all(
color: Color(0xFF7DAFBC),
width: 1.0,
),
),
alignment: Alignment.center,
// duration: Duration(milliseconds: 200),
onEnter: (_) => setState(() => isHovered = true),
onExit: (_) => setState(() => isHovered = true),
child : Container(
padding: EdgeInsets.only(top: 4, right: 4, left: 4, bottom: 6),
child: LayoutBuilder(builder: (context, constraints){
double containerWidth = constraints.maxWidth;
// double containerHeight = constraints.maxHeight;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconifyIcon(icon: widget.iconName,
color: Color((0xFF7DAFBC)),
size: containerWidth * 0.25,),
// buildDynamicWidget(data['icon'], Color(0xFF7DAFBC)),
SizedBox(width: 10,height: containerWidth * 0.04,), // Space between icon and text
Container(
width: containerWidth* 0.85,
padding: EdgeInsets.symmetric(horizontal: 4, vertical: 4),
margin: EdgeInsets.only(bottom: 3),
decoration: BoxDecoration(
color: Color(0xFF7DAFBC),
borderRadius: BorderRadius.circular(5),
border: Border.all(color: Color(0xFF7DAFBC)),
),
child: Text(
widget.title,
style: TextStyle(
color: Colors.white, // Text color
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
width: screenWidth*0.30,
margin: EdgeInsets.all(1),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: isHovered
? [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius:2,
spreadRadius: 1,
offset: Offset(0, 4),
),
]
: [],
border: Border.all(
color: Color(0xFF7DAFBC),
width: 1.0
),
],
);
},),
),
alignment: Alignment.center,
// duration: Duration(milliseconds: 200),
child: LayoutBuilder(builder: (context, constraints){
double containerWidth = constraints.maxWidth;
double containerHeight = constraints.maxHeight;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconifyIcon(icon: widget.iconName,
color: Color((0xFF7DAFBC)),
size: containerHeight * 0.45,),
// buildDynamicWidget(data['icon'], Color(0xFF7DAFBC)),
SizedBox(width: 10,height: containerHeight * 0.04,), // Space between icon and text
Container(
width: containerWidth* 0.85,
padding: EdgeInsets.symmetric(horizontal: 4, vertical: 4),
margin: EdgeInsets.only(bottom: 3),
decoration: BoxDecoration(
color: Color(0xFF7DAFBC),
borderRadius: BorderRadius.circular(5),
border: Border.all(color: Color(0xFF7DAFBC))
),
child: Text(
widget.title,
style: TextStyle(
color: Colors.white, // Text color
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
),
],
);
})
),
),
),