diff --git a/lib/config/my_router.dart b/lib/config/my_router.dart index 70b4fa22..6449fff3 100644 --- a/lib/config/my_router.dart +++ b/lib/config/my_router.dart @@ -372,14 +372,20 @@ final GoRouter router = GoRouter( // Retrieve query parameter final bgColor = state.uri.queryParameters['bgColor'] ?? '0xFFFFFFFF'; // Default white + final mainTopic = state.uri.queryParameters['mainTopic'] ?? ''; + final title = state.uri.queryParameters['title'] ?? ''; print('Router dataSets: $dataSets'); print('Router bgColor: $bgColor'); + print('Router mainTopic: $mainTopic'); + print('Router title: $title'); // Pass both values to the screen return ChartScreen1( dataSets: dataSets, bgColor: bgColor, + mainTopic: mainTopic, + title: title, ); }, ), diff --git a/lib/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/uae_numbers.dart b/lib/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/uae_numbers.dart index 258caedc..190d010f 100644 --- a/lib/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/uae_numbers.dart +++ b/lib/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/uae_numbers.dart @@ -102,12 +102,8 @@ class _UaenumberWidgetState extends State { isExpanded: isFirstTile, title: mainTopic['main_topic'], titleBackgroundColor: backgroundColor, - children: _buildSubTopics( - mainTopic['sub_topics'] ?? [], - mywidth, - borderColor, - colorPattern, - ), + children: _buildSubTopics(mainTopic['sub_topics'] ?? [], mywidth, + borderColor, colorPattern, mainTopic['main_topic']), ); }), ], @@ -116,7 +112,7 @@ class _UaenumberWidgetState extends State { } List _buildSubTopics(List subTopics, double myWidth, - Color borderColor, String colorPattern) { + Color borderColor, String colorPattern, mainTopic) { print('colorPattern123 $borderColor'); // Sort sub-topics based on `sub_topic_list_order` subTopics.sort((a, b) => (a['sub_topic_list_order'] ?? 0) @@ -128,14 +124,14 @@ class _UaenumberWidgetState extends State { children: [ buildCategoryTitle(subTopic['sub_topic'], borderColor), ..._buildDataSetCards(subTopic['tile_data'] ?? [], myWidth, - borderColor, colorPattern), + borderColor, colorPattern, mainTopic), ], )) .toList(); } - List _buildDataSetCards( - List tileData, double myWidth, Color borderColor, colorPattern) { + List _buildDataSetCards(List tileData, double myWidth, + Color borderColor, colorPattern, mainTopic) { // Sort datasets based on `data_set_list_order` tileData.sort((a, b) => (a['data_set_list_order'] ?? 0) .compareTo(b['data_set_list_order'] ?? 0)); @@ -162,16 +158,17 @@ class _UaenumberWidgetState extends State { padding: const EdgeInsets.symmetric(horizontal: 5.0), // Add spacing child: buildCard( - context, - data['data_set_tile_heading'] ?? '', - data['value'] ?? '', - data['value_source'] ?? '', - data['data_set'] ?? '', - borderColor, - borderColor, - colorPattern, - cardWidth, // Pass the calculated width - ), + context, + data['data_set_tile_heading'] ?? '', + data['value'] ?? '', + data['value_source'] ?? '', + data['data_set'] ?? '', + borderColor, + borderColor, + colorPattern, + cardWidth, + mainTopic // Pass the calculated width + ), ), ); }).toList(), @@ -211,20 +208,29 @@ class _UaenumberWidgetState extends State { } Widget buildCard( - BuildContext context, - String title, - String value, - String subtitle, - String data_set, - Color bordercolor, - Color boldColor, - colorPattern, - double mywidth, - ) { + BuildContext context, + String title, + String value, + String subtitle, + String data_set, + Color bordercolor, + Color boldColor, + colorPattern, + double mywidth, + String mainTopic) { return GestureDetector( onTap: () { - print(colorPattern); - context.go('/chartScreen/$data_set?bgColor=$colorPattern'); + // print('checking data'); + // print(mainTopic); + // print(title); + // print(subtitle); + // print(data_set); + final encodedMainTopic = Uri.encodeComponent(mainTopic); + final encodedTitle = Uri.encodeComponent(title); + + // Pass mainTopic and title as query parameters + context.go( + '/chartScreen/$data_set?bgColor=$colorPattern&mainTopic=$encodedMainTopic&title=$encodedTitle'); // context.go('/chartScreen/$data_set'); }, child: Card( diff --git a/lib/presentation/Screens/auth_verification/registration.dart b/lib/presentation/Screens/auth_verification/registration.dart index 2683cde7..b6ef1088 100644 --- a/lib/presentation/Screens/auth_verification/registration.dart +++ b/lib/presentation/Screens/auth_verification/registration.dart @@ -297,15 +297,16 @@ class _RegisterScreenState extends State { 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: (){ - ref.read(localeProvider.notifier).toggleLocale(); - },); + return MyToggle( + isOn: locale?.languageCode == 'en', + knobTextWhenOn: 'ع', + knobTextWhenOff: 'EN', + pathColorWhenOn: Colors.grey.shade300, + pathColorWhenOff: Colors.grey.shade300, + onTap: () { + ref.read(localeProvider.notifier).toggleLocale(); + }, + ); // IconButton( // icon: Icon(locale?.languageCode == 'en' // ? Icons.toggle_off_outlined @@ -329,7 +330,7 @@ class _RegisterScreenState extends State { buildIconContainer(Icons.report, Color(0xFF7DAFBC)), SizedBox(height: 20), Text( - "Your registration is pending for Admin Approval.", + "Your registration is pending for verification.", textAlign: TextAlign.center, style: TextStyle( fontSize: 16, @@ -338,7 +339,7 @@ class _RegisterScreenState extends State { ), SizedBox(height: 10), Text( - "Access will be granted once your account is approved.", + "Kindly verify your mail to proceed further.", textAlign: TextAlign.center, style: TextStyle( fontSize: 14, @@ -452,7 +453,9 @@ class _RegisterScreenState extends State { focusNode: _focusNodes[0], decoration: InputDecoration( // hintText: _showHints[0] ? 'Username' : null, - hintText: _showHints[0] ? AppLocalizations.of(context)!.register_name : null, + hintText: _showHints[0] + ? AppLocalizations.of(context)!.register_name + : null, prefixIcon: Icon( Icons.person, color: Colors.blue, @@ -471,8 +474,9 @@ class _RegisterScreenState extends State { focusNode: _focusNodes[1], decoration: InputDecoration( // hintText: 'Enter your email', - hintText: - _showHints[1] ? AppLocalizations.of(context)!.register_email : null, + hintText: _showHints[1] + ? AppLocalizations.of(context)!.register_email + : null, // _showHints[1] ? 'Enter your email' : null, prefixIcon: Icon( Icons.email, @@ -495,8 +499,10 @@ class _RegisterScreenState extends State { focusNode: _focusNodes[2], obscureText: _obscurePassword, decoration: InputDecoration( - hintText: - _showHints[2] ? AppLocalizations.of(context)!.register_password : null, + hintText: _showHints[2] + ? AppLocalizations.of(context)! + .register_password + : null, // _showHints[2] ? 'Enter your password' : null, prefixIcon: Icon( Icons.lock, @@ -528,8 +534,10 @@ class _RegisterScreenState extends State { focusNode: _focusNodes[3], obscureText: _obscureConfirmPassword, decoration: InputDecoration( - hintText: - _showHints[3] ? AppLocalizations.of(context)!.register_Confirm_password : null, + hintText: _showHints[3] + ? AppLocalizations.of(context)! + .register_Confirm_password + : null, // _showHints[3] ? 'Confirm password' : null, prefixIcon: Icon( Icons.lock, @@ -583,16 +591,22 @@ class _RegisterScreenState extends State { // text: 'I agree to ', children: [ TextSpan( - text: AppLocalizations.of(context)!.terms_conditions, + text: AppLocalizations.of(context)! + .terms_conditions, // text: 'Terms & Conditions', style: TextStyle(color: Colors.blue), ), - TextSpan(text: AppLocalizations.of(context)!.t_and), TextSpan( - text: AppLocalizations.of(context)!.privacy_policy, + text: AppLocalizations.of(context)! + .t_and), + TextSpan( + text: AppLocalizations.of(context)! + .privacy_policy, style: TextStyle(color: Colors.blue), ), - TextSpan(text: AppLocalizations.of(context)!.conditions), + TextSpan( + text: AppLocalizations.of(context)! + .conditions), ], ), ), @@ -632,7 +646,8 @@ class _RegisterScreenState extends State { children: [ Text( // "Register", - AppLocalizations.of(context)!.register_title, + AppLocalizations.of(context)! + .register_title, style: TextStyle( fontSize: 16, color: Colors.white), ), diff --git a/lib/presentation/Screens/charts/screens/chart_screen.dart b/lib/presentation/Screens/charts/screens/chart_screen.dart index 360e9d7a..3682dcb4 100644 --- a/lib/presentation/Screens/charts/screens/chart_screen.dart +++ b/lib/presentation/Screens/charts/screens/chart_screen.dart @@ -8,9 +8,16 @@ import '../filters/search_filter_helper.dart'; class ChartScreen1 extends StatefulWidget { final String dataSets; final String bgColor; + final String mainTopic; + final String title; - const ChartScreen1( - {Key? key, required this.dataSets, required String this.bgColor}); + const ChartScreen1({ + Key? key, + required this.dataSets, + required String this.bgColor, + required this.mainTopic, + required this.title, + }); @override _ChartScreen1State createState() => _ChartScreen1State(); } @@ -24,17 +31,89 @@ class _ChartScreen1State extends State { List nonChartData = []; List filterData = []; List chartsData = []; + List tabFilteredChartData = []; + List tabFilteredCardData = []; List cardData = []; List originalChartsData = []; List originalCardData = []; + List originalTabCardData = []; + List originalTabChartsData = []; late Color backgroundColor; + final ScrollController _scrollController = ScrollController(); + int _activeTabIndex = 0; + + dynamic _tabsData = []; @override void initState() { super.initState(); final String bgColor = widget.bgColor; print(' bgColor $bgColor'); - fetchChartData(widget.dataSets); + // fetchChartData(widget.dataSets); + fetchChartData(widget.dataSets).then((_) { + if (_tabsData.isNotEmpty) { + // Call onTabSelected for the first tab + onTabSelected(_tabsData[0]['id']); + } + }); + } + + void _scrollToIndex(int index) { + double scrollPosition = index * 120.0; // Adjust based on tab size + _scrollController.animateTo( + scrollPosition, + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); + } + + void _scrollLeft() { + if (_activeTabIndex > 0) { + setState(() { + _activeTabIndex--; + }); + _scrollToIndex(_activeTabIndex); + onTabSelected(_tabsData[_activeTabIndex]['id']!); // Pass the tab's id + } + } + + void _scrollRight() { + if (_activeTabIndex < _tabsData.length - 1) { + setState(() { + _activeTabIndex++; + }); + _scrollToIndex(_activeTabIndex); + onTabSelected(_tabsData[_activeTabIndex]['id']!); // Pass the tab's id + } + } + + void processChartData(chartsData) { + // Group data by 'kpi' + Map>> groupedData = {}; + for (var chart in chartsData) { + String kpi = chart['kpi'] ?? ''; + if (!groupedData.containsKey(kpi)) { + groupedData[kpi] = []; + } + groupedData[kpi]!.add(chart); + } + + // Format 'kpi' values for _tabs + List> _tabs = groupedData.keys.map((kpi) { + String name = kpi + .split('_') // Split by underscore + .map( + (word) => word[0].toUpperCase() + word.substring(1)) // Capitalize + .join(' '); // Join words with space + + return {'id': kpi, 'name': name}; + }).toList(); + + print('Grouped Data: $groupedData'); + print('Tabs: $_tabs'); + setState(() { + _tabsData = _tabs; + }); } Future fetchChartData(String dataSets) async { @@ -49,6 +128,9 @@ class _ChartScreen1State extends State { chartsData = isChartData; cardData = nonChartData; + + processChartData(chartsData); + print('chartsData :- $chartsData'); print('cardData :- $cardData'); print('filterData :- $filterData'); @@ -64,8 +146,9 @@ class _ChartScreen1State extends State { // Check if all filter_data is empty if (selectedFilters.every((filter) => filter['filter_data'].isEmpty)) { setState(() { - chartsData = List.from(originalChartsData); // Restore the original data - cardData = List.from(originalCardData); // Restore the original data + chartsData = + List.from(originalTabChartsData); // Restore the original data + cardData = List.from(originalTabCardData); // Restore the original data }); print('Returning as no filters are selected'); Navigator.pop(context); @@ -151,6 +234,14 @@ class _ChartScreen1State extends State { Navigator.pop(context); } + String capitalizeAndSplit(String text) { + if (text.isEmpty) return text; + return text + .split('_') // Split by underscores + .map((word) => word[0].toUpperCase() + word.substring(1).toLowerCase()) + .join(' '); // Join words with a space + } + void showRightSideModal(BuildContext context, List filters, List data) { // Initialize selected filters structure from the stored value, if exists List> selectedFilters = @@ -215,7 +306,7 @@ class _ChartScreen1State extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - filterKey, + capitalizeAndSplit(filterKey), style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, @@ -330,8 +421,8 @@ class _ChartScreen1State extends State { filter["filter_data"].clear(); }); setState(() { - chartsData = List.from(originalChartsData); - cardData = List.from(originalCardData); + chartsData = List.from(originalTabChartsData); + cardData = List.from(originalTabCardData); }); // Reset the selectedFiltersStorage to empty when clearing selectedFiltersStorage.clear(); @@ -347,8 +438,8 @@ class _ChartScreen1State extends State { print( "Selected Filters before applying: $selectedFilters"); setState(() { - chartsData = originalChartsData; - cardData = originalCardData; + chartsData = originalTabChartsData; + cardData = originalTabCardData; }); applyFilters(context, filters, chartsData, cardData, @@ -372,8 +463,68 @@ class _ChartScreen1State extends State { ); } + void onTabSelected(String tabId) { + print('Selected Tab: $tabId'); + selectedFiltersStorage.clear(); + setState(() { + chartsData = originalChartsData; + cardData = originalCardData; + }); + performActionForTab(tabId); + } + + void performActionForTab(String tabId) { + print('Selected Tab: $tabId'); + + // Filter the chartsData array based on the tabId + tabFilteredChartData = chartsData.where((chart) { + // Assuming each chart item has a 'kpi' field that matches tabId + return chart['kpi'] == tabId; + }).toList(); // Convert to list after filtering + + print('tabFilteredChartData Data for Tab $tabId: $tabFilteredChartData'); + + setState(() { + chartsData = tabFilteredChartData; + originalTabChartsData = + List.from(tabFilteredChartData); // Store original data + }); + + // Filter the chartsData array based on the tabId + tabFilteredCardData = cardData.where((chart) { + // Assuming each chart item has a 'kpi' field that matches tabId + return chart['kpi'] == tabId; + }).toList(); // Convert to list after filtering + + print('tabFilteredCardData Data for Tab $tabId: $tabFilteredCardData'); + + setState(() { + cardData = tabFilteredCardData; + originalTabCardData = List.from(tabFilteredCardData); + }); + + // You can perform further actions with the filteredData, like updating the UI + // For example, you can update the chart data or display the results + } + + double calculateAspectRatio(int itemCount) { + // Modify the logic based on your layout requirements + if (itemCount <= 2) { + return 190.5 / 180; + } else if (itemCount == 3) { + return 180.0 / 300; + } else if (itemCount == 4) { + return 190.5 / 180; + } else { + return 180.0 / 260; // Default for more items + // return 190.5 / 180; + } + } + @override Widget build(BuildContext context) { + double myheight = MediaQuery.of(context).size.height; + double mywidth = MediaQuery.of(context).size.width; final color = Color(int.parse(widget.bgColor.replaceFirst('0x', ''), radix: 16)); return Scaffold( @@ -385,7 +536,7 @@ class _ChartScreen1State extends State { leading: IconButton( icon: Icon(Icons.arrow_back_ios_new, color: Colors.white), onPressed: () { - context.go('/myhomepage'); + context.go('/uaenumbers'); }, ), title: Text( @@ -397,6 +548,55 @@ class _ChartScreen1State extends State { ? Center(child: CircularProgressIndicator()) : Column( children: [ + Container( + // height: myheight / 5, + width: double.infinity, + color: color, + child: Padding( + padding: EdgeInsets.only( + left: 30, right: 30, top: 10, bottom: 5), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + widget.mainTopic ?? '', + style: TextStyle( + color: Colors.white, + fontSize: 26, + ), + ), + Container( + width: mywidth / 4, + child: Divider( + thickness: 8, + color: Colors.white, + )), + Text( + widget.title, + style: TextStyle(color: Colors.white, fontSize: 20), + ), + // Row( + // mainAxisAlignment: MainAxisAlignment.end, + // children: [ + // IconButton( + // onPressed: () {}, + // icon: Icon( + // Icons.bookmark_add_outlined, + // color: Colors.white, + // size: 40, + // )), + // IconButton( + // onPressed: () {}, + // icon: Icon( + // Icons.share, + // color: Colors.white, + // size: 30, + // )), + // ], + // ), + ], + ), + )), Expanded( child: ListView( children: [ @@ -404,8 +604,7 @@ class _ChartScreen1State extends State { crossAxisAlignment: CrossAxisAlignment.end, children: [ Row( - mainAxisAlignment: MainAxisAlignment - .end, // Aligns the row's content to the right + mainAxisAlignment: MainAxisAlignment.end, children: [ Row( children: [ @@ -416,12 +615,12 @@ class _ChartScreen1State extends State { color: Colors.white, ), ), + SizedBox(width: 5), Icon(Icons.bookmark_add_outlined, color: Colors.white, size: 18), ], ), - SizedBox( - width: 10), // Horizontal space between items + SizedBox(width: 10), Row( children: [ Text( @@ -431,15 +630,16 @@ class _ChartScreen1State extends State { color: Colors.white, ), ), + SizedBox(width: 5), Icon(Icons.share_sharp, color: Colors.white, size: 18), ], ), SizedBox( - width: 10), // Horizontal space between items + width: 5), // Horizontal space between items IconButton( icon: Icon(Icons.filter_alt_outlined, - color: Colors.white, size: 18), + color: Colors.white, size: 28), onPressed: () { showRightSideModal( context, filterData, chartsData); @@ -449,136 +649,186 @@ class _ChartScreen1State extends State { ), ], ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // Left arrow button + _buildArrowButton( + onPressed: _activeTabIndex > 0 ? _scrollLeft : null, + icon: Icons.arrow_back_ios_new, + ), + // Tabs with horizontal scroll + Expanded( + child: SingleChildScrollView( + controller: _scrollController, + scrollDirection: Axis.horizontal, + child: Row( + children: + List.generate(_tabsData.length, (index) { + return _buildTab( + _tabsData[index], + isActive: index == _activeTabIndex, + ); + }), + ), + ), + ), + // Right arrow button + _buildArrowButton( + onPressed: _activeTabIndex < _tabsData.length - 1 + ? _scrollRight + : null, + icon: Icons.arrow_forward_ios, + ), + ], + ), + // Expanded( + // child: Center( + // child: Text( + // 'Selected Tab: ${_tabsData.isNotEmpty ? _tabsData[_activeTabIndex] : 'None'}', + // style: const TextStyle(fontSize: 18), + // ), + // ), + // ), // nonChartData Cards - GridView.builder( - itemCount: cardData.length, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - gridDelegate: - const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, // 2 cards per row - crossAxisSpacing: 10, - mainAxisSpacing: 10, - childAspectRatio: 190.5 / - 200, // Adjusted to maintain width and fixed height - ), - itemBuilder: (context, index) { - final item = cardData[index]; - print('item item item $item'); - final chart_type = item['chart_type']; - final chart_heading = item['chart_heading']; - final data = apiService.processNonChartData(item); - print('processNonChartData'); + Padding( + padding: const EdgeInsets.all(3.34), + child: GridView.builder( + itemCount: cardData.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: + SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: cardData.length == 2 + ? 2 + : cardData.length == 3 + ? 3 + : cardData.length == 4 + ? 2 + : 3, // Default to 3 if more than 4 items // 2 cards per row + crossAxisSpacing: 2, + mainAxisSpacing: 5, + childAspectRatio: + calculateAspectRatio(cardData.length)), + itemBuilder: (context, index) { + final item = cardData[index]; + print('item item item $item'); + final chart_type = item['chart_type']; + final chart_heading = item['chart_heading']; + final data = apiService.processNonChartData(item); + print('processNonChartData'); - if (chart_type == 'total') { - return Card( - margin: const EdgeInsets.all(10), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - elevation: 4, - child: Padding( - padding: const EdgeInsets.all(10.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon(Icons.public, - color: Color(0xFF90B0D5), size: 30), - const SizedBox(height: 3), - Text( - '${chart_heading ?? 'NA'}', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 11, - fontWeight: FontWeight.w400, - color: Colors.black87, - ), - ), - Text( - '(${data['lastYear'] ?? 'NA'})', - style: const TextStyle( - fontSize: 11, color: Colors.grey), - ), - Text( - apiService - .formatAmount(data['lastYearValue']), - style: const TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Color(0xFF90B0D5), - ), - ), - SizedBox( - height: 5, // Height of the divider - child: Divider( - color: Colors.grey, - thickness: 1, // Divider line thickness - ), - ), - Text( - apiService.formatAmount( - data['secondLastYearValue']), - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Color(0xFFD83731), - ), - ), - Text( - '(${data['secondLastYear'] ?? 'NA'})', - style: const TextStyle( - fontSize: 11, color: Colors.grey), - ), - ], + if (chart_type == 'total') { + return Card( + margin: const EdgeInsets.all(10), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), ), - ), - ); - } else if (chart_type == 'average') { - return Card( - margin: const EdgeInsets.all(10), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - elevation: 4, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon(Icons.analytics, - color: Color(0xFF90B0D5), size: 30), - const SizedBox(height: 5), - Text( - '${chart_heading ?? 'NA'}', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 11, - fontWeight: FontWeight.w400, - color: Colors.black87, + elevation: 4, + child: Padding( + padding: const EdgeInsets.all(10.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.public, + color: Color(0xFF90B0D5), size: 30), + const SizedBox(height: 3), + Text( + '${chart_heading ?? 'NA'}', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 11, + fontWeight: FontWeight.w400, + color: Colors.black87, + ), ), - ), - Text( - '(${data['firstYear'] ?? 'NA'} - ${data['lastYear'] ?? 'NA'})', - style: const TextStyle( - fontSize: 11, color: Colors.grey), - ), - Text( - '${data['roundedAverage'] ?? 'NA'}', - style: const TextStyle( - fontSize: 26, - fontWeight: FontWeight.w900, - color: Color(0xFF11AF22), + Text( + '(${data['lastYear'] ?? 'NA'})', + style: const TextStyle( + fontSize: 11, color: Colors.grey), ), - ), - ], + Text( + apiService.formatAmount( + data['lastYearValue']), + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w900, + color: Color(0xFF90B0D5), + ), + ), + SizedBox( + height: 2, // Height of the divider + child: Divider( + color: Colors.grey, + thickness: + 1, // Divider line thickness + ), + ), + Text( + apiService.formatAmount( + data['secondLastYearValue']), + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFFD83731), + ), + ), + Text( + '(${data['secondLastYear'] ?? 'NA'})', + style: const TextStyle( + fontSize: 11, color: Colors.grey), + ), + ], + ), ), - ), - ); - } else { - return const SizedBox - .shrink(); // Ignore unknown KPIs - } - }, + ); + } else if (chart_type == 'average') { + return Card( + margin: const EdgeInsets.all(10), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 4, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.analytics, + color: Color(0xFF90B0D5), size: 30), + const SizedBox(height: 5), + Text( + '${chart_heading ?? 'NA'}', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 11, + fontWeight: FontWeight.w400, + color: Colors.black87, + ), + ), + Text( + '(${data['firstYear'] ?? 'NA'} - ${data['lastYear'] ?? 'NA'})', + style: const TextStyle( + fontSize: 11, color: Colors.grey), + ), + Text( + '${data['roundedAverage'] ?? 'NA'}', + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.w900, + color: Color(0xFF90B0D5), + ), + ), + ], + ), + ), + ); + } else { + return const SizedBox + .shrink(); // Ignore unknown KPIs + } + }, + ), ), // chartsData Cards ListView.builder( @@ -612,4 +862,58 @@ class _ChartScreen1State extends State { ), ); } + + Widget _buildArrowButton( + {required VoidCallback? onPressed, required IconData icon}) { + final color = + Color(int.parse(widget.bgColor.replaceFirst('0x', ''), radix: 16)); + return Container( + margin: EdgeInsets.symmetric(horizontal: 5.0), + width: 35.0, // Set the width of the circle + height: 35.0, + decoration: BoxDecoration( + color: onPressed == null ? Colors.grey[400] : Colors.white, + shape: BoxShape.circle, + // boxShadow: [ + // BoxShadow( + // color: Colors.black26, + // blurRadius: 4.0, + // offset: Offset(2, 2), + // ), + // ], + ), + child: IconButton( + onPressed: onPressed, + icon: Icon(icon, color: onPressed == null ? Colors.grey : color), + iconSize: 15.0, + ), + ); + } + + Widget _buildTab(Map tab, {bool isActive = false}) { + return GestureDetector( + onTap: () { + setState(() { + _activeTabIndex = _tabsData.indexOf(tab); + }); + _scrollToIndex(_activeTabIndex); + onTabSelected(tab['id']!); + }, + child: Container( + margin: EdgeInsets.symmetric(horizontal: 8.0), + padding: EdgeInsets.symmetric(vertical: 5.0, horizontal: 16.0), + decoration: BoxDecoration( + color: isActive ? Colors.white : Colors.grey[400], + borderRadius: BorderRadius.circular(20.0), + ), + child: Text( + tab['name']!, + style: TextStyle( + color: isActive ? Colors.black : Colors.white, + fontWeight: isActive ? FontWeight.w500 : FontWeight.w400, + ), + ), + ), + ); + } } diff --git a/lib/presentation/Screens/charts/widgets/chart_widget.dart b/lib/presentation/Screens/charts/widgets/chart_widget.dart index ddff96f3..8061a39a 100644 --- a/lib/presentation/Screens/charts/widgets/chart_widget.dart +++ b/lib/presentation/Screens/charts/widgets/chart_widget.dart @@ -294,46 +294,64 @@ class ChartWidget extends StatelessWidget { aspectRatio: 1.5, child: BarChart( BarChartData( - alignment: BarChartAlignment.spaceBetween, + alignment: BarChartAlignment.start, barTouchData: BarTouchData( touchTooltipData: BarTouchTooltipData( + // tooltipBgColor: Colors.black.withOpacity(0.8), + fitInsideHorizontally: true, + fitInsideVertically: false, + tooltipPadding: const EdgeInsets.all(8), + tooltipMargin: 16, getTooltipItem: (groupData, groupIndex, rodData, rodIndex) { - // print('groupData $groupData'); - // print('groupIndex $groupIndex'); - // print('rodData $rodData'); - // print('rodIndex $rodIndex'); - // Assume groupedData is passed as a parameter - // List timePeriods = - // groupedData.values.first.keys.toList(); - // String timePeriod = timePeriods[groupIndex]; - // List rodStackItems = - // rodData.rodStackItems; - // - // String tooltipText = ''; - // for (int i = 0; i < rodStackItems.length; i++) { - // double fromY = rodStackItems[i].fromY; - // double toY = rodStackItems[i].toY; - // double value = toY - fromY; - // String groupName = groupedData.keys.elementAt(i); - // - // tooltipText += - // '$timePeriod - ${value.toStringAsFixed(0)} - $groupName\n'; - // } - // - // return BarTooltipItem( - // tooltipText, - // TextStyle(color: Colors.white, fontSize: 12), - // ); + // Get the list of group names dynamically + List groupNames = []; + for (var entry in chartData['response']) { + String groupValue = entry['ObsKey'][groupByKey]; + if (!groupNames.contains(groupValue)) { + groupNames.add(groupValue); + } + } + + // Accessing the stacked rod items and calculating the tooltip text + List rodStackItems = + rodData.rodStackItems; + List tooltipTextSpans = []; + + for (int i = 0; i < rodStackItems.length; i++) { + double fromY = rodStackItems[i].fromY; + double toY = rodStackItems[i].toY; + double value = toY - fromY; + String groupName = groupNames[i]; + + // Get the color for the current group + Color groupColor = _getColorForGroup( + i); // Replace with your color logic + + // Add a TextSpan for the colored circle and the group name with value + tooltipTextSpans.addAll([ + TextSpan( + text: ' ● ', + style: TextStyle( + color: groupColor, + fontSize: 14), // Circle color + ), + TextSpan( + text: + '$groupName - ${value.toStringAsFixed(0)}\n', + style: + TextStyle(color: Colors.white, fontSize: 12), + ), + ]); + } + + return BarTooltipItem( + '', + TextStyle(color: Colors.white, fontSize: 12), + children: tooltipTextSpans, + ); }, ), - // touchCallback: (BarTouchResponse touchResponse) { - // // Handle touch events, such as hover - // if (touchResponse.touchInput is PointerDownEvent) { - // // If you want to handle touch down events, you can add your logic here. - // print('Bar touched: ${touchResponse.touchInput}'); - // } - // }, handleBuiltInTouches: true, // Enable built-in touch events ), titlesData: FlTitlesData( @@ -359,7 +377,7 @@ class ChartWidget extends StatelessWidget { rightTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)), ), - gridData: FlGridData(show: true), + gridData: FlGridData(show: false), borderData: FlBorderData(show: false), barGroups: _generateBarGroups(chartData, groupByKey), ), @@ -389,11 +407,23 @@ class ChartWidget extends StatelessWidget { ), ), SizedBox(width: 5), - Text( - group, - overflow: TextOverflow.ellipsis, - style: TextStyle(fontSize: 11), - ), + Tooltip( + message: group, + waitDuration: Duration(milliseconds: 500), + showDuration: Duration(seconds: 2), + decoration: BoxDecoration( + color: Colors.black, + borderRadius: BorderRadius.circular(4), + ), + textStyle: TextStyle(color: Colors.white), + child: Text( + group.length > 10 + ? '${group.substring(0, 10)}...' + : group, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 11), + ), + ) ], ); }).toList(); diff --git a/lib/presentation/Screens/profilepage.dart b/lib/presentation/Screens/profilepage.dart index d9c8ec7b..e77c3187 100644 --- a/lib/presentation/Screens/profilepage.dart +++ b/lib/presentation/Screens/profilepage.dart @@ -149,7 +149,7 @@ class _ProfileScreenState extends State { ); print('userDetails: $userDetailsResponse'); setState(() { - _usernameController.text = userDetailsResponse.data['username'] ?? ''; + _usernameController.text = userDetailsResponse.data['uname'] ?? ''; _emailController.text = userDetailsResponse.data['email'] ?? ''; role = userDetailsResponse.data['role'] ?? ''; }); diff --git a/lib/presentation/routes/auth_routes/login_route.dart b/lib/presentation/routes/auth_routes/login_route.dart index bf929e14..29b2a978 100644 --- a/lib/presentation/routes/auth_routes/login_route.dart +++ b/lib/presentation/routes/auth_routes/login_route.dart @@ -21,6 +21,9 @@ import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home_r import '../../Screens/auth_verification/registration.dart'; import 'package:pocketbase/pocketbase.dart'; +import '../../components/indicators/locale_provider.dart'; +import '../../components/my_toggle.dart'; + class LoginRoute extends HookConsumerWidget { final pb = PocketBase('https://pb.venbait.in'); // final _pb = PocketBase('http://127.0.0.1:8090'); @@ -82,6 +85,7 @@ class LoginRoute extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final locale = ref.watch(localeProvider); final emailCtl = useTextEditingController(); final pwCtl = useTextEditingController(); final forgotPwBtn = TextButton( @@ -514,9 +518,16 @@ class LoginRoute extends HookConsumerWidget { ), children: [ 36.verticalSpace, - const Align( + Align( alignment: AlignmentDirectional.topEnd, - child: LangToggle(), + child: MyToggle(isOn: locale?.languageCode == 'en', + knobTextWhenOn: 'ع', + knobTextWhenOff: 'EN', + pathColorWhenOn: Colors.grey.shade300, + pathColorWhenOff: Colors.grey.shade300, + onTap: (){ + ref.read(localeProvider.notifier).toggleLocale(); + },), ), 16.verticalSpace, helloAndPleaseLoginTexts, diff --git a/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart b/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart index 82c4a1d4..260f7692 100644 --- a/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart +++ b/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart @@ -175,18 +175,28 @@ class MyHomePage extends StatefulWidget { State createState() => _MyHomePageState(); } -void handleInfoCardClick(BuildContext context, String data) { +void handleInfoCardClick(BuildContext context, String data, Color color) { // Handle navigation and pass dynamic data print(data); final dataSets = data; - print(dataSets); - if (dataSets == 'hotels') { - context.go('/chartScreen/$dataSets'); - } else if (dataSets == 'divorces') { - context.go('/chartScreen/$dataSets'); - } else if (dataSets == 'marriages') { + if (dataSets != null) { + // Perform navigation context.go('/chartScreen/$dataSets'); + } else { + // Show error if dataSet is null + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('No dataset available')), + ); } + // final dataSets = data; + // print(dataSets); + // if (dataSets == 'hotels') { + // context.go('/chartScreen/$dataSets'); + // } else if (dataSets == 'divorces') { + // context.go('/chartScreen/$dataSets'); + // } else if (dataSets == 'marriages') { + // context.go('/Chart/$dataSets'); + // } } class _MyHomePageState extends State { @@ -195,243 +205,294 @@ class _MyHomePageState extends State { double myheight = MediaQuery.of(context).size.height; double mywidth = MediaQuery.of(context).size.width; return BaseScaffold( - mycenterTitle: true, - title: SizedBox( - height: myheight / 5, - width: mywidth / 3, - child: Image(image: AssetImage('assets/logos/uae_stat.png'))), - body: EconomyStats(), + title: Center( + child: SizedBox( + height: myheight / 5, + width: mywidth / 3, + child: Image(image: AssetImage('assets/logos/uae_stat.png')))), + body: EconomyStatsWidget(), ); } } -class EconomyStats extends StatelessWidget { - const EconomyStats({Key? key}) : super(key: key); +class EconomyStatsWidget extends StatefulWidget { + const EconomyStatsWidget({super.key}); @override + EconomyStatsState createState() => EconomyStatsState(); +} + +class EconomyStatsState extends State { + List data = []; + + bool isLoading = true; + + @override + void initState() { + super.initState(); + fetchData(); + } + + Future fetchData() async { + const baseUrl = 'https://pb.venbait.in/api/getHomePageData'; + try { + final response = await http.get(Uri.parse(baseUrl)); + if (response.statusCode == 200) { + setState(() { + data = json.decode(response.body); + // print("HomeDAta $data"); + + // Sort data after fetching to ensure the correct order + data.forEach((mainTopic) { + if (mainTopic['tile_data'] != null) { + // print('Original tile_data: ${mainTopic['tile_data']}'); + + mainTopic['tile_data']?.sort((a, b) { + try { + final aOrder = int.tryParse( + a['data_set_list_order']?.toString() ?? '0') ?? + 0; + final bOrder = int.tryParse( + b['data_set_list_order']?.toString() ?? '0') ?? + 0; + + return aOrder.compareTo(bOrder); + } catch (e) { + // Handle any errors during sorting + print('Error during sorting: $e'); + return 0; // In case of an error, don't alter the order + } + }); + + // Print the sorted tile_data + // print('Sorted tile_data: ${mainTopic['tile_data']}'); + } + }); + + isLoading = false; + }); + } else { + throw Exception('Failed to load data'); + } + } catch (e) { + setState(() { + isLoading = false; + }); + print('Error fetching data: $e'); + } + } + Widget build(BuildContext context) { - return SingleChildScrollView( + double myheight = MediaQuery.of(context).size.height; + double mywidth = MediaQuery.of(context).size.width; + + if (isLoading) { + return Center(child: CircularProgressIndicator()); + } else if (data.isEmpty) { + return Center(child: Text("No data available")); + } + + return Container( + height: myheight, + // color: Colors.cyan, child: Padding( padding: EdgeInsets.only(left: 10, right: 10), child: Column( - children: [ - // Header Section - RoundedCornerContainer( - text: 'ECONOMY', - backgroundColor: Color(0xFF7DAFBC), - textStyle: const TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - // Grid Section + children: data.map((mainTopic) { + String colorPattern = mainTopic['color_pattern']; + // Color backgroundColor = Color(int.parse(colorPattern)); - // ECONOMY DETAILS - Padding( - padding: const EdgeInsets.only( - top: 10, - ), - child: Column( - children: [ - // First Row - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - InfoCard( - title: 'GDP (Constant)', - subtitle: '(2022) (AED)', - value: '1.62T', - bordercolor: Color(0xFF7DAFBC), - textcolor: Color(0xFF7DAFBC), - onTap: () => handleInfoCardClick(context, 'Inflation'), - ), - InfoCard( - title: 'Inflation Rate', - subtitle: '(2022)', - value: '4.82%', - bordercolor: Color(0xFF7DAFBC), - textcolor: Color(0xFF7DAFBC), - onTap: () => handleInfoCardClick(context, 'Inflation'), - ), - ], - ), - const SizedBox(height: 10), - // Second Row - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - InfoCard( - title: 'Trade Value', - subtitle: '(Jan - Jan 2024) - AED', - value: '215.4B', - bordercolor: Color(0xFF7DAFBC), - textcolor: Color(0xFF7DAFBC), - onTap: () => - handleInfoCardClick(context, 'commodities'), - ), - InfoCard( - title: 'Hotel Guests', - subtitle: '(2022)', - value: '25.21M', - bordercolor: Color(0xFF7DAFBC), - textcolor: Color(0xFF7DAFBC), - onTap: () => handleInfoCardClick(context, 'hotels'), - ), - ], - ), - const SizedBox(height: 10), - RoundedCornerContainer( - text: 'SOCIAL', - backgroundColor: Colors.brown[200]!, - textStyle: const TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), + Color backgroundColor; + try { + backgroundColor = Color(int.parse(colorPattern)); + } catch (e) { + backgroundColor = Colors.grey; // Fallback color + print('Invalid color format: $colorPattern'); + } - // SOCIAL DETAILS - Padding( - padding: const EdgeInsets.only( - top: 10, + Color borderColor = + backgroundColor; // Use the same color for the border + + // final totalTiles = mainTopic['sub_topics'] + // .expand((subTopic) => (subTopic['tile_data'] as List? ?? [])) + // .length; + + final totalTiles = + (mainTopic['tile_data'] as List?)?.length ?? 0; + + // Compute crossAxisCount dynamically + // final crossAxisCount = (totalTiles / 2).ceil(); // Divide by 2 and round up + + // final crossAxisCount = (totalTiles / 2).ceil().clamp(1, 2); // Safeguard + final crossAxisCount = + totalTiles > 0 ? (totalTiles / 2).ceil().clamp(1, 2) : 1; + + return Expanded( + child: Container( + // color: Colors.cyan, + height: + myheight, // Set the background color for the entire Column + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + RoundedCornerContainer( + text: mainTopic['main_topic'], + backgroundColor: backgroundColor, + textStyle: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.w500, + color: Colors.white, + ), ), - child: Column( - children: [ - // First Row - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - InfoCard( - title: 'Divorces', - subtitle: '(2022)', - value: '0', - bordercolor: Colors.brown[200]!, - textcolor: Colors.brown[200]!, - onTap: () => - handleInfoCardClick(context, 'divorces'), + SizedBox( + height: myheight / 4.8, // Set dynamic height + child: Container( + // color: Colors.grey[200], // Set a background color for the scrollable container + child: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: MediaQuery.of(context).size.width * 2, + ), + child: MasonryGridView.builder( + gridDelegate: + SliverSimpleGridDelegateWithFixedCrossAxisCount( + crossAxisCount: crossAxisCount, ), - InfoCard( - title: 'Marriages', - subtitle: '(2022)', - value: '0', - bordercolor: Colors.brown[200]!, - textcolor: Colors.brown[200]!, - onTap: () => - handleInfoCardClick(context, 'marriages'), - ), - ], - ), - const SizedBox(height: 10), - // Second Row - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - InfoCard( - title: 'Hospitals (Gov)', - subtitle: '(Jan - Jan 2024) - AED', - value: '215.4B', - bordercolor: Colors.brown[200]!, - textcolor: Colors.brown[200]!, - onTap: () => - handleInfoCardClick(context, 'Hospitals'), - ), - InfoCard( - title: 'Schools', - subtitle: '(2022)', - value: '25.21M', - bordercolor: Colors.brown[200]!, - textcolor: Colors.brown[200]!, - onTap: () => - handleInfoCardClick(context, 'Schools'), - ), - ], - ), - const SizedBox(height: 10), - RoundedCornerContainer( - text: 'ENVIRONMENT', - backgroundColor: Color(0xFF7DAFBC), - textStyle: const TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: Colors.white, + physics: const NeverScrollableScrollPhysics(), + itemCount: totalTiles, + itemBuilder: (context, index) { + final tileDataList = + (mainTopic['tile_data'] as List?) ?? + []; + + final tileData = Map.from( + tileDataList[index], + ); + + return Container( + margin: const EdgeInsets.all(0), + height: + myheight / 9.2, // Variable height for tiles + width: mywidth / 3, + padding: + const EdgeInsets.only(bottom: 3, top: 3), + // color: Colors.yellowAccent.shade100, // Tile background color + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: _buildRows( + [tileData], + borderColor, + mainTopic['color_pattern']), + ), + ), + ], + ), + ); + }, ), ), - - // ENVIRONMENT DETAILS - Padding( - padding: const EdgeInsets.only( - top: 10, - ), - child: Column( - children: [ - // First Row - Row( - mainAxisAlignment: - MainAxisAlignment.spaceEvenly, - children: [ - InfoCard( - title: 'Electricity', - subtitle: '(2022) (AED)', - value: '1.62T', - bordercolor: Color(0xFF7DAFBC), - textcolor: Color(0xFF7DAFBC), - onTap: () => handleInfoCardClick( - context, 'Electricity'), - ), - InfoCard( - title: 'Cruid oil', - subtitle: '(2022)', - value: '4.82%', - bordercolor: Color(0xFF7DAFBC), - textcolor: Color(0xFF7DAFBC), - onTap: () => - handleInfoCardClick(context, 'Cruid'), - ), - ], - ), - const SizedBox(height: 10), - // Second Row - Row( - mainAxisAlignment: - MainAxisAlignment.spaceEvenly, - children: [ - InfoCard( - title: 'Export of oil', - subtitle: '(Jan - Jan 2024) - AED', - value: '215.4B', - bordercolor: Color(0xFF7DAFBC), - textcolor: Color(0xFF7DAFBC), - onTap: () => - handleInfoCardClick(context, 'Export'), - ), - InfoCard( - title: 'Desalinated water', - subtitle: '(2022)', - value: '25.21M', - bordercolor: Color(0xFF7DAFBC), - textcolor: Color(0xFF7DAFBC), - onTap: () => handleInfoCardClick( - context, 'Desalinated'), - ), - ], - ), - ], - ), - ), - ], + ), ), - ), - ], + ], + ), ), - ), - ], + ); + }).toList(), ), ), ); } } +// +// List _buildRows(List> tileData, Color borderColor) { +// +// +// List rows = []; +// +// +// for (int i = 0; i < tileData.length; i += 2) { +// // Adding a row for every two items +// rows.add( +// Container( +// // color: Colors.grey[200], // Set background color for the row +// // padding: EdgeInsets.symmetric(vertical: 7.0), // Optional padding +// padding: EdgeInsets.only(top:5, bottom:2), +// child: Expanded( +// child: Row( +// mainAxisAlignment: MainAxisAlignment.start, +// children: tileData +// .sublist(i, i + 2 > tileData.length ? tileData.length : i + 2) +// .map((tile) { +// return Expanded( +// child: InfoCard( +// title: tile['data_set_tile_heading']!, +// subtitle: tile['value_source']!, +// value: tile['value']!, +// dataset:tile['data_set']!, +// bordercolor: borderColor, +// textcolor: borderColor, +// backgroundColor: Colors.white, // Set background color for InfoCard +// onTap: () => {}, +// +// ), +// ); +// }).toList(), +// ), +// ), +// ), +// ); +// // rows.add(SizedBox(height: 1)); // Add space between rows +// } +// +// return rows; +// } + +List _buildRows( + List> tileData, Color borderColor, color_pattern) { + // tileData.sort((a, b) => + // (a['data_set_list_order'] ?? 0).compareTo(b['data_set_list_order'] ?? 0),); + + List rows = []; + // print('tileDataLength: $tileData.length'); + + for (int i = 0; i < tileData.length; i += 2) { + // Adding a row for every two items + rows.add( + Container( + padding: EdgeInsets.only(top: 5, bottom: 2), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: tileData + .sublist(i, i + 2 > tileData.length ? tileData.length : i + 2) + .map((tile) { + return Expanded( + child: InfoCard( + // title: tile['data_set_tile_heading']!, + title: tile['data_set_tile_heading'] ?? 'Unknown Title', + subtitle: tile['value_source']!, + value: tile['value']!, + dataset: tile['data_set']!, + bordercolor: borderColor, + textcolor: borderColor, + backgroundColor: + Colors.white, // Set background color for InfoCard + onTap: () {}, + ), + ); + }).toList(), + ), + ), + ); + } + + return rows; +} + class RoundedCornerContainer extends StatelessWidget { final String text; final Color backgroundColor; @@ -449,7 +510,7 @@ class RoundedCornerContainer extends StatelessWidget { return Container( decoration: BoxDecoration( color: backgroundColor, - borderRadius: BorderRadius.circular(12), // Rounded corners + borderRadius: BorderRadius.circular(16), // Rounded corners ), child: Center( child: Text( @@ -470,18 +531,22 @@ class InfoCard extends StatelessWidget { final String title; final String subtitle; final String value; + final String dataset; final Color bordercolor; final Color? textcolor; - final VoidCallback onTap; // New callback parameter + final VoidCallback onTap; + final Color backgroundColor; const InfoCard({ Key? key, required this.title, required this.subtitle, required this.value, + required this.dataset, required this.bordercolor, this.textcolor, - required this.onTap, // Initialize in constructor + required this.onTap, + required this.backgroundColor, }) : super(key: key); @override @@ -489,50 +554,86 @@ class InfoCard extends StatelessWidget { double myheight = MediaQuery.of(context).size.height; double mywidth = MediaQuery.of(context).size.width; - return Expanded( - child: GestureDetector( - onTap: onTap, // Use callback for handling clicks - child: Container( - height: myheight / 9, - width: mywidth / 4, - margin: const EdgeInsets.symmetric(horizontal: 8), - decoration: BoxDecoration( - border: Border.all(color: bordercolor), - borderRadius: BorderRadius.circular(8), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Expanded( - child: Text( - title, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.black, + return GestureDetector( + onTap: () { + // Call handleInfoCardClick and pass the title and color + handleInfoCardClick( + context, dataset, bordercolor); // Pass 'title' to the function + }, + child: Container( + height: myheight / 12, + + width: mywidth / 4, + + margin: const EdgeInsets.symmetric(horizontal: 8), + // padding: const EdgeInsets.all(10), + padding: const EdgeInsets.only(bottom: 1, top: 1, left: 10, right: 10), + decoration: BoxDecoration( + border: Border.all(color: bordercolor), + borderRadius: BorderRadius.circular(8), + ), + child: Column( + 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, + fontWeight: FontWeight.w400, + // color: Colors.black, + color: Color(0xFF000000), + ), ), ), ), - const SizedBox(height: 2), - Text( - subtitle, - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 12, - color: Colors.grey, + ), + const SizedBox(height: 0.5), + + Flexible( + fit: FlexFit.loose, + child: FittedBox( + fit: BoxFit.contain, + child: Column( + children: [ + Text( + subtitle, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 11, + fontWeight: FontWeight.w400, + color: Color(0xFF8E8E8E), + ), + ), + ], ), ), - const SizedBox(height: 2), - Text( - value, - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: textcolor ?? Colors.black, - ), + ), + const SizedBox(height: 1), + + // Flexible( + // fit: FlexFit.loose, + // child: FittedBox( + // fit: BoxFit.scaleDown, + // child: + Text( + value, + style: TextStyle( + fontSize: 26, + fontWeight: FontWeight.w900, + color: textcolor ?? Colors.black, ), - ], - ), + ), + // ), + // ), + ], ), ), ); diff --git a/lib/presentation/routes/drawer_routes/Drawer Items/edit_profile.dart b/lib/presentation/routes/drawer_routes/Drawer Items/edit_profile.dart index e21aca1b..3828a89e 100644 --- a/lib/presentation/routes/drawer_routes/Drawer Items/edit_profile.dart +++ b/lib/presentation/routes/drawer_routes/Drawer Items/edit_profile.dart @@ -159,7 +159,7 @@ class _EditProfileState extends State { ); print('userDetails: $userDetailsResponse'); setState(() { - _usernameController.text = userDetailsResponse.data['username'] ?? ''; + _usernameController.text = userDetailsResponse.data['uname'] ?? ''; _emailController.text = userDetailsResponse.data['email'] ?? ''; _fullNameController.text = userDetailsResponse.data['full_name'] ?? ''; _selectedCountry = userDetailsResponse.data['country_region'] ?? ''; @@ -710,12 +710,15 @@ class _EditProfileState extends State { SizedBox(height: 10), Text.rich( TextSpan( - text: AppLocalizations.of(context)!.agree, + text: AppLocalizations.of(context)! + .agree, style: TextStyle(color: Colors.black), children: [ TextSpan( - text: AppLocalizations.of(context)!.terms_conditions, + text: AppLocalizations.of( + context)! + .terms_conditions, style: TextStyle( color: Colors.blue, decoration: @@ -728,12 +731,16 @@ class _EditProfileState extends State { }, ), TextSpan( - text: AppLocalizations.of(context)!.t_and, + text: AppLocalizations.of( + context)! + .t_and, style: TextStyle( color: Colors.black), ), TextSpan( - text: AppLocalizations.of(context)!.privacy_policy, + text: AppLocalizations.of( + context)! + .privacy_policy, style: TextStyle( color: Colors.blue, decoration: @@ -746,7 +753,9 @@ class _EditProfileState extends State { }, ), TextSpan( - text: AppLocalizations.of(context)!.conditions, + text: AppLocalizations.of( + context)! + .conditions, style: TextStyle( color: Colors.black), ), diff --git a/lib/presentation/routes/drawer_routes/Drawer Items/feedback.dart b/lib/presentation/routes/drawer_routes/Drawer Items/feedback.dart index 4d8a7f10..363a66d4 100644 --- a/lib/presentation/routes/drawer_routes/Drawer Items/feedback.dart +++ b/lib/presentation/routes/drawer_routes/Drawer Items/feedback.dart @@ -467,7 +467,8 @@ class _FeedbackFormState extends State style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)), const SizedBox(height: 10), Text(AppLocalizations.of(context)!.feedback_failed_msg, - style: const TextStyle(fontSize: 16), textAlign: TextAlign.center), + style: const TextStyle(fontSize: 16), + textAlign: TextAlign.center), const SizedBox(height: 30), ElevatedButton( onPressed: () { diff --git a/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart b/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart index 5bdb2c9f..bafc175f 100644 --- a/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart +++ b/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart @@ -1,9 +1,16 @@ +import 'dart:convert'; + 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:flutter_gen/gen_l10n/app_localizations.dart'; -class BaseScaffold extends StatefulWidget { +import '../../components/indicators/locale_provider.dart'; +import '../../components/my_toggle.dart'; + +class BaseScaffold extends ConsumerStatefulWidget { final Widget body; final Widget title; final bool? showBackButton; @@ -26,7 +33,7 @@ class BaseScaffold extends StatefulWidget { _BaseScaffoldState createState() => _BaseScaffoldState(); } -class _BaseScaffoldState extends State { +class _BaseScaffoldState extends ConsumerState { final _pb = PocketBase('https://pb.venbait.in'); String _avatarUrl = ''; dynamic userId; @@ -34,6 +41,13 @@ class _BaseScaffoldState extends State { String? userEmail; String? userAvatar; String? role; + String? userNames; + String? userEmails; + String? userAvatars; + String? roles; + String? collectionIds; + + String? collectionId; @override void initState() { @@ -55,39 +69,125 @@ class _BaseScaffoldState extends State { userId = fetchedUserId; }); //print('NAVUser ID: $userId'); - _fetchUserData(); + _initializeUserData(); } else { print('No userId found'); // Handle the case where userId is not available } } + // Future _fetchUserData() async { + // try { + // final adminAuth = await _pb.admins + // .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com'); + // final adminToken = adminAuth.token; + // //print('adminToken- ${adminToken}'); + // final userDetailsResponse = await _pb.collection('users').getOne( + // userId!, + // headers: { + // 'Authorization': 'Bearer $adminToken', + // }, + // ); + // print('NAVuserDetails: $userDetailsResponse'); + // + // setState(() { + // userName = userDetailsResponse.data['uname'] ?? ''; + // userEmail = userDetailsResponse.data['email'] ?? ''; + // userAvatar = userDetailsResponse.data['avatar'] ?? ''; + // role = userDetailsResponse.data['role'] ?? ''; + // String recordId = userId; + // String collectionId = + // userDetailsResponse.data['collectionId'] ?? '_pb_users_auth_'; + // + // if (userAvatar!.isNotEmpty && recordId.isNotEmpty) { + // _avatarUrl = + // 'https://pb.venbait.in/api/files/$collectionId/$recordId/$userAvatar'; + // } else { + // _avatarUrl = ''; // Reset to default or empty + // } + // }); + // } catch (e) { + // print('Error fetching user details: $e'); + // } + // } + + Future _initializeUserData() async { + print("Laoding 1"); + final prefs = await SharedPreferences.getInstance(); + // final userDataString = prefs.getString('userData'); + + if (prefs.containsKey('userData')) { + // print("Loading user data from local storage: $userData"); + final String? userDataString = prefs.getString('userData'); + + if (userDataString != null) { + // Decode the JSON string into a Map + final Map userData = jsonDecode(userDataString); + + // Access the userName field + setState(() { + userName = userData['userNames']; + userEmail = userData['userEmails']; + userAvatar = userData['userAvatars']; + role = userData['roles']; + String recordId = userId; + String collectionId = userData['collectionIds']; + + if (userAvatar!.isNotEmpty && recordId.isNotEmpty) { + _avatarUrl = + 'https://pb.venbait.in/api/files/$collectionId/$recordId/$userAvatar'; + } else { + _avatarUrl = ''; // Reset to default or empty + } + }); + } else { + print("No user data found in local storage."); + } + } else { + // Fetch data from server and save it to local storage + + await _fetchUserData(); + } + } + Future _fetchUserData() async { try { + // print("Fetching _fetchUserData"); final adminAuth = await _pb.admins .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com'); final adminToken = adminAuth.token; - //print('adminToken- ${adminToken}'); + final userDetailsResponse = await _pb.collection('users').getOne( userId!, headers: { 'Authorization': 'Bearer $adminToken', }, ); - print('NAVuserDetails: $userDetailsResponse'); + // print('User details fetched from server: $userDetailsResponse'); + + final userData = { + 'userNames': userDetailsResponse.data['uname'] ?? '', + 'userEmails': userDetailsResponse.data['email'] ?? '', + 'userAvatars': userDetailsResponse.data['avatar'] ?? '', + 'roles': userDetailsResponse.data['role'] ?? '', + 'collectionIds': + userDetailsResponse.data['collectionId'] ?? '_pb_users_auth_', + }; + + final prefs = await SharedPreferences.getInstance(); + await prefs.setString('userData', jsonEncode(userData)); setState(() { - userName = userDetailsResponse.data['uname'] ?? ''; - userEmail = userDetailsResponse.data['email'] ?? ''; - userAvatar = userDetailsResponse.data['avatar'] ?? ''; - role = userDetailsResponse.data['role'] ?? ''; + userNames = userData['userNames']; + userEmails = userData['userEmails']; + userAvatars = userData['userAvatars']; + roles = userData['roles']; String recordId = userId; - String collectionId = - userDetailsResponse.data['collectionId'] ?? '_pb_users_auth_'; + collectionIds = userData['collectionIds']; - if (userAvatar!.isNotEmpty && recordId.isNotEmpty) { + if (userAvatars!.isNotEmpty && recordId.isNotEmpty) { _avatarUrl = - 'https://pb.venbait.in/api/files/$collectionId/$recordId/$userAvatar'; + 'https://pb.venbait.in/api/files/$collectionIds/$recordId/$userAvatars'; } else { _avatarUrl = ''; // Reset to default or empty } @@ -97,8 +197,16 @@ class _BaseScaffoldState extends State { } } + Future logout() async { + final prefs = await SharedPreferences.getInstance(); + prefs.clear(); + context.go('/'); + } + @override Widget build(BuildContext context) { + final locale = ref.watch(localeProvider); + final localeNotifier = ref.read(localeProvider.notifier); String currentRoute = GoRouterState.of(context).matchedLocation; double myheight = MediaQuery.of(context).size.height; double mywidth = MediaQuery.of(context).size.width; @@ -109,10 +217,18 @@ class _BaseScaffoldState extends State { backgroundColor: widget.appbarColor, actions: widget.actions ?? [ - IconButton( - onPressed: () {}, - icon: const Icon(Icons.toggle_off_outlined), - ), + // IconButton( + // onPressed: () {}, + // icon: const Icon(Icons.toggle_off_outlined), + // ), + MyToggle(isOn: locale?.languageCode == 'en', + knobTextWhenOn: 'ع', + knobTextWhenOff: 'EN', + pathColorWhenOn: Colors.grey.shade300, + pathColorWhenOff: Colors.grey.shade300, + onTap: (){ + ref.read(localeProvider.notifier).toggleLocale(); + },), ], leading: Stack( children: [ @@ -216,7 +332,8 @@ class _BaseScaffoldState extends State { if (role == 'admin') ListTile( leading: Icon(Icons.manage_accounts), - title: const Text('Manage User'), + // title: Text('Manage User'), + title: Text(AppLocalizations.of(context)!.manage_user,), onTap: () => context.go('/manageuser'), ), ListTile( @@ -227,7 +344,7 @@ class _BaseScaffoldState extends State { ListTile( leading: Icon(Icons.logout), title: const Text('Logout'), - onTap: () => context.go('/'), + onTap: () => logout(), ), ], ), @@ -239,19 +356,23 @@ class _BaseScaffoldState extends State { items: [ BottomNavigationBarItem( icon: Icon(Icons.home), - label: 'Home', + // label: 'Home', + label: AppLocalizations.of(context)!.nav_home, ), BottomNavigationBarItem( icon: Icon(Icons.map), - label: 'UAE Numbers', + // label: 'UAE Numbers' + label: AppLocalizations.of(context)!.nav_uae, ), BottomNavigationBarItem( icon: Icon(Icons.bar_chart), - label: 'Competitiveness', + // label: 'Competitiveness', + label: AppLocalizations.of(context)!.nav_competitiveness, ), BottomNavigationBarItem( icon: Icon(Icons.public), - label: 'Country Profile', + // label: 'Country Profile', + label: AppLocalizations.of(context)!.country_profile, ), ], selectedItemColor: Colors.black,