3395 lines
139 KiB
Dart
3395 lines
139 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:flutter_svg/svg.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:path_provider/path_provider.dart';
|
|
import 'package:pocketbase/pocketbase.dart';
|
|
import 'package:screenshot/screenshot.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:tutorial_coach_mark/tutorial_coach_mark.dart';
|
|
import 'package:uae_stat/config/api_config.dart';
|
|
import 'package:uae_stat/config/connectivity_provider.dart';
|
|
import 'package:uae_stat/config/toast_util.dart';
|
|
import 'package:uae_stat/config/toggle_lang_service.dart';
|
|
import 'package:uae_stat/domain/use_cases/language.dart';
|
|
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/uae_numbers_asset_path.dart';
|
|
import 'package:uae_stat/presentation/Screens/charts/services/api_service.dart';
|
|
import 'package:uae_stat/presentation/Screens/charts/widgets/chart_widget.dart';
|
|
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
|
|
import 'package:uae_stat/presentation/components/my_toggle.dart';
|
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
|
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
|
|
import 'package:uae_stat/presentation/Screens/app_tour/Target_content.dart';
|
|
import '../filters/search_filter_helper.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_svg/flutter_svg.dart';
|
|
import 'package:share_plus/share_plus.dart';
|
|
import 'package:path_provider/path_provider.dart';
|
|
import 'dart:io';
|
|
|
|
class ChartScreen1 extends ConsumerStatefulWidget {
|
|
final String dataSets;
|
|
final String bgColor;
|
|
final String mainTopic;
|
|
final String title;
|
|
final String? keyParam; // Nullable String
|
|
final String kpi;
|
|
final List<Map<String, dynamic>> filter_data;
|
|
|
|
const ChartScreen1({
|
|
Key? key,
|
|
required this.dataSets,
|
|
required String this.bgColor,
|
|
required this.mainTopic,
|
|
required this.title,
|
|
required this.keyParam,
|
|
required this.kpi,
|
|
required this.filter_data,
|
|
});
|
|
@override
|
|
ConsumerState<ChartScreen1> createState() => _ChartScreen1State();
|
|
}
|
|
|
|
class _ChartScreen1State extends ConsumerState<ChartScreen1> {
|
|
// final _pb = PocketBase('https://pb.venbait.in');
|
|
final _pb = PocketBase(apiUrl);
|
|
|
|
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
|
final ApiService apiService = ApiService();
|
|
bool isLoading = true;
|
|
bool _isSharing = false;
|
|
List<dynamic> isChartData = [];
|
|
List<dynamic> nonChartData = [];
|
|
Map<String, dynamic> chartScreenData = {};
|
|
List<dynamic> filterDataSet = [];
|
|
List<dynamic> filterData = [];
|
|
List<dynamic> chartsData = [];
|
|
List<dynamic> tabFilteredChartData = [];
|
|
List<dynamic> tabFilteredCardData = [];
|
|
List<dynamic> cardData = [];
|
|
List<Map<String, dynamic>> filteredAndSortedData = [];
|
|
List<Map<String, dynamic>> selectedFiltersStorage = [];
|
|
List<Map<String, dynamic>> selectedFiltersApi = [];
|
|
List<dynamic> originalChartsData = [];
|
|
List<dynamic> originalCardData = [];
|
|
List<dynamic> originalTabCardData = [];
|
|
List<dynamic> originalTabChartsData = [];
|
|
late Color backgroundColor;
|
|
final ScrollController _scrollController = ScrollController();
|
|
int _activeTabIndex = 0;
|
|
dynamic tabWiseKpi = [];
|
|
dynamic tabFilteredKpi = [];
|
|
List<Map<String, dynamic>> formattedFilters = [];
|
|
|
|
dynamic _tabsData = [];
|
|
dynamic currentTab = [];
|
|
// List<Map<String, dynamic>> _tabsData = [];
|
|
late List<TargetFocus> marriageTargets;
|
|
late List<TargetFocus> previousMarriageTargets;
|
|
late TutorialCoachMark tutorialCoachMark;
|
|
final GlobalKey chartKey = GlobalKey();
|
|
final GlobalKey bookMarkKey = GlobalKey();
|
|
final GlobalKey cardKey = GlobalKey();
|
|
bool isBookmarked = false; // Track bookmark state
|
|
String? bookmarkId; // Stores the ID of the bookmark record in PocketBase
|
|
late final Locale locale;
|
|
late String mainTopic;
|
|
late UserService _userService;
|
|
late double tourCard;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_userService = UserService();
|
|
final String bgColor = widget.bgColor;
|
|
print(' bgColor $bgColor');
|
|
checkIfBookmarked();
|
|
locale = ref.read(localeProvider) ?? const Locale('en');
|
|
print('locale-$locale');
|
|
|
|
fetchChartData(widget.dataSets, locale?.languageCode ?? 'en', widget.kpi,
|
|
widget.filter_data)
|
|
.then((_) {
|
|
if (_tabsData.isNotEmpty) {
|
|
// Call onTabSelected for the first tab
|
|
onTabSelected(_tabsData[0]['id']);
|
|
}
|
|
});
|
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
_startTutorialAfterRender();
|
|
});
|
|
}
|
|
|
|
/// Check if the item is already bookmarked
|
|
Future<void> checkIfBookmarked() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
final userID = prefs.getString('userId');
|
|
try {
|
|
final adminAuth = await _pb.admins.authWithPassword(
|
|
'pb@venbainfotech.com',
|
|
'pb@venbainfotech.com',
|
|
);
|
|
|
|
final adminToken = adminAuth.token;
|
|
|
|
final result = await _pb.collection('bookmark').getList(
|
|
filter: "user_id = '${userID}' && dataset = '${widget.dataSets}'",
|
|
headers: {
|
|
'Authorization': adminToken, // Pass the admin token
|
|
},
|
|
);
|
|
|
|
if (result.items.isNotEmpty) {
|
|
setState(() {
|
|
isBookmarked = true;
|
|
bookmarkId = result.items.first.id;
|
|
});
|
|
}
|
|
} catch (e) {
|
|
print("Error checking bookmark: $e");
|
|
}
|
|
}
|
|
|
|
/// Add bookmark to PocketBase
|
|
Future<void> addBookmark() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
final userID = prefs.getString('userId');
|
|
try {
|
|
final adminAuth = await _pb.admins
|
|
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
|
|
final adminToken = adminAuth.token;
|
|
final response = await _pb.collection('bookmark').create(body: {
|
|
'user_id': userID,
|
|
'dataset': widget.dataSets,
|
|
'bgColor': widget.bgColor,
|
|
'mainTopic': widget.mainTopic,
|
|
'title': widget.title,
|
|
'keyParam': widget.keyParam,
|
|
}, headers: {
|
|
'Authorization': adminToken
|
|
});
|
|
|
|
setState(() {
|
|
isBookmarked = true;
|
|
bookmarkId = response.id;
|
|
});
|
|
|
|
// ToastUtil.showSuccessToast("Added to Bookmark.");
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(
|
|
AppLocalizations.of(context)!.added_to_Bookmark,
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
)
|
|
),
|
|
),
|
|
duration: Duration(seconds: 2),
|
|
),
|
|
);
|
|
|
|
print("Bookmark added: ${response.id}");
|
|
} catch (e) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(
|
|
AppLocalizations.of(context)!.failed_To_Remove,
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
)
|
|
),
|
|
),
|
|
// backgroundColor: Color(0xFFEB5F24),
|
|
duration: Duration(seconds: 2),
|
|
),
|
|
);
|
|
// ToastUtil.showDeleteToast("Unable to add to bookmarks. Please try again");
|
|
print("Error adding bookmark: $e");
|
|
}
|
|
}
|
|
|
|
/// Remove bookmark from PocketBase
|
|
Future<void> removeBookmark() async {
|
|
try {
|
|
final adminAuth = await _pb.admins
|
|
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
|
|
final adminToken = adminAuth.token;
|
|
if (bookmarkId != null) {
|
|
await _pb.collection('bookmark').delete(bookmarkId!, headers: {
|
|
'Authorization': adminToken,
|
|
});
|
|
|
|
setState(() {
|
|
isBookmarked = false;
|
|
bookmarkId = null;
|
|
});
|
|
// ToastUtil.showSuccessToast("Removed from Bookmark.");
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(
|
|
AppLocalizations.of(context)!.removed_from_Bookmark,
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
)
|
|
),
|
|
),
|
|
duration: Duration(seconds: 2),
|
|
),
|
|
);
|
|
print("Bookmark removed");
|
|
}
|
|
} catch (e) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(
|
|
'Failed to Remove',
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
)
|
|
),
|
|
),
|
|
// backgroundColor: Color(0xFFEB5F24),
|
|
duration: Duration(seconds: 2),
|
|
),
|
|
);
|
|
// ToastUtil.showDeleteToast(
|
|
// "Unable to remove from bookmarks. Please try again");
|
|
print("Error removing bookmark: $e");
|
|
}
|
|
}
|
|
|
|
/// Show confirmation dialog before removing bookmark
|
|
void showRemoveBookmarkDialog() {
|
|
double myheight = MediaQuery.of(context).size.height;
|
|
showDialog(
|
|
context: context,
|
|
barrierDismissible: false, // User must tap button to dismiss dialog
|
|
builder: (context) => AlertDialog(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(5.0), // Rounded corners
|
|
),
|
|
contentPadding: EdgeInsets.zero,
|
|
content: Stack(
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.all(10.0),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
SizedBox(
|
|
height: myheight / 30,
|
|
),
|
|
Text(
|
|
context.translate(
|
|
'Are you sure you want to remove this bookmark?',
|
|
'هل أنت متأكد أنك تريد إزالة هذه الإشارة المرجعية؟'),
|
|
textAlign: TextAlign.center,
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
fontSize: 18,
|
|
color: Color(0xFF898C81),
|
|
),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
actions: [
|
|
SizedBox(height: 20),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
children: [
|
|
SizedBox(
|
|
width: 100, // Set the desired width
|
|
child: TextButton(
|
|
onPressed: () => Navigator.pop(context),
|
|
style: TextButton.styleFrom(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(
|
|
10.0), // Adjust the radius as needed
|
|
),
|
|
side: BorderSide(
|
|
color: Color(0xFFAA8E83), // Set the outline color
|
|
width: 1, // Set the border width
|
|
),
|
|
),
|
|
child: Text(
|
|
context.translate('No', 'لا'),
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
color: Color(0xFFAA8E83),
|
|
fontSize: 16,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
SizedBox(
|
|
width: 100, // Set the desired width
|
|
child: TextButton(
|
|
onPressed: () {
|
|
Navigator.pop(context);
|
|
removeBookmark(); // Close dialog
|
|
},
|
|
style: TextButton.styleFrom(
|
|
backgroundColor: Color(0xFFAA8E83), // Set background color
|
|
foregroundColor: Colors.white,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius:
|
|
BorderRadius.circular(10.0), // Set text color
|
|
),
|
|
),
|
|
child: Text(
|
|
context.translate('Yes', 'نعم'),
|
|
style: TextStyle(color: Colors.white, fontSize: 16, fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
)
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
//when the popup need for bookmark
|
|
void showAddBookmarkDialog() {
|
|
double myheight = MediaQuery.of(context).size.height;
|
|
showDialog(
|
|
context: context,
|
|
barrierDismissible: false, // User must tap button to dismiss dialog
|
|
builder: (context) => AlertDialog(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(5.0), // Rounded corners
|
|
),
|
|
contentPadding: EdgeInsets.zero,
|
|
content: Stack(
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.all(10.0),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
SizedBox(
|
|
height: myheight / 30,
|
|
),
|
|
Text(
|
|
context.translate(
|
|
'Do you want to add this to bookmarks?',
|
|
'هل تريد إضافة هذا إلى الإشارات المرجعية؟',
|
|
),
|
|
textAlign: TextAlign.center,
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
fontSize: 18,
|
|
color: Color(0xFF898C81),
|
|
),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
actions: [
|
|
SizedBox(height: 20),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
children: [
|
|
SizedBox(
|
|
width: 100, // Set the desired width
|
|
child: TextButton(
|
|
onPressed: () => Navigator.pop(context),
|
|
style: TextButton.styleFrom(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(
|
|
10.0), // Adjust the radius as needed
|
|
),
|
|
side: BorderSide(
|
|
color: Color(0xFFAA8E83), // Set the outline color
|
|
width: 1, // Set the border width
|
|
),
|
|
),
|
|
child: Text(
|
|
context.translate('No', 'لا'),
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
color: Color(0xFFAA8E83),
|
|
fontSize: 16,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
SizedBox(
|
|
width: 100, // Set the desired width
|
|
child: TextButton(
|
|
onPressed: () {
|
|
Navigator.pop(context);
|
|
addBookmark(); // Close dialog
|
|
},
|
|
style: TextButton.styleFrom(
|
|
backgroundColor: Color(0xFFAA8E83), // Set background color
|
|
foregroundColor: Colors.white,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius:
|
|
BorderRadius.circular(10.0), // Set text color
|
|
),
|
|
),
|
|
child: Text(
|
|
context.translate('Yes', 'نعم'),
|
|
style: TextStyle(color: Colors.white, fontSize: 16, fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
// void showAddBookmarkDialog() {
|
|
// addBookmark();
|
|
// // ScaffoldMessenger.of(context).showSnackBar(
|
|
// // SnackBar(
|
|
// // content: Text(
|
|
// // 'The bookmark is added successfully',
|
|
// // ),
|
|
// // backgroundColor: Colors.green,
|
|
// // duration: Duration(seconds: 2),
|
|
// // ),
|
|
// // );
|
|
// }
|
|
|
|
///Calculate card Width
|
|
Future<double?> _getWidget() async {
|
|
await Future.delayed(Duration(milliseconds: 50)); // Ensures widget is built
|
|
final RenderBox? box =
|
|
cardKey.currentContext?.findRenderObject() as RenderBox?;
|
|
|
|
if (box != null) {
|
|
final Offset position = box.localToGlobal(Offset.zero);
|
|
final Size size = box.size;
|
|
|
|
double bottomY = position.dy + size.height; // Y position + height
|
|
// final double screenWidth = MediaQuery.of(context).size.width;
|
|
// final double screenHeight = MediaQuery.of(context).size.height;
|
|
// print("screenWidth: ${screenWidth}");
|
|
// print("screenHeight: ${screenHeight}");
|
|
// print("Widget Position: ${position.dx}, ${position.dy}");
|
|
// print("Widget Size: ${size.width} x ${size.height}");
|
|
// print("Bottom Y Position: $bottomY");
|
|
|
|
return bottomY;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
Future<void> handleSkip() async {
|
|
tutorialCoachMark.skip();
|
|
debugPrint('Skip clicked');
|
|
final prefs = await SharedPreferences.getInstance();
|
|
String? loginCount = prefs.getString('login_count');
|
|
if (loginCount == '1') {
|
|
await prefs.setString('login_count', '2');
|
|
}
|
|
ref.read(chartsTourProvider.notifier).state = true;
|
|
ref.read(previousChartsTourProvider.notifier).state = true;
|
|
ref.read(homeTourProvider.notifier).state = true;
|
|
ref.read(previousHomeTourProvider.notifier).state = true;
|
|
ref.read(scaffoldTourProvider.notifier).state = true;
|
|
ref.read(previousScaffoldTourProvider.notifier).state = true;
|
|
context.go('/myhomepage');
|
|
}
|
|
|
|
Future<void> _kpiTutorialCoachMark() async {
|
|
debugPrint('Not Initialized and Started');
|
|
tourCard = (await _getWidget())!;
|
|
// _calculateMarkPosition();
|
|
_initmarriageTargets();
|
|
debugPrint('Initialized and Started');
|
|
tutorialCoachMark = TutorialCoachMark(
|
|
paddingFocus: 0,
|
|
useSafeArea: true,
|
|
pulseEnable: false,
|
|
hideSkip: true,
|
|
targets: marriageTargets,
|
|
onFinish: () {
|
|
context.go('/myhomepage');
|
|
debugPrint('Marriage Tutorial Finished');
|
|
},
|
|
)..show(context: context);
|
|
}
|
|
|
|
void _startTutorialAfterRender() {
|
|
final chartTour = ref.watch(chartsTourProvider);
|
|
final previousChartTour = ref.watch(previousChartsTourProvider);
|
|
debugPrint('render problem');
|
|
if (cardKey.currentContext != null && !chartTour) {
|
|
_kpiTutorialCoachMark();
|
|
} else if (cardKey.currentContext != null && !previousChartTour) {
|
|
_previousKpiTutorial();
|
|
} else if (!chartTour || !previousChartTour) {
|
|
Future.delayed(Duration(milliseconds: 100), _startTutorialAfterRender);
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
Future<void> _previousKpiTutorial() async {
|
|
tourCard = (await _getWidget())!;
|
|
_initpreviousTargets();
|
|
tutorialCoachMark = TutorialCoachMark(
|
|
paddingFocus: 0,
|
|
useSafeArea: true,
|
|
pulseEnable: false,
|
|
hideSkip: true,
|
|
targets: previousMarriageTargets,
|
|
onFinish: () {
|
|
context.go('/myhomepage');
|
|
debugPrint('Previous Marriage Tutorial Finished');
|
|
},
|
|
)..show(context: context);
|
|
}
|
|
|
|
void _initmarriageTargets() {
|
|
final double screenWidth = MediaQuery.of(context).size.width;
|
|
final double screenHeight = MediaQuery.of(context).size.height;
|
|
// print('remaintarget $remainHeight');
|
|
// print ('at intint target : ${screenHeight-remainHeight}');
|
|
final double arrowPosition = screenHeight - tourCard;
|
|
marriageTargets = [
|
|
TargetFocus(
|
|
enableTargetTab: false,
|
|
identify: 'cardKey',
|
|
keyTarget: cardKey,
|
|
shape: ShapeLightFocus.RRect,
|
|
radius: 8,
|
|
paddingFocus: 0,
|
|
contents: [
|
|
createTargetContent(
|
|
text: AppLocalizations.of(context)!.kpiCards,
|
|
alignment: ContentAlign.top,
|
|
gap: 55,
|
|
space: 0,
|
|
),
|
|
TargetContent(
|
|
align: ContentAlign.bottom,
|
|
child: SizedBox(
|
|
width: double.infinity,
|
|
height: arrowPosition,
|
|
child: Stack(
|
|
children: [
|
|
Positioned(
|
|
bottom: arrowPosition * 0.41,
|
|
left: 16,
|
|
right: 16,
|
|
child: Column(
|
|
children: [
|
|
Align(
|
|
alignment: Alignment.bottomRight,
|
|
child: Padding(
|
|
padding: EdgeInsets.only(right: 10),
|
|
child: Text(
|
|
'3/7',
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
color: Colors.white,
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 5),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
ElevatedButton(
|
|
onPressed: () => handleSkip(),
|
|
style: ElevatedButton.styleFrom(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
side: const BorderSide(color: Colors.white),
|
|
backgroundColor: Colors.transparent,
|
|
elevation: 0,
|
|
),
|
|
child: Text(
|
|
AppLocalizations.of(context)!.skip,
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
color: Colors.white,
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
),
|
|
Row(
|
|
children: [
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
color: locale.languageCode == 'ar'
|
|
? Color(0xFF7DAFBC)
|
|
: null,
|
|
border: Border.all(
|
|
color: locale.languageCode == 'ar'
|
|
? Color(0xFF7DAFBC)
|
|
: Colors.white,
|
|
width: 2.0),
|
|
),
|
|
child: IconButton(
|
|
iconSize: 20,
|
|
icon: const Icon(Icons.arrow_back,
|
|
color: Colors.white),
|
|
onPressed: () {
|
|
if (locale.languageCode == 'ar') {
|
|
tutorialCoachMark.next();
|
|
} else {
|
|
ref
|
|
.read(chartsTourProvider.notifier)
|
|
.state = true;
|
|
ref
|
|
.read(previousHomeTourProvider
|
|
.notifier)
|
|
.state = false;
|
|
tutorialCoachMark.finish();
|
|
}
|
|
},
|
|
),
|
|
),
|
|
const SizedBox(width: 10),
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
color: locale.languageCode == 'ar'
|
|
? null
|
|
: Color(0xFF7DAFBC),
|
|
border: Border.all(
|
|
color: locale.languageCode == 'ar'
|
|
? Colors.white
|
|
: Color(0xFF7DAFBC),
|
|
width: 1),
|
|
),
|
|
child: IconButton(
|
|
iconSize: 20,
|
|
icon: const Icon(Icons.arrow_forward,
|
|
color: Colors.white),
|
|
onPressed: () {
|
|
if (locale.languageCode == 'ar') {
|
|
ref
|
|
.read(chartsTourProvider.notifier)
|
|
.state = true;
|
|
ref
|
|
.read(previousHomeTourProvider
|
|
.notifier)
|
|
.state = false;
|
|
tutorialCoachMark.finish();
|
|
} else {
|
|
tutorialCoachMark.next();
|
|
}
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
TargetContent(
|
|
padding: EdgeInsets.only(
|
|
bottom: 0,
|
|
top: 0,
|
|
),
|
|
align: ContentAlign.top,
|
|
child: SizedBox(
|
|
width: MediaQuery.of(context).size.width,
|
|
height: MediaQuery.of(context).size.height * 0.69,
|
|
child: Stack(children: [
|
|
Positioned(
|
|
left: MediaQuery.of(context).size.width * 0.40,
|
|
bottom: 0,
|
|
child: Image.asset(
|
|
'assets/app_tour/leftUp.png',
|
|
width: 100,
|
|
height: 90,
|
|
),
|
|
),
|
|
]),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
TargetFocus(
|
|
enableTargetTab: false,
|
|
identify: 'BookMarkKey',
|
|
keyTarget: bookMarkKey,
|
|
shape: ShapeLightFocus.RRect,
|
|
radius: 8,
|
|
paddingFocus: 12,
|
|
contents: [
|
|
createTargetContent(
|
|
text: AppLocalizations.of(context)!.bookMark,
|
|
alignment: ContentAlign.bottom,
|
|
gap: 53,
|
|
space: screenWidth * 0.22,
|
|
),
|
|
TargetContent(
|
|
align: ContentAlign.bottom,
|
|
child: SizedBox(
|
|
width: double.infinity,
|
|
height: MediaQuery.of(context).size.height * 0.65,
|
|
child: Stack(
|
|
children: [
|
|
Positioned(
|
|
bottom: 25,
|
|
left: 16,
|
|
right: 16,
|
|
child: Column(
|
|
children: [
|
|
Align(
|
|
alignment: Alignment.bottomRight,
|
|
child: Padding(
|
|
padding: EdgeInsets.only(right: 10),
|
|
child: Text(
|
|
'4/7',
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
color: Colors.white,
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 5),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
ElevatedButton(
|
|
onPressed: () => handleSkip(),
|
|
style: ElevatedButton.styleFrom(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
side: const BorderSide(color: Colors.white),
|
|
backgroundColor: Colors.transparent,
|
|
elevation: 0,
|
|
),
|
|
child: Text(
|
|
AppLocalizations.of(context)!.skip,
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
color: Colors.white,
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
),
|
|
Row(
|
|
children: [
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
color: locale.languageCode == 'ar'
|
|
? Color(0xFF7DAFBC)
|
|
: null,
|
|
border: Border.all(
|
|
color: locale.languageCode == 'ar'
|
|
? Color(0xFF7DAFBC)
|
|
: Colors.white,
|
|
width: 2.0),
|
|
),
|
|
child: IconButton(
|
|
iconSize: 20,
|
|
icon: const Icon(
|
|
Icons.arrow_back,
|
|
color: Colors.white,
|
|
),
|
|
onPressed: () {
|
|
if (locale.languageCode == 'ar') {
|
|
ref
|
|
.read(chartsTourProvider.notifier)
|
|
.state = true;
|
|
ref
|
|
.read(scaffoldTourProvider.notifier)
|
|
.state = false;
|
|
tutorialCoachMark.finish();
|
|
} else {
|
|
tutorialCoachMark.previous();
|
|
}
|
|
},
|
|
),
|
|
),
|
|
const SizedBox(width: 10),
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
color: locale.languageCode == 'ar'
|
|
? null
|
|
: Color(0xFF7DAFBC),
|
|
border: Border.all(
|
|
color: locale.languageCode == 'ar'
|
|
? Colors.white
|
|
: Color(0xFF7DAFBC),
|
|
width: 1),
|
|
),
|
|
child: IconButton(
|
|
iconSize: 20,
|
|
icon: const Icon(
|
|
Icons.arrow_forward,
|
|
color: Colors.white,
|
|
),
|
|
onPressed: () {
|
|
if (locale.languageCode == 'ar') {
|
|
tutorialCoachMark.previous();
|
|
} else {
|
|
ref
|
|
.read(chartsTourProvider.notifier)
|
|
.state = true;
|
|
ref
|
|
.read(scaffoldTourProvider.notifier)
|
|
.state = false;
|
|
tutorialCoachMark.finish();
|
|
}
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
TargetContent(
|
|
padding: EdgeInsets.only(left: 0, top: 0),
|
|
align: ContentAlign.right,
|
|
child: Container(
|
|
width: screenWidth / 4,
|
|
height: screenHeight * 0.2,
|
|
child: Stack(
|
|
children: [
|
|
Image.asset(
|
|
'assets/app_tour/leftDown.png',
|
|
fit: BoxFit.contain,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
];
|
|
}
|
|
|
|
void _initpreviousTargets() {
|
|
final double screenWidth = MediaQuery.of(context).size.width;
|
|
final double screenHeight = MediaQuery.of(context).size.height;
|
|
// print('remaintarget $remainHeight');
|
|
// print ('at intint target : ${screenHeight-remainHeight}');
|
|
final double arrowPosition = screenHeight - tourCard;
|
|
previousMarriageTargets = [
|
|
TargetFocus(
|
|
enableTargetTab: false,
|
|
identify: 'BookMarkKey',
|
|
keyTarget: bookMarkKey,
|
|
shape: ShapeLightFocus.RRect,
|
|
radius: 8,
|
|
paddingFocus: 12,
|
|
contents: [
|
|
createTargetContent(
|
|
text: AppLocalizations.of(context)!.bookMark,
|
|
space: screenWidth * 0.22,
|
|
alignment: ContentAlign.bottom,
|
|
gap: 53,
|
|
),
|
|
TargetContent(
|
|
align: ContentAlign.bottom,
|
|
child: SizedBox(
|
|
width: double.infinity,
|
|
height: MediaQuery.of(context).size.height * 0.65,
|
|
child: Stack(
|
|
children: [
|
|
Positioned(
|
|
bottom: 25,
|
|
left: 16,
|
|
right: 16,
|
|
child: Column(
|
|
children: [
|
|
Align(
|
|
alignment: Alignment.bottomRight,
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(right: 10.0),
|
|
child: Text(
|
|
'4/7',
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
color: Colors.white,
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 5),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
ElevatedButton(
|
|
onPressed: () => handleSkip(),
|
|
style: ElevatedButton.styleFrom(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
side: const BorderSide(color: Colors.white),
|
|
backgroundColor: Colors.transparent,
|
|
elevation: 0,
|
|
),
|
|
child: Text(
|
|
AppLocalizations.of(context)!.skip,
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
color: Colors.white,
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
),
|
|
Row(
|
|
children: [
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
color: locale.languageCode == 'ar'
|
|
? Color(0xFF7DAFBC)
|
|
: null,
|
|
border: Border.all(
|
|
color: locale.languageCode == 'ar'
|
|
? Color(0xFF7DAFBC)
|
|
: Colors.white,
|
|
width: 2.0,
|
|
),
|
|
),
|
|
child: IconButton(
|
|
iconSize: 20,
|
|
icon: const Icon(
|
|
Icons.arrow_back,
|
|
color: Colors.white,
|
|
),
|
|
onPressed: () {
|
|
if (locale.languageCode == 'ar') {
|
|
ref
|
|
.read(chartsTourProvider.notifier)
|
|
.state = true;
|
|
ref
|
|
.read(scaffoldTourProvider.notifier)
|
|
.state = false;
|
|
tutorialCoachMark.finish();
|
|
} else {
|
|
tutorialCoachMark.next();
|
|
}
|
|
},
|
|
),
|
|
),
|
|
const SizedBox(width: 10),
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
color: locale.languageCode == 'ar'
|
|
? null
|
|
: Color(0xFF7DAFBC),
|
|
border: Border.all(
|
|
color: locale.languageCode == 'ar'
|
|
? Colors.white
|
|
: Color(0xFF7DAFBC),
|
|
width: 2.0,
|
|
),
|
|
),
|
|
child: IconButton(
|
|
iconSize: 20,
|
|
icon: const Icon(Icons.arrow_forward,
|
|
color: Colors.white),
|
|
onPressed: () {
|
|
if (locale.languageCode == 'ar') {
|
|
tutorialCoachMark.next();
|
|
} else {
|
|
ref
|
|
.read(chartsTourProvider.notifier)
|
|
.state = true;
|
|
ref
|
|
.read(scaffoldTourProvider.notifier)
|
|
.state = false;
|
|
tutorialCoachMark.finish();
|
|
}
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
TargetContent(
|
|
padding: EdgeInsets.only(left: 0, top: 0),
|
|
align: ContentAlign.right,
|
|
child: SizedBox(
|
|
width: screenWidth / 4,
|
|
height: screenHeight * 0.2,
|
|
child: Stack(
|
|
children: [
|
|
Image.asset(
|
|
'assets/app_tour/leftDown.png',
|
|
fit: BoxFit.contain,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
TargetFocus(
|
|
enableTargetTab: false,
|
|
identify: 'cardKey',
|
|
keyTarget: cardKey,
|
|
shape: ShapeLightFocus.RRect,
|
|
radius: 8,
|
|
paddingFocus: 0,
|
|
contents: [
|
|
createTargetContent(
|
|
text: AppLocalizations.of(context)!.kpiCards,
|
|
space: 0,
|
|
alignment: ContentAlign.top,
|
|
gap: 55,
|
|
),
|
|
TargetContent(
|
|
align: ContentAlign.bottom,
|
|
child: SizedBox(
|
|
width: double.infinity,
|
|
height: arrowPosition,
|
|
child: Stack(
|
|
children: [
|
|
Positioned(
|
|
bottom: arrowPosition * 0.41,
|
|
left: 16,
|
|
right: 16,
|
|
child: Column(
|
|
children: [
|
|
Align(
|
|
alignment: Alignment.bottomRight,
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(right: 10.0),
|
|
child: Text(
|
|
'3/7',
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
color: Colors.white,
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 5),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
ElevatedButton(
|
|
onPressed: () => handleSkip(),
|
|
style: ElevatedButton.styleFrom(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
side: const BorderSide(color: Colors.white),
|
|
backgroundColor: Colors.transparent,
|
|
elevation: 0,
|
|
),
|
|
child: Text(
|
|
AppLocalizations.of(context)!.skip,
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
color: Colors.white,
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
),
|
|
Row(
|
|
children: [
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
color: locale.languageCode == 'ar'
|
|
? Color(0xFF7DAFBC)
|
|
: null,
|
|
border: Border.all(
|
|
color: locale.languageCode == 'ar'
|
|
? Color(0xFF7DAFBC)
|
|
: Colors.white,
|
|
width: 2.0,
|
|
),
|
|
),
|
|
child: IconButton(
|
|
iconSize: 20,
|
|
icon: const Icon(
|
|
Icons.arrow_back,
|
|
color: Colors.white,
|
|
),
|
|
onPressed: () {
|
|
if (locale.languageCode == 'ar') {
|
|
tutorialCoachMark.previous();
|
|
} else {
|
|
ref
|
|
.read(previousChartsTourProvider
|
|
.notifier)
|
|
.state = true;
|
|
ref
|
|
.read(previousHomeTourProvider
|
|
.notifier)
|
|
.state = false;
|
|
tutorialCoachMark.finish();
|
|
}
|
|
},
|
|
),
|
|
),
|
|
const SizedBox(width: 10),
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
color: locale.languageCode == 'ar'
|
|
? null
|
|
: Color(0xFF7DAFBC),
|
|
border: Border.all(
|
|
color: locale.languageCode == 'ar'
|
|
? Colors.white
|
|
: Color(0xFF7DAFBC),
|
|
width: 1),
|
|
),
|
|
child: IconButton(
|
|
iconSize: 20,
|
|
icon: const Icon(
|
|
Icons.arrow_forward,
|
|
color: Colors.white,
|
|
),
|
|
onPressed: () {
|
|
if (locale.languageCode == 'ar') {
|
|
ref
|
|
.read(previousChartsTourProvider
|
|
.notifier)
|
|
.state = true;
|
|
ref
|
|
.read(previousHomeTourProvider
|
|
.notifier)
|
|
.state = false;
|
|
tutorialCoachMark.finish();
|
|
} else {
|
|
tutorialCoachMark.previous();
|
|
}
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
TargetContent(
|
|
padding: EdgeInsets.only(
|
|
bottom: 0,
|
|
top: 0,
|
|
),
|
|
align: ContentAlign.top,
|
|
child: SizedBox(
|
|
width: MediaQuery.of(context).size.width,
|
|
height: MediaQuery.of(context).size.height * 0.69,
|
|
child: Stack(children: [
|
|
Positioned(
|
|
left: MediaQuery.of(context).size.width * 0.40,
|
|
bottom: 0,
|
|
child: Image.asset(
|
|
'assets/app_tour/leftUp.png',
|
|
width: 100,
|
|
height: 90,
|
|
),
|
|
),
|
|
]),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
];
|
|
}
|
|
|
|
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
|
|
print("TABFiltered Data: $filterData");
|
|
}
|
|
}
|
|
|
|
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<String, List<Map<String, dynamic>>> 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<Map<String, String>> _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;
|
|
// });
|
|
// }
|
|
|
|
void processChartData(chartsData) {
|
|
print("PROCESSING FILTER CHART DATa");
|
|
|
|
// Group data by 'kpi'
|
|
Map<String, List<Map<String, dynamic>>> 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 with tab_heading
|
|
List<Map<String, String>> _tabs = groupedData.entries.map((entry) {
|
|
String kpi = entry.key;
|
|
var firstChart = entry.value.first;
|
|
|
|
// Extract tab_heading from the first chart in the grouped list
|
|
String tabHeading = entry.value.isNotEmpty
|
|
? entry.value.first['tab_heading'] ?? 'Unknown'
|
|
: 'Unknown';
|
|
|
|
int tabOrder = int.tryParse(firstChart['tab_order'] ?? '0') ?? 0;
|
|
|
|
String formattedKpi = kpi
|
|
.split('_') // Split by underscore
|
|
.map((word) => word.isNotEmpty
|
|
? word[0].toUpperCase() + word.substring(1)
|
|
: '') // Capitalize
|
|
.join(' '); // Join words with space
|
|
return {'id': kpi, 'name': tabHeading, 'order': tabOrder.toString()};
|
|
}).toList();
|
|
|
|
// Sort tabs by tab_order
|
|
_tabs.sort((a, b) {
|
|
int orderA = int.tryParse(a['order'] ?? '0') ?? 0;
|
|
int orderB = int.tryParse(b['order'] ?? '0') ?? 0;
|
|
return orderA.compareTo(orderB);
|
|
});
|
|
|
|
print('Sorted Tabs: $_tabs');
|
|
|
|
// print('Grouped Data: $groupedData');
|
|
print('Tabs: $_tabs');
|
|
|
|
setState(() {
|
|
_tabsData = _tabs;
|
|
});
|
|
}
|
|
|
|
Future<void> fetchChartData(String dataSets, locale, kpi, filter_data) async {
|
|
var data =
|
|
await apiService.fetchChartData(dataSets, locale, kpi, filter_data);
|
|
|
|
tabFilteredKpi = kpi;
|
|
print('FETCH KPI- $kpi');
|
|
print('FETCH KFilter- $filter_data');
|
|
print('locale1 - $locale');
|
|
// print('locale11 - $loacleData');
|
|
|
|
// Store filter_data if it has a value
|
|
if (filter_data != null && filter_data is List && filter_data.isNotEmpty) {
|
|
selectedFiltersApi = filter_data.map<Map<String, dynamic>>((entry) {
|
|
if (entry is Map<dynamic, dynamic>) {
|
|
return entry.map<String, dynamic>(
|
|
(key, value) => MapEntry(key.toString(), value));
|
|
}
|
|
return {};
|
|
}).toList();
|
|
|
|
print('FETVCFILTEr: $selectedFiltersApi');
|
|
}
|
|
|
|
if (data.containsKey('filterData')) {
|
|
var filterData = data['filterData'];
|
|
filterDataSet = filterData.entries
|
|
.map((entry) => {'key': entry.key, 'value': entry.value})
|
|
.toList();
|
|
print("filterDataf1:- $filterData");
|
|
print("filterDataf11:- $filterDataSet");
|
|
} else {
|
|
filterDataSet = data['filterData'] ?? [];
|
|
print('filterData not found!');
|
|
}
|
|
|
|
setState(() {
|
|
isChartData = data['isChartData'] ?? [];
|
|
nonChartData = data['nonChartData'] ?? [];
|
|
// filterData = data['filterData'] ?? [];
|
|
|
|
originalChartsData = List.from(isChartData); // Store original data
|
|
originalCardData = List.from(nonChartData);
|
|
|
|
chartsData = isChartData;
|
|
cardData = nonChartData;
|
|
chartScreenData = data['chartScreenData'] ?? {};
|
|
|
|
processChartData(chartsData);
|
|
|
|
onTabSelected(tabFilteredKpi);
|
|
|
|
print('chartsData :- $chartsData');
|
|
print('cardData :- $cardData');
|
|
print('chartScreenData :- $chartScreenData');
|
|
// print('filterData :- $filterData');
|
|
isLoading = false;
|
|
});
|
|
// print('chartsData -$chartsData');
|
|
// print('cardData -$cardData');
|
|
}
|
|
|
|
void applyFilters(BuildContext context, List filters, List data,
|
|
List dataCard, List selectedFilters) {
|
|
setState(() {
|
|
isLoading = true;
|
|
});
|
|
Navigator.pop(context);
|
|
print('applyFilters called');
|
|
print('Selected ApplyFilters: $selectedFilters');
|
|
|
|
if (selectedFilters.every((filter) => filter['filter_data'].isEmpty)) {
|
|
setState(() {
|
|
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);
|
|
return; // Exit early since all filter_data are empty
|
|
}
|
|
|
|
// Transform the selectedFilters list into the required format
|
|
formattedFilters = selectedFilters
|
|
// .where((filter) => filter['filter_data'] != null && filter['filter_data'].isNotEmpty)
|
|
.map((filter) {
|
|
return {
|
|
'filter_key': filter['filter_key'],
|
|
'filter_data':
|
|
filter['filter_data'].map((item) => item.toString()).toList()
|
|
};
|
|
}).toList();
|
|
|
|
print('Formatted Filters1: $formattedFilters');
|
|
|
|
// Convert to JSON format
|
|
String selectedFormatFilters =
|
|
jsonEncode({'kpi': tabWiseKpi, 'filter_data': formattedFilters});
|
|
|
|
print('Formatted Filters2: $selectedFormatFilters');
|
|
final locale = ref.watch(localeProvider)?.languageCode ?? 'en';
|
|
|
|
print('locale2- $locale');
|
|
|
|
// Call fetchChartData with correct parameters
|
|
fetchChartData(
|
|
widget.dataSets,
|
|
locale,
|
|
tabWiseKpi, // Ensure you are passing the correct KPI here
|
|
formattedFilters // Pass the formatted filter data here
|
|
);
|
|
// Navigator.pop(context);
|
|
|
|
selectedFiltersStorage = List.from(selectedFilters);
|
|
print('selectedFiltersStoraged- $selectedFiltersStorage');
|
|
}
|
|
|
|
void applyFilters1(BuildContext context, List filters, List data,
|
|
List dataCard, List selectedFilters) {
|
|
print("applyFilters called");
|
|
print("Selected ApplyFilters: $selectedFilters");
|
|
print("Selected data: $data");
|
|
|
|
if (selectedFilters.every((filter) => filter['filter_data'].isEmpty)) {
|
|
setState(() {
|
|
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);
|
|
return; // Exit early since all filter_data are empty
|
|
}
|
|
|
|
// Loop through each chart data in the `data` list
|
|
List filteredData = [];
|
|
Set<String> addedChartIds = {}; // Track unique chart identifiers
|
|
for (var chart in data) {
|
|
final groupBy = chart['group_by'];
|
|
List response = chart['response'] ?? [];
|
|
final chartHeading = chart['chart_heading'];
|
|
|
|
// Filter the response based on selected filters
|
|
var chartFilteredData = response.where((responseItem) {
|
|
final obsKey = responseItem['ObsKey'];
|
|
|
|
// Check if each selected filter's `filter_data` matches `ObsKey` values
|
|
return selectedFilters.every((filter) {
|
|
final filterKey = filter['filter_key'];
|
|
final filterValues = filter['filter_data'];
|
|
|
|
// Ensure the filter_key exists in the `ObsKey` and check if it matches any of the `filter_data`
|
|
if (groupBy == filterKey && obsKey.containsKey(filterKey)) {
|
|
final obsKeyValue = obsKey[filterKey]?.toString();
|
|
return filterValues.isEmpty || filterValues.contains(obsKeyValue);
|
|
}
|
|
|
|
if (filterKey == 'TIME_PERIOD' && obsKey.containsKey('TIME_PERIOD')) {
|
|
final timePeriodValue = obsKey['TIME_PERIOD']?.toString();
|
|
return filterValues.isEmpty ||
|
|
filterValues.contains(timePeriodValue);
|
|
}
|
|
|
|
return true;
|
|
});
|
|
}).toList();
|
|
|
|
print('chartFilteredData- $chartFilteredData');
|
|
|
|
// If chartFilteredData is empty, print the message
|
|
if (chartFilteredData.isEmpty) {
|
|
if (kDebugMode) {
|
|
print(
|
|
"Selected year: ${selectedFilters.firstWhere((f) => f['filter_key'] == 'TIME_PERIOD', orElse: () => {
|
|
'filter_data': ['Unknown']
|
|
})['filter_data']} - No data for chart: : $chartHeading");
|
|
}
|
|
|
|
// Add the complete chart data to the filteredData list
|
|
filteredData.add({
|
|
...chart,
|
|
'response': response, // Return full unfiltered response
|
|
});
|
|
|
|
continue;
|
|
}
|
|
|
|
// If any data matches the filter, add the whole chart data object
|
|
// Add filtered chart only once
|
|
if (chartFilteredData.isNotEmpty &&
|
|
!addedChartIds.contains(chart['chart_heading'])) {
|
|
filteredData.add({
|
|
...chart,
|
|
'response': chartFilteredData,
|
|
});
|
|
addedChartIds
|
|
.add(chart['chart_heading']); // Track by a unique identifier
|
|
}
|
|
}
|
|
|
|
// List filteredCardData = [];
|
|
// for (var chart in dataCard) {
|
|
// Map<String, dynamic> chartData = Map<String, dynamic>.from(chart);
|
|
// // Extract response data for filtering
|
|
// List response = chartData['response'] ?? [];
|
|
|
|
// // Filter the response based on selected filters
|
|
// var cardFilteredData = response.where((responseItem) {
|
|
// final obsKey = responseItem['ObsKey'];
|
|
|
|
// // Check if each selected filter's `filter_data` matches `ObsKey` values
|
|
// return selectedFilters.every((filter) {
|
|
// final filterKey = filter['filter_key'];
|
|
// final filterValues = filter['filter_data'];
|
|
|
|
// // Ensure the filter_key exists in the `ObsKey` and check if it matches any of the `filter_data`
|
|
// if (obsKey.containsKey(filterKey)) {
|
|
// final obsKeyValue = obsKey[filterKey]?.toString();
|
|
// return filterValues.isEmpty || filterValues.contains(obsKeyValue);
|
|
// }
|
|
// return false;
|
|
// });
|
|
// }).toList();
|
|
//
|
|
// // If any data matches the filter, add the whole chart data object
|
|
// if (cardFilteredData.isNotEmpty) {
|
|
// filteredCardData.add({
|
|
// ...chart, // Include all other properties of the chart object
|
|
// 'response': cardFilteredData, // Only include filtered response data
|
|
// });
|
|
// }
|
|
// }
|
|
|
|
// Update the chartsData with the filtered data
|
|
setState(() {
|
|
chartsData = filteredData;
|
|
// cardData = filteredCardData; // Adjust this part as needed
|
|
});
|
|
|
|
print("Filtered Data: $filteredData");
|
|
// print("Filtered Card: $filteredCardData");
|
|
|
|
// Go back after applying filters
|
|
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) {
|
|
// Hide system UI (including bottom navigation bar)
|
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
|
|
print('selectedFiltersStoraged1- $selectedFiltersStorage');
|
|
|
|
// Initialize selected filters structure from the stored value, if exists
|
|
List<Map<String, dynamic>> selectedFilters =
|
|
selectedFiltersStorage.isNotEmpty
|
|
? List.from(selectedFiltersStorage) // Use stored filters
|
|
: filters.map((filter) {
|
|
return {"filter_key": filter["filter_key"], "filter_data": []};
|
|
}).toList(); // Or initialize empty filters
|
|
|
|
print('selectedFiltersStoragedrgt- $selectedFiltersStorage');
|
|
|
|
showModalBottomSheet(
|
|
context: context,
|
|
isScrollControlled: true,
|
|
backgroundColor: Colors.transparent,
|
|
builder: (BuildContext context) {
|
|
return Align(
|
|
alignment: Alignment.centerRight,
|
|
child: Container(
|
|
width: MediaQuery.of(context).size.width * 0.7,
|
|
height: MediaQuery.of(context).size.height,
|
|
color: Colors.white,
|
|
child: Column(
|
|
children: [
|
|
// Header Section
|
|
Padding(
|
|
padding: const EdgeInsets.only(
|
|
top: 26.0, bottom: 0.0, left: 16.0, right: 16.0),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
SvgPicture.asset(
|
|
UaeNumbersAssetPath.filterCharts,
|
|
semanticsLabel: 'Filter',
|
|
colorFilter: ColorFilter.mode(
|
|
Color(0xFF8E8E8E), BlendMode.srcIn),
|
|
width: 25,
|
|
height: 25,
|
|
),
|
|
SizedBox(width: 8),
|
|
Text(
|
|
context.translate(
|
|
'Filters',
|
|
'المرشحات',
|
|
),
|
|
style: TextStyle(
|
|
color: Color(0xFF8E8E8E),
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.w500,
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
// IconButton(
|
|
// icon: Icon(Icons.close),
|
|
// onPressed: () => Navigator.pop(context),
|
|
// ),
|
|
],
|
|
),
|
|
),
|
|
// Filters Section
|
|
Divider(
|
|
color: Color(0xFFA7B5C5),
|
|
),
|
|
Expanded(
|
|
child: ListView.builder(
|
|
itemCount: filters.length,
|
|
itemBuilder: (context, index) {
|
|
filters.sort((a, b) => a["filter_text_and_order"]["order"]
|
|
.compareTo(b["filter_text_and_order"]["order"]));
|
|
|
|
final filter = filters[index];
|
|
final filterKey = filter["filter_key"];
|
|
final filterData = filter["filter_data"];
|
|
final filter_text_and_order =
|
|
filter["filter_text_and_order"];
|
|
final fieldOrder = filter_text_and_order['order'];
|
|
|
|
return StatefulBuilder(
|
|
builder: (context, setState) {
|
|
// Get the corresponding selected filter object
|
|
var selectedFilter = selectedFilters
|
|
.firstWhere((f) => f["filter_key"] == filterKey);
|
|
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 16.0, vertical: 8.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
context.translate(filter_text_and_order['en'],
|
|
filter_text_and_order['ar']),
|
|
// capitalizeAndSplit(filterKey),
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w700,
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
color: Color(0xFF8E8E8E),
|
|
),
|
|
),
|
|
InkWell(
|
|
onTap: () {
|
|
showDialog(
|
|
context: context,
|
|
builder: (BuildContext context) {
|
|
return StatefulBuilder(
|
|
builder: (context, dialogSetState) {
|
|
final locale =
|
|
ref.watch(localeProvider);
|
|
|
|
print('localelocale $locale');
|
|
return AlertDialog(
|
|
title: Text(
|
|
// "${locale == 'ar' ? 'يختار ' : 'Select '}"
|
|
"${context.translate(filter_text_and_order['en'], filter_text_and_order['ar'])}",
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
)
|
|
),),
|
|
content: SingleChildScrollView(
|
|
child: ListBody(
|
|
children: filterData
|
|
.map<Widget>((value) {
|
|
return CheckboxListTile(
|
|
title: Text(
|
|
value.toString(),
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
)
|
|
),),
|
|
value: selectedFilter[
|
|
"filter_data"]
|
|
.contains(value),
|
|
onChanged:
|
|
(bool? isChecked) {
|
|
dialogSetState(() {
|
|
if (isChecked ==
|
|
true) {
|
|
selectedFilter[
|
|
"filter_data"]
|
|
.add(value);
|
|
} else {
|
|
selectedFilter[
|
|
"filter_data"]
|
|
.remove(value);
|
|
}
|
|
});
|
|
},
|
|
);
|
|
}).toList(),
|
|
),
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () {
|
|
// Store the updated selected filters after dialog closes
|
|
setState(() {});
|
|
Navigator.pop(context);
|
|
},
|
|
child: Text(
|
|
context.translate(
|
|
'OK',
|
|
'نعم',
|
|
),
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
)
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
},
|
|
);
|
|
},
|
|
child: Container(
|
|
width: double.infinity,
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: 16.0, vertical: 12.0),
|
|
decoration: BoxDecoration(
|
|
border:
|
|
Border.all(color: Color(0xFF7296BE)),
|
|
borderRadius: BorderRadius.circular(8.0),
|
|
),
|
|
child: Wrap(
|
|
spacing: 8.0,
|
|
runSpacing: 4.0,
|
|
children:
|
|
selectedFilter["filter_data"].isEmpty
|
|
? [
|
|
Text(
|
|
// "${locale == 'ar' ? 'يختار ' : 'Select '}"
|
|
"${context.translate(filter_text_and_order['en'], filter_text_and_order['ar'])}",
|
|
style: TextStyle(
|
|
color: Colors.grey,
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),),
|
|
)
|
|
]
|
|
: selectedFilter["filter_data"]
|
|
.map<Widget>((value) {
|
|
return Chip(
|
|
label: Text(value,
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
)
|
|
),),
|
|
// backgroundColor: Colors.white, // Default background color
|
|
onDeleted: () {
|
|
setState(() {
|
|
selectedFilter[
|
|
"filter_data"]
|
|
.remove(value);
|
|
});
|
|
},
|
|
);
|
|
}).toList(),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
),
|
|
|
|
Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
child: ElevatedButton(
|
|
onPressed: () {
|
|
setState(() {
|
|
isLoading = true;
|
|
});
|
|
selectedFilters.forEach((filter) {
|
|
filter["filter_data"].clear();
|
|
});
|
|
setState(() {
|
|
chartsData = List.from(originalTabChartsData);
|
|
cardData = List.from(originalTabCardData);
|
|
});
|
|
selectedFiltersStorage.clear();
|
|
formattedFilters = [];
|
|
|
|
final locale =
|
|
ref.watch(localeProvider)?.languageCode ?? 'en';
|
|
|
|
print('locale22- $locale');
|
|
|
|
fetchChartData(
|
|
widget.dataSets,
|
|
locale,
|
|
tabWiseKpi, // Ensure you are passing the correct KPI here
|
|
formattedFilters // Pass the formatted filter data here
|
|
);
|
|
Navigator.pop(context);
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Color(0xFF92722A),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
),
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 0.0,
|
|
vertical: 14.2), // Added padding
|
|
child: Text(
|
|
context.translate('Clear', 'واضح'),
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
color: Colors.white,
|
|
fontSize: 13,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
overflow:
|
|
TextOverflow.ellipsis, // Prevents wrapping
|
|
maxLines: 1, // Ensures single line
|
|
),
|
|
),
|
|
),
|
|
),
|
|
SizedBox(width: 16),
|
|
Expanded(
|
|
child: ElevatedButton(
|
|
onPressed: () {
|
|
print(
|
|
"Selected Filters before applying: $selectedFilters");
|
|
|
|
if (selectedFilters.every((filter) => filter['filter_data'].isEmpty)) {
|
|
setState(() {
|
|
isLoading = true;
|
|
});
|
|
selectedFilters.forEach((filter) {
|
|
filter["filter_data"].clear();
|
|
});
|
|
setState(() {
|
|
chartsData = List.from(originalTabChartsData);
|
|
cardData = List.from(originalTabCardData);
|
|
});
|
|
selectedFiltersStorage.clear();
|
|
formattedFilters = [];
|
|
|
|
final locale =
|
|
ref.watch(localeProvider)?.languageCode ?? 'en';
|
|
|
|
print('locale22- $locale');
|
|
|
|
fetchChartData(
|
|
widget.dataSets,
|
|
locale,
|
|
tabWiseKpi, // Ensure you are passing the correct KPI here
|
|
formattedFilters // Pass the formatted filter data here
|
|
);
|
|
Navigator.pop(context);
|
|
} else {
|
|
setState(() {
|
|
// isLoading = true;
|
|
chartsData = originalTabChartsData;
|
|
cardData = originalTabCardData;
|
|
});
|
|
|
|
applyFilters(context, filters, chartsData, cardData,
|
|
selectedFilters);
|
|
// selectedFiltersStorage = List.from(selectedFilters);
|
|
}
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Color(0xFF92722A),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
),
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 0.0,
|
|
vertical: 14.2), // Added padding
|
|
child: FittedBox(
|
|
fit: BoxFit
|
|
.scaleDown, // Ensures text resizes if necessary
|
|
child: Text(
|
|
context.translate('Filter', 'فلتر'),
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
color: Colors.white,
|
|
fontSize: 13,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
overflow:
|
|
TextOverflow.ellipsis, // Prevents wrapping
|
|
maxLines: 1, // Ensures single line
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
).whenComplete(() {
|
|
// Restore system UI when modal is dismissed
|
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
|
});
|
|
}
|
|
|
|
void onTabSelected(String tabId) {
|
|
print('Selected Tab on: $tabId');
|
|
tabWiseKpi = tabId;
|
|
print('Selected Tab tabWiseKpi: $tabWiseKpi');
|
|
|
|
selectedFiltersStorage.clear();
|
|
|
|
if (selectedFiltersApi.isNotEmpty && tabWiseKpi == tabFilteredKpi) {
|
|
selectedFiltersStorage = List.from(selectedFiltersApi);
|
|
}
|
|
|
|
filterData = filterDataSet
|
|
.where((item) => item['key'] == tabId)
|
|
.map((item) => item['value'])
|
|
.where((item) => item != null && item is Iterable)
|
|
.expand((item) => item)
|
|
.toList();
|
|
|
|
print("FilteringDAtss START1d - $filterData");
|
|
print('TABId1 - $tabId');
|
|
|
|
Map<String, int> monthMap = {
|
|
'jan': 1,
|
|
'feb': 2,
|
|
'mar': 3,
|
|
'apr': 4,
|
|
'may': 5,
|
|
'jun': 6,
|
|
'jul': 7,
|
|
'aug': 8,
|
|
'sep': 9,
|
|
'oct': 10,
|
|
'nov': 11,
|
|
'dec': 12
|
|
};
|
|
|
|
for (var item in filterData) {
|
|
if (item['filter_key'] == 'TIME_PERIOD') {
|
|
List<String> timePeriods = List<String>.from(item['filter_data']);
|
|
|
|
timePeriods.sort((a, b) {
|
|
List<String> aParts = a.split(RegExp(r'[-_]'));
|
|
List<String> bParts = b.split(RegExp(r'[-_]'));
|
|
|
|
int yearA = int.parse(aParts[0]);
|
|
int yearB = int.parse(bParts[0]);
|
|
|
|
// if (yearA != yearB) return yearA.compareTo(yearB);
|
|
if (yearA != yearB)
|
|
return yearB.compareTo(yearA); // Sort years in descending order
|
|
|
|
if (aParts.length == 1) return -1; // Year-only comes first
|
|
if (bParts.length == 1) return 1; // Year-only comes first
|
|
|
|
int monthA =
|
|
int.tryParse(aParts[1]) ?? monthMap[aParts[1].toLowerCase()] ?? 0;
|
|
int monthB =
|
|
int.tryParse(bParts[1]) ?? monthMap[bParts[1].toLowerCase()] ?? 0;
|
|
|
|
return monthB.compareTo(
|
|
monthA); // Sort months in descending order within the same year
|
|
// return monthA.compareTo(monthB);
|
|
});
|
|
|
|
item['filter_data'] = timePeriods;
|
|
} else if (item['filter_key'] == 'REF_AREA') {
|
|
continue;
|
|
} else {
|
|
// Sort alphabetically for all other cases
|
|
List<String> otherFilters = List<String>.from(item['filter_data']);
|
|
otherFilters.sort((a, b) => a.toLowerCase().compareTo(b.toLowerCase()));
|
|
item['filter_data'] = otherFilters;
|
|
}
|
|
}
|
|
|
|
// for (var item in filterData) {
|
|
// if (item['filter_key'] == 'TIME_PERIOD') {
|
|
// // Convert the values to integers, sort them, and convert back to strings
|
|
// List<int> timePeriodData = item['filter_data']
|
|
// .map<int>((e) => int.parse(e.toString())) // Convert to int
|
|
// .toList();
|
|
// timePeriodData.sort((a, b) => a.compareTo(b)); // Sort numerically
|
|
//
|
|
// // Optionally, convert sorted integers back to strings if necessary
|
|
// item['filter_data'] = timePeriodData.map((e) => e.toString()).toList();
|
|
// }
|
|
// }
|
|
|
|
print('TABId - $tabId');
|
|
|
|
print("TABFiltered Data: $filterData");
|
|
setState(() {
|
|
chartsData = originalChartsData;
|
|
cardData = originalCardData;
|
|
});
|
|
performActionForTab(tabId);
|
|
}
|
|
|
|
void performActionForTab(String tabId) {
|
|
print('Selected Tab perform: $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 before $tabId: $tabFilteredCardData');
|
|
|
|
tabFilteredCardData
|
|
.sort((a, b) => (a['order_id'] as int).compareTo(b['order_id'] as int));
|
|
|
|
print(
|
|
'tabFilteredCardData Data for Tab after $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 / 250;
|
|
// } else if (itemCount == 4) {
|
|
// return 190.5 / 180;
|
|
// } else {
|
|
// return 180.0 / 180; // Default for more items
|
|
// // return 190.5 / 180;
|
|
// }
|
|
// }
|
|
|
|
double calculateAspectRatio(int crossAxisCount, List<dynamic> cardData) {
|
|
// Determine a default aspect ratio based on the most common chart type in the list
|
|
if (cardData.any((item) =>
|
|
item['chart_type'] == 'total' || item['chart_type'] == 'average')) {
|
|
// return crossAxisCount == 1 ? 0.2 : (crossAxisCount > 2 ? 0.9 : 0.7);
|
|
return crossAxisCount == 2 ? 1.0 : 0.7; // Larger Content
|
|
// return crossAxisCount == 2 ? 1.5 : 0.9;
|
|
} else {
|
|
return crossAxisCount == 2 ? 1.5 : 0.9;
|
|
// Shorter Content
|
|
}
|
|
}
|
|
|
|
Future<void> shareCurrentPage(BuildContext context, bool isSharing) async {
|
|
if (isSharing) return; // Prevent multiple taps
|
|
|
|
isSharing = true;
|
|
setState(() {
|
|
isLoading = true;
|
|
});
|
|
|
|
try {
|
|
final String currentRoute = GoRouterState.of(context).uri.toString();
|
|
final String baseAppLink = 'https://fcscapp.onelink.me';
|
|
final String shareLink = '$baseAppLink$currentRoute';
|
|
final String shareText = 'Check this out!\n\n$shareLink';
|
|
print('Generated Share Link: $shareLink');
|
|
|
|
// Optional: Capture SVG to image
|
|
final String svgAssetPath = 'assets/backgrounds/share/fcsc.svg';
|
|
final screenshotController = ScreenshotController();
|
|
final svgWidget = SvgPicture.asset(
|
|
svgAssetPath,
|
|
width: 200,
|
|
height: 200,
|
|
);
|
|
|
|
final Uint8List? capturedImage =
|
|
await screenshotController.captureFromWidget(
|
|
Material(child: svgWidget),
|
|
);
|
|
|
|
if (capturedImage == null) {
|
|
print('SVG capture failed, proceeding without image.');
|
|
await Share.share(shareText, subject: 'App Link');
|
|
} else {
|
|
final directory = await getTemporaryDirectory();
|
|
final file = File('${directory.path}/share_image.png');
|
|
await file.writeAsBytes(capturedImage);
|
|
|
|
await Share.shareXFiles(
|
|
[XFile(file.path)],
|
|
text: shareText,
|
|
subject: 'Shared Content',
|
|
);
|
|
|
|
await file.delete();
|
|
}
|
|
setState(() {
|
|
isLoading = false;
|
|
});
|
|
} catch (e) {
|
|
setState(() {
|
|
isLoading = false;
|
|
});
|
|
print("Error sharing content: $e");
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text('Error sharing: ${e.toString()}')),
|
|
);
|
|
} finally {
|
|
setState(() {
|
|
isLoading = false;
|
|
});
|
|
isSharing = false; // Reset flag
|
|
}
|
|
}
|
|
|
|
// double calculateAspectRatio(int crossAxisCount, List<dynamic> cardData) {
|
|
// if (cardData.any((item) =>
|
|
// item['chart_type'] == 'total' || item['chart_type'] == 'average')) {
|
|
// if (crossAxisCount == 1) {
|
|
// return 0.9 ; // Aspect ratio when crossAxisCount is less than 2
|
|
// } else if (crossAxisCount == 2) {
|
|
// return 1.0; // Aspect ratio when crossAxisCount is exactly 2
|
|
// } else if (crossAxisCount > 2) {
|
|
// return 1.0; // Aspect ratio when crossAxisCount is greater than 2
|
|
// }
|
|
// } else {
|
|
// if (crossAxisCount < 2) {
|
|
// return 0.8; // Aspect ratio when crossAxisCount is less than 2
|
|
// } else if (crossAxisCount == 2) {
|
|
// return 1.5; // Aspect ratio when crossAxisCount is exactly 2
|
|
// } else if (crossAxisCount > 2) {
|
|
// return 0.9; // Aspect ratio when crossAxisCount is greater than 2
|
|
// }
|
|
// }
|
|
// return 1.0; // Default fallback (should never be reached)
|
|
// }
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final locale = ref.watch(localeProvider);
|
|
final localeNotifier = ref.read(localeProvider.notifier);
|
|
String url=GoRouterState.of(context).uri.toString();
|
|
final uri = Uri.parse(url);
|
|
final pageTitle = uri.queryParameters['key'] ?? '';
|
|
|
|
final isConnected = ref.watch(connectivityProvider);
|
|
|
|
// If no internet, redirect to InternetCheckScreen
|
|
ref.listen<AsyncValue<bool>>(connectivityProvider, (previous, hasInternet) {
|
|
if (hasInternet.value == false) {
|
|
context.push('/internetcheck');
|
|
}
|
|
});
|
|
|
|
ref.listen<Locale?>(localeProvider, (previous, next) async {
|
|
final localeCode = next?.languageCode ?? 'en';
|
|
// Default to 'en' if null
|
|
setState(() {
|
|
isLoading = true;
|
|
print('isLoadingref $isLoading');
|
|
});
|
|
await _userService.updateLanguage(localeCode);
|
|
|
|
// fetchChartData(widget.dataSets, localeCode);
|
|
print('Saving currentTab: $tabWiseKpi before locale change');
|
|
print('localeCodeWgt- $localeCode');
|
|
currentTab = tabWiseKpi;
|
|
print('Saving currentTab: $currentTab After locale change');
|
|
|
|
final List<Map<String, dynamic>> safeFilterData =
|
|
widget.filter_data ?? [];
|
|
final String safeKpi = widget.kpi ?? '';
|
|
fetchChartData(widget.dataSets, localeCode, safeKpi, safeFilterData)
|
|
.then((_) {
|
|
print('DEBUG KPI: $safeKpi');
|
|
|
|
print('TAB11: $currentTab');
|
|
|
|
if (currentTab.isNotEmpty) {
|
|
// Call onTabSelected for the first tab
|
|
onTabSelected(currentTab);
|
|
print('TAB1: $currentTab');
|
|
} else if (_tabsData.isNotEmpty) {
|
|
// Call onTabSelected for the first tab
|
|
print('TAB2: $_tabsData');
|
|
onTabSelected(_tabsData[0]['id']);
|
|
}
|
|
});
|
|
});
|
|
double myheight = MediaQuery.of(context).size.height;
|
|
double mywidth = MediaQuery.of(context).size.width;
|
|
mainTopic = chartScreenData['main_topic'] ?? '';
|
|
|
|
int crossAxisCount =
|
|
cardData.isNotEmpty ? (cardData.length / 2).ceil().clamp(1, 2) : 1;
|
|
|
|
final color =
|
|
Color(int.parse(widget.bgColor.replaceFirst('0x', ''), radix: 16));
|
|
// Color bodyColor = Color(
|
|
// int.parse(
|
|
// (chartScreenData['body_color'] ?? '#898C81')
|
|
// .replaceFirst('#', '0xFF'), // Correct format for ARGB in Flutter
|
|
// radix: 16,
|
|
// ),
|
|
// );
|
|
//
|
|
//
|
|
final bodyColor = Color(
|
|
int.parse(
|
|
(chartScreenData['body_color'] ?? '#898C81').replaceFirst('#', '0xff'),
|
|
),
|
|
);
|
|
final chartHeader = chartScreenData['main_topic'] ?? '';
|
|
|
|
print('ChartScrnBodyColor');
|
|
print('chartHeader - $chartHeader');
|
|
print(chartScreenData['body_color']);
|
|
print(
|
|
Color(int.parse((chartScreenData['body_color'] ?? '#898C81')
|
|
.replaceFirst('#', '0xff'))),
|
|
);
|
|
|
|
return PopScope(
|
|
canPop: false, // Allow back navigation only if not login screen
|
|
onPopInvokedWithResult: (didPop, result) {
|
|
if (didPop) return;
|
|
context.pop();
|
|
// context.go('/uaenumbers'); // Show exit confirmation dialog
|
|
},
|
|
|
|
child: BaseScaffold(
|
|
key: _scaffoldKey,
|
|
title: (pageTitle == 'home')
|
|
? Text(AppLocalizations.of(context)!.nav_home,
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
)
|
|
),)
|
|
: Text(
|
|
context.translate(
|
|
'UAE Numbers',
|
|
'أرقام الإمارات',
|
|
),
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
)
|
|
),
|
|
),
|
|
// appbarColor: Color(int.parse(widget.bgColor)), // Example color
|
|
appbarColor: Color(int.parse(
|
|
(chartScreenData['header_color'] ?? '#ffffff')
|
|
.replaceFirst('#', '0xff'))),
|
|
// Example color
|
|
showBackButton: true,
|
|
colorChange: true,
|
|
navBackArrow: Text(widget.keyParam ?? 'Default Value'),
|
|
body: Stack(children: [
|
|
if (!isLoading)
|
|
Container(
|
|
color: color,
|
|
child: Column(
|
|
children: [
|
|
Container(
|
|
// height: myheight / 5,
|
|
width: double.infinity,
|
|
// color: color,
|
|
color: Color(int.parse(
|
|
(chartScreenData['header_color'] ?? '#898C81')
|
|
.replaceFirst('#', '0xff'))),
|
|
child: Padding(
|
|
padding: EdgeInsets.only(
|
|
left: 30, right: 30, top: 8, bottom: 5),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
// widget.mainTopic ?? '',
|
|
// chartScreenData['main_topic'] ?? '',
|
|
mainTopic,
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 28,
|
|
fontWeight: FontWeight.w600,
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
),
|
|
),
|
|
Container(
|
|
width: mywidth / 7,
|
|
height: 1.5,
|
|
child: Divider(
|
|
thickness: 4,
|
|
color: Colors.white,
|
|
)),
|
|
Text(
|
|
// widget.title,
|
|
chartScreenData['data_set_tile_heading'] ?? '',
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.w500,
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
)),
|
|
Container(
|
|
color: Color(int.parse(
|
|
(chartScreenData['header_color'] ?? '#898C81')
|
|
.replaceFirst('#', '0xff'))),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.end,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
children: [
|
|
Row(
|
|
key: bookMarkKey,
|
|
children: [
|
|
GestureDetector(
|
|
onTap: () {
|
|
if (isBookmarked) {
|
|
showRemoveBookmarkDialog();
|
|
} else {
|
|
showAddBookmarkDialog();
|
|
}
|
|
},
|
|
child: Container(
|
|
padding: EdgeInsets.all(4),
|
|
decoration: BoxDecoration(
|
|
color: isBookmarked
|
|
? Colors.white.withAlpha(90)
|
|
: null, // Background color
|
|
borderRadius: BorderRadius.circular(
|
|
8), // Curved corners
|
|
),
|
|
child: Row(
|
|
children: [
|
|
isBookmarked
|
|
? Text(
|
|
context.translate(
|
|
'Bookmarked',
|
|
'إشارة مرجعية',
|
|
),
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
fontSize: 16,
|
|
color: Color(int.parse(
|
|
(chartScreenData[
|
|
'body_color'] ??
|
|
'#898C81')
|
|
.replaceFirst(
|
|
'#', '0xff'))),
|
|
),
|
|
)
|
|
: Text(
|
|
context.translate(
|
|
'Bookmark',
|
|
'إشارة مرجعية',
|
|
),
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
fontSize: 16,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
SizedBox(width: 5),
|
|
|
|
// Text(
|
|
// "Bookmark",
|
|
// style: const TextStyle(
|
|
// fontSize: 16,
|
|
// color: Colors.white),
|
|
// ),
|
|
// const SizedBox(width: 5),
|
|
// Image.asset(
|
|
// isBookmarked
|
|
// ? UaeNumbersAssetPath
|
|
// .bookmarksSelectedUae // Filled bookmark image
|
|
// : UaeNumbersAssetPath
|
|
// .bookmarksUae, // Default bookmark image
|
|
// // color: Colors.white,
|
|
// width: 24,
|
|
// height: 24,
|
|
// ),
|
|
Icon(
|
|
size: 24,
|
|
Icons.bookmarks_rounded,
|
|
color: isBookmarked
|
|
? Color(int.parse(
|
|
(chartScreenData[
|
|
'body_color'] ??
|
|
'#898C81')
|
|
.replaceFirst(
|
|
'#', '0xff')))
|
|
: Colors.white,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
SizedBox(width: 10),
|
|
Row(
|
|
children: [
|
|
GestureDetector(
|
|
onTap: () =>
|
|
shareCurrentPage(context, _isSharing),
|
|
child: Row(
|
|
children: [
|
|
Text(
|
|
context.translate('Share', 'يشارك'),
|
|
style:TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
fontSize: 16,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
SizedBox(width: 5),
|
|
_isSharing
|
|
? SizedBox(
|
|
width: 24,
|
|
height: 24,
|
|
child:
|
|
CircularProgressIndicator(
|
|
color: Colors.white,
|
|
strokeWidth: 2,
|
|
),
|
|
) // Show loading spinner
|
|
: SvgPicture.asset(
|
|
UaeNumbersAssetPath.share,
|
|
semanticsLabel: 'share',
|
|
width: 24,
|
|
height: 24,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
SizedBox(
|
|
width: 10), // Horizontal space between items
|
|
Row(
|
|
children: [
|
|
GestureDetector(
|
|
onTap: () {
|
|
showRightSideModal(
|
|
context, filterData, chartsData);
|
|
},
|
|
child: Row(
|
|
children: [
|
|
Text(
|
|
context.translate(
|
|
'Filter',
|
|
'فلتر',
|
|
),
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
fontSize: 16,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
SizedBox(width: 10),
|
|
SvgPicture.asset(
|
|
UaeNumbersAssetPath.filterCharts,
|
|
semanticsLabel: 'Filter',
|
|
width: 24,
|
|
height: 24,
|
|
),
|
|
],
|
|
),
|
|
)
|
|
// IconButton(
|
|
// icon: Icon(Icons.filter_alt_outlined,
|
|
// color: Colors.white, size: 24),
|
|
// onPressed: () {
|
|
// showRightSideModal(
|
|
// context, filterData, chartsData);
|
|
// },
|
|
// ),
|
|
],
|
|
),
|
|
SizedBox(width: 10),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Expanded(
|
|
child: Container(
|
|
color: Color(int.parse(
|
|
(chartScreenData['body_color'] ?? '#898C81')
|
|
.replaceFirst('#', '0xff'))),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(10.0),
|
|
child: ListView(
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
// Left arrow button
|
|
if (_tabsData.length > 1)
|
|
_buildArrowButton(
|
|
onPressed: _activeTabIndex > 0
|
|
? _scrollLeft
|
|
: null,
|
|
icon: Icons.arrow_back_ios_new,
|
|
),
|
|
// Tabs with horizontal scroll
|
|
if (_tabsData.length > 1)
|
|
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
|
|
if (_tabsData.length > 1)
|
|
_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
|
|
Padding(
|
|
padding: const EdgeInsets.only(
|
|
top: 8.0, left: 1.0, right: 1.0),
|
|
child: LayoutBuilder(
|
|
key: cardKey,
|
|
builder: (context, constraints) {
|
|
List<Widget> rows = [];
|
|
List<Widget> tempRow = [];
|
|
|
|
for (var card in cardData) {
|
|
if (card['card_full_length'] == true) {
|
|
if (tempRow.isNotEmpty) {
|
|
rows.add(Row(children: tempRow));
|
|
tempRow = [];
|
|
}
|
|
rows.add(Row(
|
|
children: [
|
|
Expanded(
|
|
child: CardWidget(
|
|
card: card,
|
|
chartScreenData:
|
|
chartScreenData)),
|
|
],
|
|
));
|
|
} else {
|
|
tempRow.add(Expanded(
|
|
child: CardWidget(
|
|
card: card,
|
|
chartScreenData:
|
|
chartScreenData)));
|
|
if (tempRow.length == 2) {
|
|
rows.add(Row(children: tempRow));
|
|
tempRow = [];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (tempRow.isNotEmpty) {
|
|
rows.add(Row(children: tempRow));
|
|
}
|
|
|
|
return SingleChildScrollView(
|
|
child: Column(
|
|
children: rows,
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
ListView.builder(
|
|
itemCount: chartsData.length,
|
|
shrinkWrap: true,
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
itemBuilder: (context, index) {
|
|
return Card(
|
|
margin: const EdgeInsets.all(10),
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
border: Border.all(
|
|
color: Color(
|
|
int.parse(
|
|
(chartScreenData[
|
|
'border_color'] ??
|
|
'#898C81')
|
|
.replaceFirst('#', '0xff'),
|
|
),
|
|
), // Border color
|
|
width: 1, // Border width
|
|
),
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(
|
|
8), // Optional: Rounded corners
|
|
),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Column(
|
|
crossAxisAlignment:
|
|
CrossAxisAlignment.start,
|
|
children: [
|
|
// ConstrainedBox
|
|
Container(
|
|
height: (chartsData[index][
|
|
'chart_height'] ==
|
|
null ||
|
|
chartsData[index][
|
|
'chart_height'] ==
|
|
0)
|
|
? 350
|
|
: double.tryParse(
|
|
chartsData[index]
|
|
['chart_height']
|
|
.toString()),
|
|
// height: (chartsData[index]
|
|
// ['chart_type'] ==
|
|
// 'pie_chart')
|
|
// ? 500
|
|
// : (chartsData[index][
|
|
// 'chart_type'] ==
|
|
// 'bar_chart_horizontal')
|
|
// ? 370
|
|
// : (chartsData[index][
|
|
// 'chart_type'] ==
|
|
// 'horizontal_rotate')
|
|
// ? 385
|
|
// : (chartsData[index]
|
|
// [
|
|
// 'chart_type'] ==
|
|
// 'fl_stacked_bar')
|
|
// ? 390
|
|
// : 350,
|
|
|
|
// child: buildChart(chartsData[index]),
|
|
child: ChartWidget(
|
|
chartData:
|
|
chartsData[index],
|
|
bodyColor: bodyColor,
|
|
chartHeader: chartHeader),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
));
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
if (isLoading)
|
|
Container(
|
|
color: Color(0x98FFFCE5), // Semi-transparent background
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Container(
|
|
margin: EdgeInsets.symmetric(
|
|
horizontal: 40), // Left & Right space
|
|
child: LinearProgressIndicator(
|
|
minHeight: 5, // Adjust thickness
|
|
backgroundColor:
|
|
Colors.grey[100], // Optional: Background color
|
|
valueColor: AlwaysStoppedAnimation<Color>(
|
|
Color(0xFFAA8E83)), // Loader color
|
|
),
|
|
),
|
|
],
|
|
),
|
|
)
|
|
])),
|
|
);
|
|
}
|
|
|
|
Widget _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: 30.0, // Set the width of the circle
|
|
height: 30.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<String, String> 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(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
color: isActive ? Colors.black : Colors.white,
|
|
fontWeight: isActive ? FontWeight.w500 : FontWeight.w400,
|
|
fontSize: 13,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Function to convert hex color string to Color
|
|
Color _getColorFromHex(String hexColor) {
|
|
hexColor = hexColor.replaceFirst('#', '');
|
|
if (hexColor.length == 6) {
|
|
hexColor = 'FF$hexColor'; // Add alpha if not provided
|
|
}
|
|
return Color(int.parse(hexColor, radix: 16));
|
|
}
|
|
|
|
class CardWidget extends StatelessWidget {
|
|
final Map<String, dynamic> card;
|
|
final Map<String, dynamic> chartScreenData;
|
|
|
|
const CardWidget(
|
|
{Key? key, required this.card, required this.chartScreenData})
|
|
: super(key: key);
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final chart_type = card['chart_type'];
|
|
final chart_heading = card['chart_heading'];
|
|
final card_full_length = card['card_full_length'];
|
|
print('cardfulllength $card_full_length');
|
|
final response = card['response'] as List<dynamic>? ?? [];
|
|
final card_logo = card['card_logo'];
|
|
double cardHeight =
|
|
(chart_type == 'totals' || chart_type == 'averages') ? 180.0 : 130.0;
|
|
|
|
final bodyColor = Color(
|
|
int.parse(
|
|
(chartScreenData['border_color'] ?? '#898C81')
|
|
.replaceFirst('#', '0xff'),
|
|
),
|
|
);
|
|
|
|
if (response.isEmpty) {
|
|
return SizedBox.shrink();
|
|
} else if (response.length == 1) {
|
|
return Container(
|
|
margin: const EdgeInsets.all(10),
|
|
// width: card_full_length ? double.infinity : null,
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: Border.all(
|
|
color: Color(int.parse(
|
|
(chartScreenData['border_color'] ?? '#898C81')
|
|
.replaceFirst('#', '0xff'))),
|
|
// Dynamic border color
|
|
width: 1, // Adjust border thickness
|
|
),
|
|
),
|
|
child: Card(
|
|
margin: const EdgeInsets.all(0),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
elevation: 2,
|
|
child: Container(
|
|
// height: cardHeight,
|
|
height: 158 ,
|
|
padding: const EdgeInsets.only(
|
|
left: 16.0, right: 16.0, bottom: 2.0, top: 1.0),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white, // Move color inside BoxDecoration
|
|
borderRadius: BorderRadius.circular(
|
|
12), // Ensure border radius is applied
|
|
),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Image.network(
|
|
card_logo ?? '',
|
|
width: 30,
|
|
height: 30,
|
|
errorBuilder: (context, error, stackTrace) {
|
|
return Icon(Icons.public,
|
|
color: Color(0xFF90B0D5), size: 30); // Fallback icon
|
|
},
|
|
),
|
|
const SizedBox(height: 1),
|
|
MouseRegion(
|
|
onEnter: (event) {
|
|
// Show tooltip on hover
|
|
final overlay = Overlay.of(context)
|
|
.context
|
|
.findRenderObject() as RenderBox;
|
|
final entry = OverlayEntry(
|
|
builder: (context) => Positioned(
|
|
left: overlay.localToGlobal(Offset.zero).dx,
|
|
top: overlay.localToGlobal(Offset.zero).dy,
|
|
child: Material(
|
|
color: Colors.transparent,
|
|
child: Container(
|
|
padding: EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: Colors.black87,
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
child: Text(
|
|
chart_heading ?? 'NA',
|
|
style: TextStyle(color: Colors.white, fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
Overlay.of(context).insert(entry);
|
|
Future.delayed(
|
|
Duration(seconds: 2), () => entry.remove());
|
|
},
|
|
child: Text(
|
|
chart_heading ?? 'NA',
|
|
textAlign: TextAlign.center,
|
|
maxLines: 2,
|
|
overflow:
|
|
TextOverflow.ellipsis, // Truncate text with '...'
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.w400,
|
|
color: Colors.black87,
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 5),
|
|
Text(
|
|
RegExp(r'\d').hasMatch(response[0]['display_value'] ?? '')
|
|
? '(${response[0]['display_value'] ?? 'NA'})'
|
|
: '${response[0]['display_value'] ?? 'NA'}',
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
fontSize: 11,
|
|
color: Colors.grey,
|
|
overflow:
|
|
TextOverflow.ellipsis, // Truncate if text overflows
|
|
),
|
|
maxLines: 1, // Limit to one line to prevent overflow
|
|
textAlign: TextAlign.center, // Ensure proper alignment
|
|
),
|
|
const SizedBox(height: 1),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment
|
|
.center, // Center-aligns the row contents
|
|
children: [
|
|
Text(
|
|
response[0]['value'] ?? 'NA',
|
|
textAlign: TextAlign.center, // Ensures text is centered
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
fontSize: 23,
|
|
fontWeight: FontWeight.w800,
|
|
color: (response[0]['font_color'] ?? '').isEmpty
|
|
? bodyColor
|
|
: _getColorFromHex(response[0]['font_color']),
|
|
),
|
|
),
|
|
const SizedBox(width: 4), // Space between text and icon
|
|
if (response[0]['font_color'] != '') ...[
|
|
if (response[0]['font_color'] == '#D83731')
|
|
const Icon(Icons.arrow_downward,
|
|
color: Colors.red, size: 20)
|
|
else if (response[0]['font_color'] == '#11AF22')
|
|
const Icon(Icons.arrow_upward,
|
|
color: Colors.green, size: 20),
|
|
],
|
|
],
|
|
)
|
|
],
|
|
),
|
|
),
|
|
));
|
|
} else {
|
|
return Container(
|
|
margin: const EdgeInsets.all(10),
|
|
// width: card_full_length ? double.infinity : null,
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: Border.all(
|
|
color: Color(int.parse(
|
|
(chartScreenData['border_color'] ?? '#898C81')
|
|
.replaceFirst('#', '0xff'))), // Dynamic border color
|
|
width: 1, // Adjust border thickness
|
|
),
|
|
),
|
|
child: Card(
|
|
margin: const EdgeInsets.all(0),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
elevation: 0, // Remove shadow to keep only the outli
|
|
|
|
child: Container(
|
|
height: 158,
|
|
padding: const EdgeInsets.all(10.0),
|
|
// padding: const EdgeInsets.only(
|
|
// left: 10.0, right: 10.0, bottom: 5.0, top: 1.0),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white, // Move color inside BoxDecoration
|
|
borderRadius:
|
|
BorderRadius.circular(12), // Ensure border radius is applied
|
|
),
|
|
child: Column(
|
|
mainAxisSize:
|
|
MainAxisSize.min, // Adjust card height based on content
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
// const Icon(Icons.public,
|
|
// color: Color(0xFF90B0D5), size: 30),
|
|
Image.network(
|
|
card_logo ?? '',
|
|
width: 30,
|
|
height: 30,
|
|
errorBuilder: (context, error, stackTrace) {
|
|
return Icon(Icons.public,
|
|
color: Color(0xFF90B0D5), size: 30); // Fallback icon
|
|
},
|
|
),
|
|
const SizedBox(height: 3),
|
|
MouseRegion(
|
|
onEnter: (event) {
|
|
final overlay = Overlay.of(context);
|
|
final entry = OverlayEntry(
|
|
builder: (context) => Positioned(
|
|
left: event.position.dx,
|
|
top: event.position.dy + 10, // Adjust tooltip position
|
|
child: Material(
|
|
color: Colors.transparent,
|
|
child: Container(
|
|
padding: EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: Colors.black87,
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
child: Text(
|
|
chart_heading ?? 'NA',
|
|
style:
|
|
TextStyle(color: Colors.white, fontSize: 10, fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
overlay.insert(entry);
|
|
Future.delayed(Duration(seconds: 2), () => entry.remove());
|
|
},
|
|
child: SizedBox(
|
|
width: double.infinity, // Ensures it uses available space
|
|
child: Text(
|
|
chart_heading ?? 'NA',
|
|
textAlign: TextAlign.center,
|
|
maxLines: 2,
|
|
overflow: TextOverflow.ellipsis, // Truncate with '...'
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.w400,
|
|
color: Colors.black87,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 1),
|
|
Text(
|
|
// '(${response[0]['display_value'] ?? 'NA'})',
|
|
|
|
RegExp(r'\d').hasMatch(response[0]['display_value'] ?? '')
|
|
? '(${response[0]['display_value'] ?? 'NA'})'
|
|
: '${response[0]['display_value'] ?? 'NA'}',
|
|
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
fontSize: 11,
|
|
color: Colors.grey,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
const SizedBox(height: 1),
|
|
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment
|
|
.center, // Center-aligns the row contents
|
|
children: [
|
|
Text(
|
|
response[0]['value'] ?? 'NA',
|
|
overflow: TextOverflow.ellipsis,
|
|
maxLines: 1,
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
fontSize: 23,
|
|
fontWeight: FontWeight.w900,
|
|
color: (response[0]['font_color'] ?? '').isEmpty
|
|
? bodyColor
|
|
: _getColorFromHex(response[0]['font_color']),
|
|
),
|
|
),
|
|
const SizedBox(width: 1), // Space between text and icon
|
|
if (response[0]['font_color'] != '') ...[
|
|
if (response[0]['font_color'] == '#D83731')
|
|
const Icon(Icons.arrow_downward,
|
|
color: Colors.red, size: 20)
|
|
else if (response[0]['font_color'] == '#11AF22')
|
|
const Icon(Icons.arrow_upward,
|
|
color: Colors.green, size: 20),
|
|
],
|
|
],
|
|
),
|
|
|
|
SizedBox(
|
|
height: 2, // Height of the divider
|
|
width: 100,
|
|
child: Divider(
|
|
color: Color(0xFFBBBCBD),
|
|
thickness: 1, // Divider line thickness
|
|
),
|
|
),
|
|
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment
|
|
.center, // Center-aligns the row contents
|
|
children: [
|
|
Text(
|
|
response[1]['value'] ?? 'NA',
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w600,
|
|
color: (response[1]['font_color'] ?? '').isEmpty
|
|
? const Color(0xFFD83731)
|
|
: _getColorFromHex(response[1]['font_color']),
|
|
),
|
|
textAlign: TextAlign.center, // Ensure proper alignment
|
|
overflow:
|
|
TextOverflow.ellipsis, // Truncate if text overflows
|
|
),
|
|
const SizedBox(width: 1), // Space between text and icon
|
|
if (response[1]['font_color'] != '') ...[
|
|
if (response[1]['font_color'] == '#D83731')
|
|
const Icon(Icons.arrow_downward,
|
|
color: Colors.red, size: 20)
|
|
else if (response[1]['font_color'] == '#11AF22')
|
|
const Icon(Icons.arrow_upward,
|
|
color: Colors.green, size: 20),
|
|
],
|
|
],
|
|
),
|
|
|
|
Text(
|
|
// '(${response[1]['display_value'] ?? 'NA'})',
|
|
RegExp(r'\d').hasMatch(response[1]['display_value'] ?? '')
|
|
? '(${response[1]['display_value'] ?? 'NA'})'
|
|
: '${response[1]['display_value'] ?? 'NA'}',
|
|
style: TextStyle(
|
|
fontFamily: context.translate(
|
|
'Roboto',
|
|
'NotoKufi',
|
|
),
|
|
fontSize: 8,
|
|
fontWeight: FontWeight.w500,
|
|
color: Colors.grey,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
// return Card(
|
|
// elevation: 4,
|
|
// shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
|
// child: Padding(
|
|
// padding: const EdgeInsets.all(16.0),
|
|
// child: Column(
|
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
|
// children: [
|
|
// Image.network(
|
|
// card['card_logo'],
|
|
// height: 50,
|
|
// width: 50,
|
|
// fit: BoxFit.contain,
|
|
// ),
|
|
// SizedBox(height: 10),
|
|
// Text(
|
|
// card['chart_heading'] ?? '',
|
|
// style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
|
// ),
|
|
// SizedBox(height: 10),
|
|
// Column(
|
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
|
// children: List.generate(card['response'].length, (index) {
|
|
// var response = card['response'][index];
|
|
// return Text(
|
|
// "${response['display_value']}: ${response['value']}",
|
|
// style: TextStyle(fontSize: 14),
|
|
// );
|
|
// }),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// ),
|
|
// );
|
|
}
|
|
}
|