import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/src/public_ext.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/painting.dart'; import 'package:intl/intl.dart'; import 'package:mohem_flutter_app/api/monthly_attendance_api_client.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/utils.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/get_day_hours_type_details_list_model.dart'; import 'package:mohem_flutter_app/models/get_schedule_shifts_details_list_model.dart'; import 'package:mohem_flutter_app/models/get_time_card_summary_list_model.dart'; import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; import 'package:mohem_flutter_app/widgets/circular_step_progress_bar.dart'; import 'package:month_picker_dialog/month_picker_dialog.dart'; import 'package:pie_chart/pie_chart.dart'; import 'package:syncfusion_flutter_calendar/calendar.dart'; class MonthlyAttendanceScreen extends StatefulWidget { MonthlyAttendanceScreen({Key? key}) : super(key: key); @override _MonthlyAttendanceScreenState createState() { return _MonthlyAttendanceScreenState(); } } class _MonthlyAttendanceScreenState extends State { bool isPresent = false; bool isAbsent = false; bool isMissing = false; bool isOff = false; DateTime date = DateTime.now(); late DateTime formattedDate; var currentMonth = DateTime.now().month; String searchMonth = getMonth(DateTime.now().month); int searchYear = DateTime.now().year; int? pRTPID; List getDayHoursTypeDetailsList = []; GetTimeCardSummaryList? getTimeCardSummaryList; GetScheduleShiftsDetailsList? getScheduleShiftsDetailsList; @override void initState() { super.initState(); formattedDate = date; callTimeCardAndHourDetails(date.day, searchMonth, searchYear); } void callTimeCardAndHourDetails(index, searchMonth, searchYear) async { try { Utils.showLoading(context); getTimeCardSummaryList = await MonthlyAttendanceApiClient().getTimeCardSummary(searchMonth, searchYear); getDayHoursTypeDetailsList = await MonthlyAttendanceApiClient().getDayHoursTypeDetails(searchMonth, searchYear); Utils.hideLoading(context); _calendarController.displayDate = formattedDate; setState(() {}); } catch (ex) { Utils.hideLoading(context); Utils.handleException(ex, context, null); } } Future getScheduleShiftsDetails(index, pRTPID) async { try { Utils.showLoading(context); getScheduleShiftsDetailsList = await MonthlyAttendanceApiClient().getScheduleShiftsDetails(pRTPID); Utils.hideLoading(context); //setState(() {}); } catch (ex) { Utils.hideLoading(context); Utils.handleException(ex, context, null); } } final CalendarController _calendarController = CalendarController(); final List _colorList = [Color(0xff2AB2AB), Color(0xff202529)]; @override Widget build(BuildContext context) { Map dataMap = { "Present": getTimeCardSummaryList?.aTTENDEDDAYS?.toDouble() ?? 0, "Absent": getTimeCardSummaryList?.aBSENTDAYS?.toDouble() ?? 0, }; return Scaffold( backgroundColor: Colors.white, appBar: AppBarWidget( context, // title: LocaleKeys.mowadhafhiRequest.tr(), title: "", // showHomeButton: true, ), body: ListView( scrollDirection: Axis.vertical, children: [ Column( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.attendance.tr().toText24(isBold: true, color: MyColors.grey3AColor), Row( children: [ "${DateFormat("MMMM-yyyy").format(formattedDate)}".toText16(color: MyColors.greyACColor), const Icon(Icons.keyboard_arrow_down_rounded, color: MyColors.greyACColor), ], ).onPress(() async { showMonthPicker( context: context, //locale: EasyLocalization.of(context)?.locale, initialDate: formattedDate, firstDate: DateTime(searchYear - 2), lastDate: DateTime.now(), ).then((selectedDate) { if (selectedDate != null) { searchMonth = getMonth(selectedDate.month); searchYear = selectedDate.year; formattedDate = selectedDate; //DateFormat('MMMM-yyyy').format(selectedDate); callTimeCardAndHourDetails(selectedDate.day, searchMonth, searchYear); } }); }), 18.height, AspectRatio(aspectRatio: 304 / 244, child: calendarWidget()), ], ).paddingOnly(left: 21, right: 21, top: 21), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ optionUI(LocaleKeys.scheduleDays.tr(), "${getTimeCardSummaryList?.sCHEDULEDAYS != null ? getTimeCardSummaryList!.sCHEDULEDAYS : 0}"), 6.width, optionUI(LocaleKeys.offDays.tr(), "${getTimeCardSummaryList?.oFFDAYS != null ? getTimeCardSummaryList!.oFFDAYS : 0}"), 6.width, optionUI(LocaleKeys.nonAnalyzed.tr(), "${getTimeCardSummaryList?.uNAUTHORIZEDLEAVE != null ? getTimeCardSummaryList!.uNAUTHORIZEDLEAVE : 0}"), 6.width, optionUI(LocaleKeys.shortageHour.tr(), "${getTimeCardSummaryList?.sHORTAGEHRS != null ? getTimeCardSummaryList!.sHORTAGEHRS : 0}"), ], ).paddingOnly(left: 21, right: 21), 38.height, AspectRatio( aspectRatio: 375 / 226, child: Container( padding: const EdgeInsets.only(top: 28, bottom: 28, left: 21, right: 21), decoration: BoxDecoration( color: Colors.white, borderRadius: const BorderRadius.only( topLeft: Radius.circular(25.0), topRight: Radius.circular(25.0), ), boxShadow: [ BoxShadow( offset: const Offset(0, 1), blurRadius: 26, color: MyColors.darkColor.withOpacity(0.1), ), ], ), child: Row( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ "${LocaleKeys.attendance.tr()} ${LocaleKeys.stats.tr()}".toSectionHeading(), 29.height, Row( children: [ Container( height: 9, width: 9, decoration: BoxDecoration( color: MyColors.lightGreenColor, borderRadius: BorderRadius.circular(100), ), ), 7.width, "${LocaleKeys.present.tr()} ${getTimeCardSummaryList?.aTTENDEDDAYS != null ? getTimeCardSummaryList?.aTTENDEDDAYS : 0}" .toText16(isBold: true, color: MyColors.lightGreenColor), ], ), 8.height, Row( children: [ Container( height: 9, width: 9, decoration: BoxDecoration( color: MyColors.backgroundBlackColor, borderRadius: BorderRadius.circular(100), ), ), 7.width, "${LocaleKeys.absent.tr()} ${getTimeCardSummaryList?.aBSENTDAYS != null ? getTimeCardSummaryList?.aBSENTDAYS : 0}".toText16( isBold: true, color: MyColors.backgroundBlackColor, ) ], ), ], ).expanded, //44.width, SizedBox( height: 170, width: 170, child: PieChart( dataMap: dataMap, animationDuration: const Duration(milliseconds: 800), chartLegendSpacing: 0, chartRadius: MediaQuery.of(context).size.width / 5.2, colorList: _colorList, initialAngleInDegree: 270, chartType: ChartType.ring, emptyColor: MyColors.greyACColor, ringStrokeWidth: 80, legendOptions: const LegendOptions( showLegendsInRow: false, showLegends: false, ), chartValuesOptions: const ChartValuesOptions( showChartValueBackground: false, showChartValues: true, showChartValuesInPercentage: true, showChartValuesOutside: true, decimalPlaces: 0, chartValueStyle: TextStyle( fontWeight: FontWeight.bold, fontSize: 16, letterSpacing: -0.64, color: MyColors.white, ), ), ), ), ], ), ), ), ], ), ], ), ); } Widget optionUI(String title, String value) { return AspectRatio( aspectRatio: 1 / 1, child: Container( padding: const EdgeInsets.only(top: 10, left: 8, right: 8, bottom: 5), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(15), boxShadow: [ BoxShadow( offset: const Offset(0, 1), blurRadius: 26, color: MyColors.darkColor.withOpacity(0.1), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [title.toText10(color: MyColors.darkTextColor).expanded, value.toText20(color: MyColors.darkTextColor)], ), ), ).expanded; } Widget calendarWidget() { return SfCalendar( view: CalendarView.month, showDatePickerButton: false, controller: _calendarController, backgroundColor: Colors.white, headerHeight: 0, viewNavigationMode: ViewNavigationMode.none, todayHighlightColor: MyColors.grey3AColor, showNavigationArrow: false, showCurrentTimeIndicator: false, showWeekNumber: false, cellBorderColor: Colors.white, selectionDecoration: BoxDecoration( border: Border.all(color: MyColors.white, width: 1), shape: BoxShape.circle, ), dataSource: MeetingDataSource(_getDataSource()), onTap: calendarTapped, monthViewSettings: const MonthViewSettings( dayFormat: 'EEE', showTrailingAndLeadingDates: false, showAgenda: false, ), viewHeaderStyle: const ViewHeaderStyle( dayTextStyle: TextStyle(color: MyColors.grey3AColor, fontSize: 13, fontWeight: FontWeight.w600), ), monthCellBuilder: (build, details) { if (details.date.month == formattedDate.month && details.date.year == formattedDate.year) { int val = details.date.day; int index = val-1; //check day is off // if (getDayHoursTypeDetailsList.isNotEmpty) { // bool isDayIsOff = getDayHoursTypeDetailsList[val - 1].aTTENDEDFLAG == 'N' && getDayHoursTypeDetailsList[val - 1].dAYTYPE == 'OFF'; // bool isDayIsPresent = getDayHoursTypeDetailsList[val - 1].aTTENDEDFLAG == 'Y'; // bool isDayIsAbsent = getDayHoursTypeDetailsList[val - 1].aTTENDEDFLAG == 'N' && getDayHoursTypeDetailsList[val - 1].aBSENTFLAG == 'Y'; List getDayHours = getDayHoursTypeDetailsList.where((GetDayHoursTypeDetailsList element) => DateFormat("MM/dd/yyyy", "en_US").parse(element.sCHEDULEDATE!).day == details.date.day).toList(); if (getDayHours.isNotEmpty) { bool isDayIsOff = getDayHoursTypeDetailsList[index].aTTENDEDFLAG == 'N' && getDayHoursTypeDetailsList[index].dAYTYPE == 'OFF'; bool isDayIsPresent = getDayHoursTypeDetailsList[index].aTTENDEDFLAG == 'Y'; bool isDayIsAbsent = getDayHoursTypeDetailsList[index].aTTENDEDFLAG == 'N' && getDayHoursTypeDetailsList[index].aBSENTFLAG == 'Y'; if (isDayIsOff) { return Container( margin: const EdgeInsets.all(4), decoration: BoxDecoration(color: MyColors.greyACColor.withOpacity(.12), shape: BoxShape.circle), alignment: Alignment.center, child: Text( "$val", style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500, color: MyColors.greyA5Color), ), ); } //check day is Present else if (isDayIsPresent) { return Container( margin: const EdgeInsets.all(4), decoration: BoxDecoration( gradient: const LinearGradient( transform: GradientRotation(.46), begin: Alignment.topRight, end: Alignment.bottomLeft, colors: [MyColors.gradiantEndColor, MyColors.gradiantStartColor], ), shape: BoxShape.circle, boxShadow: [ BoxShadow( offset: const Offset(0, 2), blurRadius: 26, color: MyColors.blackColor.withOpacity(0.100), ), ], ), alignment: Alignment.center, child: Text( "$val", style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w500, color: MyColors.white), ), ); } //check day is Absent else if (isDayIsAbsent) { return Container( margin: const EdgeInsets.all(4), decoration: BoxDecoration( color: MyColors.backgroundBlackColor, shape: BoxShape.circle, boxShadow: [ BoxShadow( offset: const Offset(0, 2), blurRadius: 26, color: MyColors.blackColor.withOpacity(0.100), ), ], ), alignment: Alignment.center, child: Text( "$val", style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500, color: MyColors.white), ), ); } } return Container( margin: const EdgeInsets.all(4), decoration: BoxDecoration( border: Border.all(color: MyColors.backgroundBlackColor, width: 2.0, style: BorderStyle.solid), //Border.all shape: BoxShape.circle, boxShadow: [ BoxShadow( offset: const Offset(0, 2), blurRadius: 26, color: MyColors.blackColor.withOpacity(0.100), ), ], ), alignment: Alignment.center, child: Text( "$val", style: const TextStyle( fontSize: 13, fontWeight: FontWeight.w500, color: Color(0xff1F2428), ), ), ); } else { return const SizedBox(); } }, ); } void calendarTapped(CalendarTapDetails details) async { dynamic index = details.date?.day; if (index != null) { index = index - 1; } pRTPID = getDayHoursTypeDetailsList[index].rTPID; await getScheduleShiftsDetails(index, pRTPID); dynamic value = getScheduleShiftsDetailsList!.pERCENTAGE; dynamic percentage; if (value!.indexOf('%') == 3) { percentage = int.parse(value!.substring(0, 3)); } else if (value!.indexOf('%') == 2) { percentage = int.parse(value!.substring(0, 2)); } else if (value!.indexOf('%') == 1 && value!.indexOf('%') != 0 && value!.indexOf('%') != "") { percentage = int.parse(value!.substring(0, 1)); } else if (value!.indexOf('%') == 0 || value!.indexOf('%') == "") { percentage = 0; } if (details.date!.month == formattedDate.month && details.date!.year == formattedDate.year) { int val = details.date!.day; getDayHoursTypeDetailsList?[val - 1].aTTENDEDFLAG == 'Y' ? showModalBottomSheet( context: context, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(25)), isScrollControlled: true, backgroundColor: MyColors.backgroundBlackColor, builder: (_) { return DraggableScrollableSheet( maxChildSize: 0.9, expand: false, builder: (_, controller) { dynamic dmyString = getScheduleShiftsDetailsList!.sCHEDULEDATE; DateTime dateTime1 = DateFormat("MM/dd/yyyy hh:mm:ss", "en_US").parse(dmyString); return Column( children: [ Container( width: 49, height: 7, margin: const EdgeInsets.symmetric(vertical: 10), decoration: BoxDecoration( borderRadius: BorderRadius.circular(25), color: MyColors.darkGreyColor, ), ), Expanded( child: ListView.builder( controller: controller, itemCount: 1, itemBuilder: (_, i) => Container( decoration: const BoxDecoration( borderRadius: BorderRadius.vertical( top: Radius.circular(25.0), ), color: MyColors.backgroundBlackColor, ), child: Column( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ "${DateFormat("MMMM-dd-yyyy").format(dateTime1).replaceAll('-', " ")}".toText24(isBold: true, color: Colors.white), LocaleKeys.attendanceDetails.tr().toText16(color: MyColors.greyACColor), 12.height, CircularStepProgressBar( totalSteps: 16 * 4, currentStep: percentage, width: 224, height: 236, selectedColor: MyColors.gradiantEndColor, unselectedColor: MyColors.grey70Color, child: Column( mainAxisSize: MainAxisSize.min, children: [ "${getScheduleShiftsDetailsList!.pERCENTAGE}".toText44(color: Colors.white, isBold: true), LocaleKeys.completed.tr().toText11(color: MyColors.greyACColor), 28.height, LocaleKeys.shiftTime.tr().toText11(color: MyColors.greyACColor), "${getScheduleShiftsDetailsList!.sHTNAME}".toText22(color: Colors.white, isBold: true), ], ).center, ).center, ], ).paddingOnly(left: 21, right: 21, top: 27, bottom: 37), Stack( children: [ Container( width: double.infinity, decoration: const BoxDecoration(borderRadius: BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)), color: Colors.white), padding: const EdgeInsets.only(left: 31, right: 31, top: 30, bottom: 29), child: Column( children: [ Row( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.actualCheckIn.tr().toText11( color: MyColors.grey67Color, ), "${getScheduleShiftsDetailsList!.sHTACTUALSTARTTIME}" != "" ? "${getScheduleShiftsDetailsList!.sHTACTUALSTARTTIME}".toText22(color: Colors.black, isBold: true) : "__".toText22(color: Colors.black, isBold: true), ], ).expanded, Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.actualCheckOut.tr().toText11( color: MyColors.grey67Color, ), "${getScheduleShiftsDetailsList!.sHTACTUALENDTIME}" != "" ? "${getScheduleShiftsDetailsList!.sHTACTUALENDTIME}".toText22(color: Colors.black, isBold: true) : "__".toText22(color: Colors.black, isBold: true), ], ).expanded, ], ), 25.height, const Divider( height: 1, thickness: 1, color: MyColors.lightGreyEFColor, ), 25.height, Row( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.approvedCheckIn.tr().toText11( color: MyColors.grey67Color, ), "${getScheduleShiftsDetailsList!.aPPROVEDSTARTTIME}" != "" ? "${getScheduleShiftsDetailsList!.aPPROVEDSTARTTIME}".toText22(color: MyColors.greenColor, isBold: true) : "__".toText22(color: MyColors.greenColor, isBold: true), ], ).expanded, Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.approvedCheckOut.tr().toText11( color: MyColors.grey67Color, ), "${getScheduleShiftsDetailsList!.aPPROVEDENDTIME}" != "" ? "${getScheduleShiftsDetailsList!.aPPROVEDENDTIME}".toText22(color: MyColors.greenColor, isBold: true) : "__".toText22(color: MyColors.greenColor, isBold: true), ], ).expanded, ], ), 25.height, const Divider( height: 1, thickness: 1, color: MyColors.lightGreyEFColor, ), 25.height, Row( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.lateIn.tr().toText11( color: MyColors.grey67Color, ), "${getDayHoursTypeDetailsList[index].lATEINHRS}".toText22(color: MyColors.redColor, isBold: true), ], ).expanded, Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.excess.tr().toText11( color: MyColors.grey67Color, ), "${getDayHoursTypeDetailsList[index].eXCESSHRS}".toText22(color: MyColors.backgroundBlackColor, isBold: true), ], ).expanded, ], ), 25.height, const Divider( height: 1, thickness: 1, color: MyColors.lightGreyEFColor, ), 25.height, Row( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.shortage.tr().toText11( color: MyColors.grey67Color, ), "${getDayHoursTypeDetailsList[index].sHORTAGEHRS}".toText22(color: MyColors.backgroundBlackColor, isBold: true), ], ).expanded, Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.earlyOut.tr().toText11( color: MyColors.grey67Color, ), "${getDayHoursTypeDetailsList[index].eARLYOUTHRS}".toText22(color: Colors.black, isBold: true), ], ).expanded, ], ), ], ), ), ], ), ], ), ), ), ), ], ); }, ); }, ) : null; } } List _getDataSource() { List meetings = []; return meetings; } static String getMonth(int month) { switch (month) { case 1: return "January"; case 2: return "February"; case 3: return "March"; case 4: return "April"; case 5: return "May"; case 6: return "June"; case 7: return "July"; case 8: return "August"; case 9: return "September"; case 10: return "October"; case 11: return "November"; case 12: return "December"; default: return ""; } } static String getMonthAr(int month) { switch (month) { case 1: return 'يناير'; case 2: return ' فبراير'; case 3: return 'مارس'; case 4: return 'أبريل'; case 5: return 'مايو'; case 6: return 'يونيو'; case 7: return 'يوليو'; case 8: return 'أغسطس'; case 9: return 'سبتمبر'; case 10: return ' اكتوبر'; case 11: return ' نوفمبر'; case 12: return 'ديسمبر'; default: return ""; } } } class MeetingDataSource extends CalendarDataSource { MeetingDataSource(List source) { appointments = source; } @override DateTime getStartTime(int index) { return _getMeetingData(index).from; } @override DateTime getEndTime(int index) { return _getMeetingData(index).to; } @override String getSubject(int index) { return _getMeetingData(index).eventName; } @override Color getColor(int index) { return _getMeetingData(index).background; } @override bool isAllDay(int index) { return _getMeetingData(index).isAllDay; } Meeting _getMeetingData(int index) { dynamic meeting = appointments; Meeting meetingData; if (meeting is Meeting) { meetingData = meeting; } return meeting; } } class Meeting { Meeting(this.eventName, this.from, this.to, this.background, this.isAllDay); String eventName; DateTime from; DateTime to; Color background; bool isAllDay; }