From 4b935f1dc5c547eaad2e751f8aebec50aa8e42e5 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Tue, 13 Sep 2022 10:33:40 +0300 Subject: [PATCH] leave balance dashboard added. --- lib/api/dashboard_api_client.dart | 7 +- lib/classes/utils.dart | 32 ++- lib/models/generic_response_model.dart | 2 +- lib/provider/dashboard_provider_model.dart | 14 +- lib/ui/landing/dashboard_screen.dart | 2 +- .../leave_balance/leave_balance_screen.dart | 218 +++++++++++++++--- 6 files changed, 237 insertions(+), 38 deletions(-) diff --git a/lib/api/dashboard_api_client.dart b/lib/api/dashboard_api_client.dart index 5b00e7f..214ea05 100644 --- a/lib/api/dashboard_api_client.dart +++ b/lib/api/dashboard_api_client.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:mohem_flutter_app/api/api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/consts.dart'; +import 'package:mohem_flutter_app/models/dashboard/get_accrual_balances_list_model.dart'; import 'package:mohem_flutter_app/models/dashboard/get_attendance_tracking_list_model.dart'; import 'package:mohem_flutter_app/models/dashboard/itg_forms_model.dart'; import 'package:mohem_flutter_app/models/dashboard/list_menu.dart'; @@ -46,13 +47,13 @@ class DashboardApiClient { }, url, postParams); } - Future getAccrualBalances() async { + Future> getAccrualBalances(String effectiveDate) async { String url = "${ApiConsts.erpRest}GET_ACCRUAL_BALANCES"; - Map postParams = {"P_EFFECTIVE_DATE": "1/30/2022"}; + Map postParams = {"P_EFFECTIVE_DATE": effectiveDate}; postParams.addAll(AppState().postParamsJson); return await ApiClient().postJsonForObject((json) { GenericResponseModel responseData = GenericResponseModel.fromJson(json); - return responseData; + return responseData.getAccrualBalancesList ?? []; }, url, postParams); } diff --git a/lib/classes/utils.dart b/lib/classes/utils.dart index 7977598..f77f998 100644 --- a/lib/classes/utils.dart +++ b/lib/classes/utils.dart @@ -1,7 +1,9 @@ import 'dart:convert'; +import 'dart:io'; import 'dart:typed_data'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:fluttertoast/fluttertoast.dart'; @@ -184,7 +186,7 @@ class Utils { border: Border.all( width: 1, // color: background // <--- border width here - ), + ), borderRadius: BorderRadius.circular(radius), ); } @@ -279,4 +281,32 @@ class Utils { } return formattedDate; } + + static Future selectDate(BuildContext context, DateTime selectedDate) async { + if (!Platform.isIOS) { + await showCupertinoModalPopup( + context: context, + builder: (cxt) => Container( + height: 250, + color: Colors.white, + child: CupertinoDatePicker( + backgroundColor: Colors.white, + mode: CupertinoDatePickerMode.date, + onDateTimeChanged: (value) { + if (value != null && value != selectedDate) { + selectedDate = value; + } + }, + initialDateTime: selectedDate, + ), + ), + ); + } else { + DateTime? picked = await showDatePicker(context: context, initialDate: selectedDate, initialEntryMode: DatePickerEntryMode.calendarOnly, firstDate: DateTime(2015, 8), lastDate: DateTime(2101)); + if (picked != null && picked != selectedDate) { + selectedDate = picked; + } + } + return selectedDate; + } } diff --git a/lib/models/generic_response_model.dart b/lib/models/generic_response_model.dart index ccc53c1..c6c605e 100644 --- a/lib/models/generic_response_model.dart +++ b/lib/models/generic_response_model.dart @@ -1167,7 +1167,7 @@ class GenericResponseModel { listItemImagesDetails = json['List_ItemImagesDetails']; listItemMaster = json['List_ItemMaster']; listMedicineDetails = json['List_MedicineDetails']; - listMenu = json["List_Menu"] == null ? null : List.from(json["List_Menu"].map((x) => ListMenu.fromJson(x))); + listMenu = json["List_Menu"] == null ? [] : List.from(json["List_Menu"].map((x) => ListMenu.fromJson(x))); listNewEmployees = json['List_NewEmployees']; listRadScreen = json['List_RadScreen']; logInTokenID = json['LogInTokenID']; diff --git a/lib/provider/dashboard_provider_model.dart b/lib/provider/dashboard_provider_model.dart index a98d091..17f90d2 100644 --- a/lib/provider/dashboard_provider_model.dart +++ b/lib/provider/dashboard_provider_model.dart @@ -8,6 +8,7 @@ import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/models/dashboard/drawer_menu_item_model.dart'; +import 'package:mohem_flutter_app/models/dashboard/get_accrual_balances_list_model.dart'; import 'package:mohem_flutter_app/models/dashboard/get_attendance_tracking_list_model.dart'; import 'package:mohem_flutter_app/models/dashboard/get_open_notifications_list.dart'; import 'package:mohem_flutter_app/models/dashboard/itg_forms_model.dart'; @@ -36,7 +37,10 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { //Leave and Ticket Balance bool isLeaveTicketBalanceLoading = true; - double leaveBalance = 0; + List? accrualList; + GetAccrualBalancesList? leaveBalanceAccrual; + + double get leaveBalance => leaveBalanceAccrual?.accrualNetEntitlement ?? 0; double ticketBalance = 0; //Menu Entries @@ -129,12 +133,12 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } //Leave and Ticket Balance API's & Methods - Future fetchLeaveTicketBalance(context) async { + Future fetchLeaveTicketBalance(context, DateTime date) async { try { - GenericResponseModel? genericResponseModel = await DashboardApiClient().getAccrualBalances(); + accrualList = await DashboardApiClient().getAccrualBalances(DateFormat("MM/dd/yyyy").format(date)); isLeaveTicketBalanceLoading = false; - leaveBalance = genericResponseModel?.getAccrualBalancesList![0].accrualNetEntitlement ?? 0.0; - ticketBalance = (genericResponseModel?.getAccrualBalancesList![1].accrualNetEntitlement ?? 0.0) + (genericResponseModel?.getAccrualBalancesList![2].accrualNetEntitlement ?? 0.0); + leaveBalanceAccrual = accrualList![0]; + ticketBalance = (accrualList![1].accrualNetEntitlement ?? 0.0) + (accrualList![2].accrualNetEntitlement ?? 0.0); notifyListeners(); } catch (ex) { isLeaveTicketBalanceLoading = false; diff --git a/lib/ui/landing/dashboard_screen.dart b/lib/ui/landing/dashboard_screen.dart index 3f4e5f2..6bc7d39 100644 --- a/lib/ui/landing/dashboard_screen.dart +++ b/lib/ui/landing/dashboard_screen.dart @@ -47,7 +47,7 @@ class _DashboardScreenState extends State { data.fetchAttendanceTracking(context); data.fetchWorkListCounter(context); data.fetchMissingSwipe(context); - data.fetchLeaveTicketBalance(context); + data.fetchLeaveTicketBalance(context, DateTime.now()); data.fetchMenuEntries(); data.getCategoryOffersListAPI(context); } diff --git a/lib/ui/leave_balance/leave_balance_screen.dart b/lib/ui/leave_balance/leave_balance_screen.dart index 52d2613..4306e36 100644 --- a/lib/ui/leave_balance/leave_balance_screen.dart +++ b/lib/ui/leave_balance/leave_balance_screen.dart @@ -1,15 +1,23 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/api/dashboard_api_client.dart'; import 'package:mohem_flutter_app/api/leave_balance_api_client.dart'; +import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/date_uitl.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/models/dashboard/get_accrual_balances_list_model.dart'; import 'package:mohem_flutter_app/models/leave_balance/get_absence_transaction_list_model.dart'; +import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; import 'package:mohem_flutter_app/widgets/item_detail_view_widget.dart'; +import 'package:pie_chart/pie_chart.dart'; +import 'package:provider/provider.dart'; class LeaveBalance extends StatefulWidget { LeaveBalance({Key? key}) : super(key: key); @@ -23,9 +31,16 @@ class LeaveBalance extends StatefulWidget { class _LeaveBalanceState extends State { List? absenceTransList; + List chartModelList = []; + + GetAccrualBalancesList? leaveBalanceAccrual; + + DateTime accrualDateTime = DateTime.now(); + @override void initState() { super.initState(); + getAbsenceTransactions(); } @@ -36,10 +51,29 @@ class _LeaveBalanceState extends State { void getAbsenceTransactions() async { try { - Utils.showLoading(context); - absenceTransList = await LeaveBalanceApiClient().getAbsenceTransactions(-999); - Utils.hideLoading(context); - setState(() {}); + Utils.showLoading(context); + absenceTransList = await LeaveBalanceApiClient().getAbsenceTransactions(-999); + Utils.hideLoading(context); + setState(() {}); + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + } + + void changeAccrualDate() async { + try { + Utils.showLoading(context); + List accrualList = await DashboardApiClient().getAccrualBalances(DateFormat("MM/dd/yyyy").format(accrualDateTime)); + if (accrualList.isNotEmpty) { + leaveBalanceAccrual = accrualList[0]; + chartModelList = [ + PieChartModel("Current Balance", leaveBalanceAccrual?.accrualNetEntitlement ?? 0, MyColors.textMixColor, titleAppend: ""), + PieChartModel("Used", leaveBalanceAccrual?.accrualUsedEntitlement?.toDouble() ?? 0, MyColors.backgroundBlackColor, titleAppend: ""), + ]; + } + Utils.hideLoading(context); + setState(() {}); } catch (ex) { Utils.hideLoading(context); Utils.handleException(ex, context, null); @@ -48,35 +82,90 @@ class _LeaveBalanceState extends State { @override Widget build(BuildContext context) { + if (leaveBalanceAccrual == null) { + leaveBalanceAccrual = Provider.of(context, listen: false).leaveBalanceAccrual; + + chartModelList = [ + PieChartModel("Current Balance", leaveBalanceAccrual?.accrualNetEntitlement ?? 0, MyColors.textMixColor, titleAppend: ""), + PieChartModel("Used", leaveBalanceAccrual?.accrualUsedEntitlement?.toDouble() ?? 0, MyColors.backgroundBlackColor, titleAppend: ""), + ]; + } return Scaffold( backgroundColor: Colors.white, appBar: AppBarWidget( context, title: LocaleKeys.leaveBalance.tr(), ), - body: absenceTransList == null - ? const SizedBox() - : (absenceTransList!.isEmpty - ? Utils.getNoDataWidget(context) - : ListView.separated( - physics: const BouncingScrollPhysics(), - padding: const EdgeInsets.all(21), - itemBuilder: (cxt, int index) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - ItemDetailView(LocaleKeys.startDateT.tr(), absenceTransList![index].sTARTDATE ?? ""), - ItemDetailView(LocaleKeys.endDateT.tr(), absenceTransList![index].eNDDATE ?? ""), - ItemDetailView(LocaleKeys.absenceType.tr(), absenceTransList![index].aBSENCETYPE ?? ""), - ItemDetailView(LocaleKeys.absenceCategory.tr(), absenceTransList![index].aBSENCECATEGORY ?? ""), - ItemDetailView(LocaleKeys.days.tr(), absenceTransList![index].aBSENCEDAYS?.toString() ?? ""), - ItemDetailView(LocaleKeys.hours.tr(), absenceTransList![index].aBSENCEHOURS?.toString() ?? ""), - ItemDetailView(LocaleKeys.approvalStatus.tr(), absenceTransList![index].aPPROVALSTATUS ?? ""), - ItemDetailView(LocaleKeys.absenceStatus.tr(), absenceTransList![index].aBSENCESTATUS ?? ""), - ], - ).objectContainerView(), - separatorBuilder: (cxt, index) => 12.height, - itemCount: absenceTransList!.length)), + body: ListView( + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.all(21), + children: [ + Column( + children: [ + Row( + children: [ + "Current Leave Balance".toText20().expanded, + Row( + children: [ + const Icon(Icons.calendar_month_rounded, color: MyColors.darkIconColor, size: 16), + 5.width, + DateUtil.formatDateToDate(accrualDateTime, AppState().isArabic(context)).toText13(isUnderLine: true), + 8.width + ], + ).onPress(() async { + DateTime selectedDate = await Utils.selectDate(context, accrualDateTime); + if (selectedDate != accrualDateTime) { + accrualDateTime = selectedDate; + changeAccrualDate(); + } + }), + ], + ), + 8.height, + AspectRatio( + aspectRatio: 302 / 122, + child: Row( + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + chartLegend(chartModelList[0], isFirst: true), + chartLegend(chartModelList[1]), + ], + ).paddingOnly(top: 8, bottom: 8).expanded, + getChart() + ], + ), + ) + ], + ).paddingOnly(top: 19, bottom: 11, right: 6, left: 14).objectContainerView(disablePadding: true, radius: 10), + 12.height, + absenceTransList == null + ? const SizedBox() + : (absenceTransList!.isEmpty + ? Utils.getNoDataWidget(context).paddingOnly(top: 50) + : ListView.separated( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + padding: EdgeInsets.zero, + itemBuilder: (cxt, int index) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + ItemDetailView(LocaleKeys.startDateT.tr(), absenceTransList![index].sTARTDATE ?? ""), + ItemDetailView(LocaleKeys.endDateT.tr(), absenceTransList![index].eNDDATE ?? ""), + ItemDetailView(LocaleKeys.absenceType.tr(), absenceTransList![index].aBSENCETYPE ?? ""), + ItemDetailView(LocaleKeys.absenceCategory.tr(), absenceTransList![index].aBSENCECATEGORY ?? ""), + ItemDetailView(LocaleKeys.days.tr(), absenceTransList![index].aBSENCEDAYS?.toString() ?? ""), + ItemDetailView(LocaleKeys.hours.tr(), absenceTransList![index].aBSENCEHOURS?.toString() ?? ""), + ItemDetailView(LocaleKeys.approvalStatus.tr(), absenceTransList![index].aPPROVALSTATUS ?? ""), + ItemDetailView(LocaleKeys.absenceStatus.tr(), absenceTransList![index].aBSENCESTATUS ?? ""), + ], + ).objectContainerView(), + separatorBuilder: (cxt, index) => 12.height, + itemCount: absenceTransList!.length)), + ], + ), floatingActionButton: Container( height: 54, width: 54, @@ -93,4 +182,79 @@ class _LeaveBalanceState extends State { }), ); } + + Widget getChart() { + List _colorList = chartModelList.map((e) => e.color).toList(); + return PieChart( + dataMap: {for (var e in chartModelList) e.title: e.parsedValue}, + animationDuration: const Duration(milliseconds: 800), + chartRadius: MediaQuery.of(context).size.width / 3.2, + colorList: _colorList, + emptyColor: MyColors.lightGreyEAColor, + initialAngleInDegree: 270, + legendOptions: const LegendOptions( + showLegendsInRow: false, + showLegends: false, + ), + chartValuesOptions: const ChartValuesOptions( + showChartValueBackground: false, + showChartValues: true, + showChartValuesInPercentage: true, + showChartValuesOutside: false, + decimalPlaces: 0, + chartValueStyle: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 10, + letterSpacing: -0.64, + color: MyColors.white, + ), + ), + ); + } + + Widget chartLegend(PieChartModel chartModel, {bool isFirst = false}) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 12, + height: 12, + decoration: BoxDecoration(color: chartModel.color, shape: BoxShape.circle), + ).paddingOnly(top: 2), + 9.width, + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + RichText( + text: TextSpan( + text: chartModel.title, + style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: isFirst ? MyColors.textMixColor : MyColors.backgroundBlackColor), + children: [ + TextSpan( + text: " " + chartModel.titleAppend, + style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: isFirst ? MyColors.textMixColor : MyColors.backgroundBlackColor), + ), + ], + ), + ), + chartModel.value.toString().toText14(), + ], + ).expanded, + ], + ); + } +} + +class PieChartModel { + String title; + String titleAppend; + double value; + Color color; + + PieChartModel(this.title, this.value, this.color, {this.titleAppend = ""}); + + Map get chartMapValue => {title: value}; + + double get parsedValue => value > 0 ? value : value + 0.1; }