From 423968d7f6e36e21139cd9d9b5c54aaabbc54df1 Mon Sep 17 00:00:00 2001 From: WaseemAbbasi22 Date: Mon, 29 Sep 2025 16:51:52 +0300 Subject: [PATCH] timer history added --- .../utilities/service_request_utils.dart | 30 +++ .../components/internal_request.dart | 176 ++++-------------- .../ppm_wo/update_ppm/update_ppm.dart | 4 + .../ppm_wo/update_ppm/wo_info_form.dart | 53 +++++- .../components/task_info_widget.dart | 98 ++++++---- .../recurrent_work_order_view.dart | 9 +- .../tasks_wo/update_task_request_view.dart | 53 ++++-- .../common_widgets/working_time_tile.dart | 76 ++++++++ .../update_device_transfer.dart | 65 +++++-- .../gas_refill/update_gas_refill_request.dart | 43 +++-- ...total_working_time_detail_bottomsheet.dart | 108 +++++------ 11 files changed, 430 insertions(+), 285 deletions(-) create mode 100644 lib/new_views/common_widgets/working_time_tile.dart diff --git a/lib/modules/cm_module/utilities/service_request_utils.dart b/lib/modules/cm_module/utilities/service_request_utils.dart index 8b8b77c1..7703d8ea 100644 --- a/lib/modules/cm_module/utilities/service_request_utils.dart +++ b/lib/modules/cm_module/utilities/service_request_utils.dart @@ -63,6 +63,7 @@ class ServiceRequestUtils { } static String formatTimerDuration(int seconds) { + int hours = seconds ~/ 3600; int minutes = (seconds % 3600) ~/ 60; int remainingSeconds = seconds % 60; @@ -74,9 +75,11 @@ class ServiceRequestUtils { if (minutes > 0) { formattedDuration += '$minutes minute${minutes > 1 ? 's' : ''} '; } + if (remainingSeconds > 0) { formattedDuration += '$remainingSeconds second${remainingSeconds > 1 ? 's' : ''} '; } + if (formattedDuration.isEmpty) { formattedDuration = 'Less than a second'; } @@ -105,6 +108,33 @@ class ServiceRequestUtils { return formattedDuration.trim(); } + static String formatTotalWorkingHours(double timeInHours) { + // Convert hours → seconds + int totalSeconds = (timeInHours * 3600).round(); + + int hours = totalSeconds ~/ 3600; + int minutes = (totalSeconds % 3600) ~/ 60; + int remainingSeconds = totalSeconds % 60; + + String formattedDuration = ''; + if (hours > 0) { + formattedDuration += '$hours hour${hours > 1 ? 's' : ''} '; + } + if (minutes > 0) { + formattedDuration += '$minutes minute${minutes > 1 ? 's' : ''} '; + } + if (remainingSeconds > 0) { + formattedDuration += '$remainingSeconds second${remainingSeconds > 1 ? 's' : ''} '; + } + if (formattedDuration.isEmpty) { + formattedDuration = 'Less than a minute'; + } + + return formattedDuration.trim(); + } + + + static void getQrCode({required BuildContext context}) async { showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); diff --git a/lib/modules/cm_module/views/forms/maintenance_request/components/internal_request.dart b/lib/modules/cm_module/views/forms/maintenance_request/components/internal_request.dart index 17e5e442..2cc1548f 100644 --- a/lib/modules/cm_module/views/forms/maintenance_request/components/internal_request.dart +++ b/lib/modules/cm_module/views/forms/maintenance_request/components/internal_request.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/controllers/providers/api/status_drop_down/report/service_report_repair_location_provider.dart'; @@ -7,13 +9,14 @@ import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/models/lookup.dart'; import 'package:test_sa/modules/cm_module/service_request_detail_provider.dart'; -import 'package:test_sa/modules/cm_module/utilities/service_request_utils.dart'; import 'package:test_sa/modules/cm_module/views/forms/maintenance_request/components/assistant_employee_list.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart'; import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart'; +import 'package:test_sa/new_views/common_widgets/working_time_tile.dart'; import 'package:test_sa/providers/service_request_providers/last_situation_provider.dart'; import 'package:test_sa/views/widgets/timer/app_timer.dart'; +import 'package:test_sa/views/widgets/total_working_time_detail_bottomsheet.dart'; class InternalMaintenanceRequest extends StatefulWidget { static const String id = "/add-internal-activity"; @@ -30,11 +33,14 @@ class _InternalMaintenanceRequestState extends State final TextEditingController _workingHoursController = TextEditingController(); final TextEditingController _travellingHoursController = TextEditingController(); Lookup statusLookup = Lookup.fromJson({"id": 5619, "name": "New", "value": 1}); + List timerList = []; + double totalWorkingHours = 0; @override void initState() { _requestDetailProvider = Provider.of(context, listen: false); super.initState(); + calculateWorkingTime(); WidgetsBinding.instance.addPostFrameCallback((_) { _travellingHoursController.text = _requestDetailProvider?.activityMaintenanceHelperModel?.travelHours != null ? _requestDetailProvider!.activityMaintenanceHelperModel!.travelHours.toString() : ''; @@ -42,6 +48,30 @@ class _InternalMaintenanceRequestState extends State // _isLoading = true; } + void calculateWorkingTime() { + final helperModel = _requestDetailProvider?.activityMaintenanceHelperModel; + final timers = helperModel?.activityMaintenanceTimers ?? []; + totalWorkingHours = timers.fold(0.0, (sum, item) { + if (item.startTime == null || item.endTime == null) return sum; + try { + final start = DateTime.parse(item.startTime!); + final end = DateTime.parse(item.endTime!); + final diffInHours = end.difference(start).inSeconds / 3600.0; // convert to hours + return sum + diffInHours; + } catch (_) { + return sum; + } + }); + timerList = timers.map((e) { + return TimerHistoryModel( + id: e.id, + startTime: e.startTime, + endTime: e.endTime, + workingHours: e.workingHours, + ); + }).toList(); + } + @override void dispose() { _workingHoursController.dispose(); @@ -52,10 +82,6 @@ class _InternalMaintenanceRequestState extends State @override Widget build(BuildContext context) { return Consumer(builder: (context, requestDetailProvider, child) { - double totalWorkingHours = _requestDetailProvider?.activityMaintenanceHelperModel?.activityMaintenanceTimers - ?.fold(0.0, (sum, item) => (sum ?? 0) + DateTime.parse(item.endTime!).difference(DateTime.parse(item.startTime!)).inSeconds) ?? - 0; - bool enableTimer = (requestDetailProvider.lastVisitedRequestActivityId == -1 || requestDetailProvider.activityMaintenanceHelperModel?.id == 0) ? true : requestDetailProvider.activityMaintenanceHelperModel?.id == requestDetailProvider.lastVisitedRequestActivityId; @@ -70,19 +96,6 @@ class _InternalMaintenanceRequestState extends State child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - // SingleItemDropDownMenu( - // context: context, - // height: 56.toScreenHeight, - // title: context.translation.activityStatus, - // showShadow: false, - // backgroundColor: AppColor.neutral100, - // initialValue: requestDetailProvider.activityMaintenanceHelperModel?.activityStatus, - // onSelect: (status) { - // print('status i got is ${status?.toJson()}'); - // requestDetailProvider.activityMaintenanceHelperModel?.activityStatus = status; - // }, - // ), - // 8.height, SingleItemDropDownMenu( context: context, height: 56.toScreenHeight, @@ -100,16 +113,6 @@ class _InternalMaintenanceRequestState extends State }, ), 8.height, - // AppTimerPicker( - // label: context.translation.timer, - // timer: requestDetailProvider.activityMaintenanceHelperModel?.activityTimePicker, - // enabled: enableTimer, - // // timerList: requestDetailProvider.activityMaintenanceHelperModel?.timerModelList ?? [], - // onPick: (time) { - // requestDetailProvider.activityMaintenanceHelperModel?.activityTimePicker = time; - // }, - // ), - // 8.height, AppTimer( label: context.translation.timer, timer: requestDetailProvider.activityMaintenanceHelperModel?.activityMaintenanceTimerModel, @@ -129,124 +132,14 @@ class _InternalMaintenanceRequestState extends State return true; }, ), - // Row( - // mainAxisSize: MainAxisSize.min, - // children: [ - // ADatePicker( - // label: context.translation.startTime, - // hideShadow: true, - // backgroundColor: AppColor.neutral100, - // date: requestDetailProvider.activityMaintenanceHelperModel?.startTime, - // formatDateWithTime: true, - // onDatePicker: (selectedDate) { - // showTimePicker( - // context: context, - // initialTime: TimeOfDay.now(), - // ).then((selectedTime) { - // // Handle the selected date and time here. - // if (selectedTime != null) { - // DateTime selectedDateTime = DateTime( - // selectedDate.year, - // selectedDate.month, - // selectedDate.day, - // selectedTime.hour, - // selectedTime.minute, - // ); - // requestDetailProvider.activityMaintenanceHelperModel?.startTime = selectedDateTime; - // requestDetailProvider.activityMaintenanceHelperModel?.endTime = null; - // requestDetailProvider.updateActivityMaintenanceHelperModel(requestDetailProvider.activityMaintenanceHelperModel); - // _workingHoursController.clear(); - // ServiceRequestUtils.calculateAndAssignWorkingHours( - // startTime: requestDetailProvider.activityMaintenanceHelperModel!.startTime, - // endTime: requestDetailProvider.activityMaintenanceHelperModel!.endTime, - // workingHoursController: _workingHoursController, - // updateModel: (hours) { - // requestDetailProvider.activityMaintenanceHelperModel?.workingHour = hours; - // }, - // ); - // } - // }); - // }, - // ).expanded, - // 8.width, - // ADatePicker( - // label: context.translation.endTime, - // hideShadow: true, - // backgroundColor: AppColor.neutral100, - // date: requestDetailProvider.activityMaintenanceHelperModel?.endTime, - // formatDateWithTime: true, - // onDatePicker: (selectedDate) { - // showTimePicker( - // context: context, - // initialTime: TimeOfDay.now(), - // ).then((selectedTime) { - // // Handle the selected date and time here. - // if (selectedTime != null) { - // DateTime selectedDateTime = DateTime(selectedDate.year, selectedDate.month, selectedDate.day, selectedTime.hour, selectedTime.minute); - // if (requestDetailProvider.activityMaintenanceHelperModel!.startTime != null && - // selectedDateTime.isBefore(requestDetailProvider.activityMaintenanceHelperModel!.startTime!)) { - // "End Date time must be greater then start date".showToast; - // return; - // } - // requestDetailProvider.activityMaintenanceHelperModel?.endTime = selectedDateTime; - // requestDetailProvider.updateActivityMaintenanceHelperModel(requestDetailProvider.activityMaintenanceHelperModel); - // ServiceRequestUtils.calculateAndAssignWorkingHours( - // startTime: requestDetailProvider.activityMaintenanceHelperModel!.startTime, - // endTime: requestDetailProvider.activityMaintenanceHelperModel!.endTime, - // workingHoursController: _workingHoursController, - // updateModel: (hours) { - // requestDetailProvider.activityMaintenanceHelperModel?.workingHour = hours; - // }, - // ); - // } - // }); - // }, - // ).expanded, - // ], - // ), 8.height, if (totalWorkingHours > 0.0) ...[ - Container( - height: 56.toScreenHeight, - padding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth), - alignment: Alignment.centerLeft, - decoration: BoxDecoration( - color: AppColor.fieldBgColor(context), - borderRadius: BorderRadius.circular(10), - boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)], - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - "Total Working Time", - style: Theme.of(context).textTheme.bodySmall?.copyWith(color: context.isDark ? null : AppColor.neutral20, fontWeight: FontWeight.w500), - ), - Text( - " ${ServiceRequestUtils.formatTimerDuration(totalWorkingHours.round())}", - style: Theme.of(context).textTheme.bodyMedium, - ), - ], - ), + WorkingTimeTile( + timerList: timerList, + totalWorkingTime: totalWorkingHours, ), 8.height, ], - // if (totalWorkingHours > 0.0) ...[ - // AppTextFormField( - // labelText: context.translation.workingHours, - // backgroundColor: AppColor.neutral80, - // controller: _workingHoursController, - // suffixIcon: "clock".toSvgAsset(width: 20, color: context.isDark ? AppColor.neutral10 : null).paddingOnly(end: 16), - // initialValue: requestDetailProvider.activityMaintenanceHelperModel?.workingHour != null ? requestDetailProvider.activityMaintenanceHelperModel?.workingHour.toString() : '', - // textAlign: TextAlign.center, - // labelStyle: AppTextStyles.textFieldLabelStyle, - // enable: false, - // showShadow: false, - // style: Theme.of(context).textTheme.titleMedium, - // ), - // 8.height, - // ], AppTextFormField( labelText: context.translation.travelingHours, controller: _travellingHoursController, @@ -305,7 +198,6 @@ class _InternalMaintenanceRequestState extends State }); }, ), - // const AssistantEmployeeCard().toShadowContainer(context, paddingObject: const EdgeInsets.symmetric(horizontal: 16)).paddingOnly(start: 13, end: 14, top: 12), ], ), ), diff --git a/lib/modules/pm_module/ppm_wo/update_ppm/update_ppm.dart b/lib/modules/pm_module/ppm_wo/update_ppm/update_ppm.dart index 6ce5b9d6..e4bd68ba 100644 --- a/lib/modules/pm_module/ppm_wo/update_ppm/update_ppm.dart +++ b/lib/modules/pm_module/ppm_wo/update_ppm/update_ppm.dart @@ -18,6 +18,7 @@ import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart'; import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; +import 'package:test_sa/views/widgets/total_working_time_detail_bottomsheet.dart'; import 'ppm_calibration_tools_form.dart'; import 'ppm_external_details_form.dart'; @@ -43,6 +44,7 @@ class _UpdatePpmState extends State with TickerProviderStateMixin { TabController? _tabController; late PpmProvider ppmProvider; + _onSubmit({required int status}) async { if (ppmProvider.validate()) { showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); @@ -100,6 +102,8 @@ class _UpdatePpmState extends State with TickerProviderStateMixin { super.initState(); } + + void initTabs(typeOfService) { if (typeOfService == null) { return; diff --git a/lib/modules/pm_module/ppm_wo/update_ppm/wo_info_form.dart b/lib/modules/pm_module/ppm_wo/update_ppm/wo_info_form.dart index 73be9501..3cef0f21 100644 --- a/lib/modules/pm_module/ppm_wo/update_ppm/wo_info_form.dart +++ b/lib/modules/pm_module/ppm_wo/update_ppm/wo_info_form.dart @@ -24,6 +24,7 @@ import 'package:test_sa/modules/cm_module/utilities/service_request_utils.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart'; import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart'; +import 'package:test_sa/new_views/common_widgets/working_time_tile.dart'; import 'package:test_sa/providers/ppm_asset_availability_provider.dart'; import 'package:test_sa/providers/ppm_electrical_safety_provider.dart'; import 'package:test_sa/providers/ppm_service_provider.dart'; @@ -33,6 +34,7 @@ import 'package:test_sa/views/widgets/date_and_time/date_picker.dart'; import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; import 'package:test_sa/views/widgets/requests/request_status.dart'; import 'package:test_sa/views/widgets/timer/app_timer.dart'; +import 'package:test_sa/views/widgets/total_working_time_detail_bottomsheet.dart'; class WoInfoForm extends StatefulWidget { // final Ppm model; @@ -46,10 +48,14 @@ class WoInfoForm extends StatefulWidget { } class _WoInfoFormState extends State { + List timerList = []; + double totalWorkingHours = 0; + @override void initState() { WidgetsBinding.instance.addPostFrameCallback((_) async { PpmProvider ppmProvider = Provider.of(context, listen: false); + calculateWorkingTime(); if (widget.planPreventiveVisit.preventiveVisitAttachments != null && widget.planPreventiveVisit.preventiveVisitAttachments!.isNotEmpty) { ppmProvider.ppmPlanAttachments = []; ppmProvider.ppmPlanAttachments.addAll(widget.planPreventiveVisit.preventiveVisitAttachments!.map((e) => GenericAttachmentModel(id: e.id, name: e.attachmentName!)).toList()); @@ -59,10 +65,35 @@ class _WoInfoFormState extends State { super.initState(); } + void calculateWorkingTime() { + final helperModel = widget.planPreventiveVisit; + final timers = helperModel.preventiveVisitTimers ?? []; + totalWorkingHours = timers.fold(0.0, (sum, item) { + if (item.startDateTime == null || item.endDateTime == null) return sum; + try { + final start = DateTime.parse(item.startDateTime!); + final end = DateTime.parse(item.endDateTime!); + final diffInHours = end.difference(start).inSeconds / 3600.0; // convert to hours + return sum + diffInHours; + } catch (_) { + return sum; + } + }); + print('totol working hours ${totalWorkingHours}'); + timerList = timers.map((e) { + return TimerHistoryModel( + id: e.id, + startTime: e.startDateTime, + endTime: e.endDateTime, + workingHours: e.workingHours, + ); + }).toList(); + } + @override Widget build(BuildContext context) { - double totalWorkingHours = - widget.planPreventiveVisit.preventiveVisitTimers?.fold(0.0, (sum, item) => (sum ?? 0) + DateTime.parse(item.endDateTime!).difference(DateTime.parse(item.startDateTime!)).inSeconds) ?? 0; + // double totalWorkingHours = + // widget.planPreventiveVisit.preventiveVisitTimers?.fold(0.0, (sum, item) => (sum ?? 0) + DateTime.parse(item.endDateTime!).difference(DateTime.parse(item.startDateTime!)).inSeconds) ?? 0; return Consumer(builder: (context, ppmProvider, child) { return ListView( @@ -380,14 +411,18 @@ class _WoInfoFormState extends State { ), 11.height, if (totalWorkingHours > 0.0) ...[ - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - context.translation.totalWorkingTime.bodyText2(context), - " ${ServiceRequestUtils.formatTimerDuration(totalWorkingHours.round())}".bodyText(context).custom(color: AppColor.black20), - ], + WorkingTimeTile( + timerList: timerList, + totalWorkingTime: totalWorkingHours, ), + // Row( + // crossAxisAlignment: CrossAxisAlignment.center, + // mainAxisAlignment: MainAxisAlignment.start, + // children: [ + // context.translation.totalWorkingTime.bodyText2(context), + // " ${ServiceRequestUtils.formatTimerDuration(totalWorkingHours.round())}".bodyText(context).custom(color: AppColor.black20), + // ], + // ), ], // 8.height, ], diff --git a/lib/modules/pm_module/recurrent_wo/components/task_info_widget.dart b/lib/modules/pm_module/recurrent_wo/components/task_info_widget.dart index 1871dc61..5c7dea3a 100644 --- a/lib/modules/pm_module/recurrent_wo/components/task_info_widget.dart +++ b/lib/modules/pm_module/recurrent_wo/components/task_info_widget.dart @@ -10,15 +10,55 @@ import 'package:test_sa/models/ppm/recurrent_wo.dart'; import 'package:test_sa/modules/cm_module/utilities/service_request_utils.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart'; +import 'package:test_sa/new_views/common_widgets/working_time_tile.dart'; import 'package:test_sa/views/widgets/requests/request_status.dart'; import 'package:test_sa/views/widgets/timer/app_timer.dart'; +import 'package:test_sa/views/widgets/total_working_time_detail_bottomsheet.dart'; -class RecurrentTaskInfoWidget extends StatelessWidget { +class RecurrentTaskInfoWidget extends StatefulWidget { final RecurrentWoData? model; RecurrentTaskInfoWidget({super.key, required this.model}); + @override + State createState() => _RecurrentTaskInfoWidgetState(); +} + +class _RecurrentTaskInfoWidgetState extends State { + @override + void initState() { + calculateWorkingTime(); + super.initState(); + } + + List timerList = []; + double totalWorkingHours = 0; + + void calculateWorkingTime() { + final helperModel = widget.model; + final timers = helperModel?.planRecurrentTaskTimers ?? []; + totalWorkingHours = timers.fold(0.0, (sum, item) { + if (item.startTime == null || item.endTime == null) return sum; + try { + final start = DateTime.parse(item.startTime!); + final end = DateTime.parse(item.endTime!); + final diffInHours = end.difference(start).inSeconds / 3600.0; // convert to hours + return sum + diffInHours; + } catch (_) { + return sum; + } + }); + timerList = timers.map((e) { + return TimerHistoryModel( + id: e.id, + startTime: e.startTime, + endTime: e.endTime, + workingHours: e.workingHours, + ); + }).toList(); + } + @override Widget build(BuildContext context) { return Column( @@ -28,41 +68,41 @@ class RecurrentTaskInfoWidget extends StatelessWidget { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (model?.status != null) + if (widget.model?.status != null) StatusLabel( - label: model?.status?.name, - textColor: AppColor.getRequestStatusTextColorByName(context, model?.status?.name), - backgroundColor: AppColor.getRequestStatusColorByName(context, model?.status?.name), + label: widget.model?.status?.name, + textColor: AppColor.getRequestStatusTextColorByName(context, widget.model?.status?.name), + backgroundColor: AppColor.getRequestStatusColorByName(context, widget.model?.status?.name), ), 8.height, - model!.title!.bodyText(context).custom(color: AppColor.textColor(context)), + widget.model!.title!.bodyText(context).custom(color: AppColor.textColor(context)), 2.height, - '${context.translation.taskNo}: ${model!.taskNo!}'.bodyText2(context).custom(color: AppColor.neutral120), - '${context.translation.site}: ${model!.site!.siteName!}'.bodyText2(context).custom(color: AppColor.neutral120), - '${context.translation.assignedEmployee}: ${model!.engineer!.userName ?? ""}'.bodyText2(context).custom(color: AppColor.neutral120), - '${context.translation.scheduledDate}: ${model!.scheduleDate!.toMonthYearFormat}'.bodyText2(context).custom(color: AppColor.neutral120), + '${context.translation.taskNo}: ${widget.model!.taskNo!}'.bodyText2(context).custom(color: AppColor.neutral120), + '${context.translation.site}: ${widget.model!.site!.siteName!}'.bodyText2(context).custom(color: AppColor.neutral120), + '${context.translation.assignedEmployee}: ${widget.model!.engineer!.userName ?? ""}'.bodyText2(context).custom(color: AppColor.neutral120), + '${context.translation.scheduledDate}: ${widget.model!.scheduleDate!.toMonthYearFormat}'.bodyText2(context).custom(color: AppColor.neutral120), ], ).toShadowContainer(context), 12.height, - model!.planRecurrentMedicalTaskRooms!.isEmpty + widget.model!.planRecurrentMedicalTaskRooms!.isEmpty ? Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - buildingInfoWidget(label: context.translation.department, value: model!.department?.name?.cleanupWhitespace.capitalizeFirstOfEach, context: context), + buildingInfoWidget(label: context.translation.department, value: widget.model!.department?.name?.cleanupWhitespace.capitalizeFirstOfEach, context: context), 8.height, - buildingInfoWidget(label: context.translation.floor, value: model!.floor?.name?.cleanupWhitespace.capitalizeFirstOfEach, context: context), + buildingInfoWidget(label: context.translation.floor, value: widget.model!.floor?.name?.cleanupWhitespace.capitalizeFirstOfEach, context: context), 8.height, - buildingInfoWidget(label: context.translation.room, value: model!.room?.name?.cleanupWhitespace.capitalizeFirstOfEach, context: context), + buildingInfoWidget(label: context.translation.room, value: widget.model!.room?.name?.cleanupWhitespace.capitalizeFirstOfEach, context: context), 8.height, - _timerWidget(context, model!.totalWorkingHours!), + _timerWidget(context,totalWorkingHours), 8.height, - _commentWidget(context: context, model: model) + _commentWidget(context: context, model: widget.model) ], // ], ).toShadowContainer(context, padding: 12) : Column( - children: [_timerWidget(context, model!.totalWorkingHours!), 8.height, _commentWidget(context: context, model: model)], + children: [_timerWidget(context, totalWorkingHours), 8.height, _commentWidget(context: context, model: widget.model)], ).toShadowContainer(context, padding: 12), ], ); @@ -82,10 +122,10 @@ class RecurrentTaskInfoWidget extends StatelessWidget { color: AppColor.fieldBgColor(context), borderRadius: BorderRadius.circular(10), ), - pickerTimer: model?.recurrentWoTimePicker, - pickerFromDate: DateTime.tryParse(model?.scheduleDate ?? ''), + pickerTimer: widget.model?.recurrentWoTimePicker, + pickerFromDate: DateTime.tryParse(widget.model?.scheduleDate ?? ''), onPick: (time) { - model?.recurrentWoTimePicker = time; + widget.model?.recurrentWoTimePicker = time; }, timerProgress: (isRunning) {}, onChange: (timer) async { @@ -95,17 +135,9 @@ class RecurrentTaskInfoWidget extends StatelessWidget { ), if (totalWorkingHours > 0.0) ...[ 12.height, - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - 'Total Working Time:'.bodyText2(context).custom(color: AppColor.neutral120), - 8.width, - Text( - ServiceRequestUtils.formatTimerDuration(totalWorkingHours.round()), - style: AppTextStyles.bodyText.copyWith(color: AppColor.textColor(context), fontWeight: FontWeight.w600), - ), - ], + WorkingTimeTile( + timerList: timerList, + totalWorkingTime: totalWorkingHours, ), ], ], @@ -119,8 +151,8 @@ class RecurrentTaskInfoWidget extends StatelessWidget { labelText: context.translation.comment, backgroundColor: AppColor.fieldBgColor(context), showShadow: false, - hintStyle: TextStyle(color: context.isDark?AppColor.white10:AppColor.black10), - labelStyle: TextStyle(color: context.isDark?AppColor.white10:AppColor.black10), + hintStyle: TextStyle(color: context.isDark ? AppColor.white10 : AppColor.black10), + labelStyle: TextStyle(color: context.isDark ? AppColor.white10 : AppColor.black10), alignLabelWithHint: true, textInputType: TextInputType.multiline, onChange: (value) { diff --git a/lib/modules/pm_module/recurrent_wo/recurrent_work_order_view.dart b/lib/modules/pm_module/recurrent_wo/recurrent_work_order_view.dart index 28d88fdd..fccf0af5 100644 --- a/lib/modules/pm_module/recurrent_wo/recurrent_work_order_view.dart +++ b/lib/modules/pm_module/recurrent_wo/recurrent_work_order_view.dart @@ -13,6 +13,7 @@ import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart'; import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; import 'package:test_sa/views/widgets/loaders/app_loading.dart'; import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; +import 'package:test_sa/views/widgets/total_working_time_detail_bottomsheet.dart'; import '../../../../../../controllers/providers/api/all_requests_provider.dart'; import 'components/room_tabs_widget.dart'; @@ -36,6 +37,7 @@ class _RecurrentWorkOrderViewState extends State { @override void initState() { allRequestsProvider = Provider.of(context, listen: false); + // calculateWorkingTime(); Provider.of(context, listen: false).getRecurrentWoById(id: widget.taskId!); super.initState(); } @@ -61,7 +63,7 @@ class _RecurrentWorkOrderViewState extends State { ? Column( children: [ SingleChildScrollView( - padding: EdgeInsets.all(16), + padding: const EdgeInsets.all(16), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, @@ -81,14 +83,13 @@ class _RecurrentWorkOrderViewState extends State { if (requestProvider.recurrentWoData?.status?.value != 1) ...[ FooterActionButton.footerContainer( context: context, - child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ AppFilledButton( label: context.translation.save, - buttonColor: context.isDark?AppColor.neutral20: AppColor.white60, - textColor: context.isDark?Colors.white: AppColor.black10, + buttonColor: context.isDark ? AppColor.neutral20 : AppColor.white60, + textColor: context.isDark ? Colors.white : AppColor.black10, onPressed: () => _updateTask(context: context, status: 0), ).expanded, 12.width, diff --git a/lib/modules/tm_module/tasks_wo/update_task_request_view.dart b/lib/modules/tm_module/tasks_wo/update_task_request_view.dart index e30de6b1..b17fa21f 100644 --- a/lib/modules/tm_module/tasks_wo/update_task_request_view.dart +++ b/lib/modules/tm_module/tasks_wo/update_task_request_view.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:developer'; import 'dart:io'; import 'package:flutter/material.dart'; @@ -27,6 +28,7 @@ import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart'; import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart'; import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart'; +import 'package:test_sa/new_views/common_widgets/working_time_tile.dart'; import 'package:test_sa/providers/gas_request_providers/site_provider.dart'; import 'package:test_sa/providers/loading_list_notifier.dart'; import 'package:test_sa/providers/task_request_provider/task_job_provider.dart'; @@ -38,6 +40,7 @@ import 'package:test_sa/views/widgets/loaders/app_loading.dart'; import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; import 'package:test_sa/views/widgets/status/report/service_report_assistant_employee_menu.dart'; import 'package:test_sa/views/widgets/timer/app_timer.dart'; +import 'package:test_sa/views/widgets/total_working_time_detail_bottomsheet.dart'; import '../../../../models/new_models/site.dart'; import '../../../models/new_models/work_order_detail_model.dart'; @@ -60,6 +63,8 @@ class _UpdateTaskRequestState extends State { List otherAttachments = []; bool installationType = true; String comments = ''; + List timerList = []; + double totalWorkingHours = 0; @override void initState() { @@ -76,12 +81,40 @@ class _UpdateTaskRequestState extends State { UserProvider _userProvider = Provider.of(context, listen: false); TaskData? taskModel = _taskProvider?.taskRequestModel; _taskProvider?.refresh(); - if (taskModel?.taskJobAttachments != null) { - attachments = taskModel!.taskJobAttachments!.where((e) => e.createdBy == _userProvider.user?.userID).map((e) => GenericAttachmentModel(id: e.id, name: e.name ?? '')).toList(); + calculateWorkingTime(helperModel: taskModel!); + if (taskModel.taskJobAttachments != null) { + attachments = taskModel.taskJobAttachments!.where((e) => e.createdBy == _userProvider.user?.userID).map((e) => GenericAttachmentModel(id: e.id, name: e.name ?? '')).toList(); otherAttachments = taskModel.taskJobAttachments?.where((e) => e.createdBy != _userProvider.user?.userID).toList() ?? []; } } + void calculateWorkingTime({required TaskData helperModel}) { + //we can make a single timer model for all type of request .... + // final helperModel = _taskProvider?.taskRequestModel; + final timers = helperModel.taskJobActivityEngineerTimers ?? []; + totalWorkingHours = timers.fold(0.0, (sum, item) { + if (item.startDate == null || item.endDate == null) return sum; + try { + final start = DateTime.parse(item.startDate!); + final end = DateTime.parse(item.endDate!); + + final diffInHours = end.difference(start).inSeconds / 3600.0; // convert to hours + + return sum + diffInHours; + } catch (_) { + return sum; + } + }); + timerList = timers.map((e) { + return TimerHistoryModel( + id: e.id, + startTime: e.startDate, + endTime: e.endDate, + workingHours: e.totalWorkingHour, + ); + }).toList(); + } + @override void dispose() { _requestedQuantityController.dispose(); @@ -509,8 +542,6 @@ class _UpdateTaskRequestState extends State { } Widget _timerWidget(BuildContext context, bool isTimerEnable, TaskRequestProvider taskProvider) { - double totalWorkingHours = - taskProvider.taskRequestModel?.taskJobActivityEngineerTimers?.fold(0.0, (sum, item) => (sum ?? 0) + DateTime.parse(item.endDate!).difference(DateTime.parse(item.startDate!)).inSeconds) ?? 0; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -535,17 +566,9 @@ class _UpdateTaskRequestState extends State { ), if (totalWorkingHours > 0.0) ...[ 8.height, - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - 'Total Working Time:'.bodyText2(context).custom(color: AppColor.textColor(context)), - 8.width, - Text( - ServiceRequestUtils.formatTimerDuration(totalWorkingHours.round()), - style: AppTextStyles.bodyText.copyWith(color: AppColor.textColor(context), fontWeight: FontWeight.w600), - ), - ], + WorkingTimeTile( + timerList: timerList, + totalWorkingTime: totalWorkingHours, ), ], 8.height, diff --git a/lib/new_views/common_widgets/working_time_tile.dart b/lib/new_views/common_widgets/working_time_tile.dart new file mode 100644 index 00000000..94a8b820 --- /dev/null +++ b/lib/new_views/common_widgets/working_time_tile.dart @@ -0,0 +1,76 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/modules/cm_module/utilities/service_request_utils.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/views/widgets/total_working_time_detail_bottomsheet.dart'; + +class WorkingTimeTile extends StatelessWidget { + final List timerList; + final double totalWorkingTime; + final String title; + + const WorkingTimeTile({ + Key? key, + required this.timerList, + required this.totalWorkingTime, + this.title = "Total Working Time", + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + height: 56.toScreenHeight, + padding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth), + alignment: Alignment.centerLeft, + decoration: BoxDecoration( + color: AppColor.fieldBgColor(context), + borderRadius: BorderRadius.circular(10), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 10, + ) + ], + ), + child: Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + title, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: context.isDark ? null : AppColor.neutral20, + fontWeight: FontWeight.w500, + ), + ), + Text( + " ${ServiceRequestUtils.formatTotalWorkingHours(totalWorkingTime)}", + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ).expanded, + 8.width, + const Icon(Icons.info_outline_rounded, color: AppColor.primary10), + ], + ), + ).onPress(() async{ + await showModalBottomSheet( + context: context, + isDismissible: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + clipBehavior: Clip.antiAliasWithSaveLayer, + builder: (BuildContext context) => + TotalWorkingTimeDetailBottomSheet(timerList: timerList), + ); + }); + } +} \ No newline at end of file diff --git a/lib/views/pages/device_transfer/update_device_transfer.dart b/lib/views/pages/device_transfer/update_device_transfer.dart index 7e93a34a..a7ac10eb 100644 --- a/lib/views/pages/device_transfer/update_device_transfer.dart +++ b/lib/views/pages/device_transfer/update_device_transfer.dart @@ -22,11 +22,13 @@ import 'package:test_sa/modules/cm_module/views/components/action_button/footer_ import 'package:test_sa/modules/cm_module/views/components/bottom_sheets/service_request_bottomsheet.dart'; import 'package:test_sa/modules/cm_module/views/forms/maintenance_request/components/assistant_employee_list.dart'; import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; +import 'package:test_sa/new_views/common_widgets/working_time_tile.dart'; import 'package:test_sa/views/app_style/sizing.dart'; import 'package:test_sa/views/widgets/date_and_time/date_picker.dart'; import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; import 'package:test_sa/views/widgets/loaders/loading_manager.dart'; import 'package:test_sa/views/widgets/status/report/service_report_assistant_employee_menu.dart'; +import 'package:test_sa/views/widgets/total_working_time_detail_bottomsheet.dart'; import '../../../extensions/text_extensions.dart'; import '../../../models/ppm/ppm.dart'; @@ -53,6 +55,8 @@ class _UpdateDeviceTransferState extends State { final DeviceTransfer _formModel = DeviceTransfer(); final GlobalKey _formKey = GlobalKey(); final GlobalKey _scaffoldKey = GlobalKey(); + List timerList = []; + double totalWorkingHours = 0; List attachments = []; @@ -200,7 +204,7 @@ class _UpdateDeviceTransferState extends State { void initState() { _formModel.fromDetails(widget.model); attachments = widget.model.assetTransferAttachments?.map((e) => GenericAttachmentModel(id: e.id?.toInt()??0, name: e.attachmentName!)).toList() ?? []; - + calculateWorkingTime(); super.initState(); } @@ -210,14 +214,41 @@ class _UpdateDeviceTransferState extends State { super.dispose(); } + void calculateWorkingTime() { + List timers =[]; + if(widget.isSender){ + timers = widget.model.senderVisitTimers??[]; + } else { + timers = widget.model.receiverVisitTimers??[]; + } + totalWorkingHours = timers.fold(0.0, (sum, item) { + if (item.startDateTime == null || item.endDateTime == null) return sum; + try { + final start = DateTime.parse(item.startDateTime!); + final end = DateTime.parse(item.endDateTime!); + final diffInHours = end.difference(start).inSeconds / 3600.0; // convert to hours + return sum + diffInHours; + } catch (_) { + return sum; + } + }); + print('total working time is ${totalWorkingHours}'); + timerList = timers.map((e) { + return TimerHistoryModel( + id: e.id, + startTime: e.startDateTime, + endTime: e.endDateTime, + workingHours: e.workingHours, + ); + }).toList(); + } + bool isTimerRunning = false; @override Widget build(BuildContext context) { _deviceTransferProvider = Provider.of(context, listen: false); - double totalWorkingHours = widget.isSender - ? (widget.model.senderVisitTimers?.fold(0.0, (sum, item) => (sum ?? 0) + DateTime.parse(item.endDateTime!).difference(DateTime.parse(item.startDateTime!)).inSeconds) ?? 0) - : (widget.model.receiverVisitTimers?.fold(0.0, (sum, item) => (sum ?? 0) + DateTime.parse(item.endDateTime!).difference(DateTime.parse(item.startDateTime!)).inSeconds) ?? 0); + bool isTimerEnable = widget.isSender ? (!(_formModel.senderMachineStatusValue == 3)) : (!(_formModel.receiverMachineStatusValue == 3)); return Scaffold( appBar: DefaultAppBar( @@ -361,18 +392,22 @@ class _UpdateDeviceTransferState extends State { ), if (totalWorkingHours > 0.0) ...[ 12.height, - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - 'Total Working Time:'.bodyText2(context).custom(color: AppColor.neutral120), - 8.width, - Text( - ServiceRequestUtils.formatTimerDuration(totalWorkingHours.round()), - style: AppTextStyles.bodyText.copyWith(color: context.isDark?AppColor.white10: AppColor.neutral50, fontWeight: FontWeight.w600), - ), - ], + WorkingTimeTile( + timerList: timerList, + totalWorkingTime: totalWorkingHours, ), + // Row( + // crossAxisAlignment: CrossAxisAlignment.center, + // mainAxisAlignment: MainAxisAlignment.start, + // children: [ + // 'Total Working Time:'.bodyText2(context).custom(color: AppColor.neutral120), + // 8.width, + // Text( + // ServiceRequestUtils.formatTimerDuration(totalWorkingHours.round()), + // style: AppTextStyles.bodyText.copyWith(color: context.isDark?AppColor.white10: AppColor.neutral50, fontWeight: FontWeight.w600), + // ), + // ], + // ), ], ], ); diff --git a/lib/views/pages/user/gas_refill/update_gas_refill_request.dart b/lib/views/pages/user/gas_refill/update_gas_refill_request.dart index b4d21ab8..2629e405 100644 --- a/lib/views/pages/user/gas_refill/update_gas_refill_request.dart +++ b/lib/views/pages/user/gas_refill/update_gas_refill_request.dart @@ -19,9 +19,11 @@ import 'package:test_sa/modules/cm_module/views/components/action_button/footer_ import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart'; +import 'package:test_sa/new_views/common_widgets/working_time_tile.dart'; import 'package:test_sa/providers/loading_list_notifier.dart'; import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; import 'package:test_sa/views/widgets/loaders/loading_manager.dart'; +import 'package:test_sa/views/widgets/total_working_time_detail_bottomsheet.dart'; import '../../../../extensions/text_extensions.dart'; import '../../../../models/new_models/gas_refill_model.dart'; @@ -58,6 +60,7 @@ class _UpdateGasRefillRequestState extends State { Lookup? _deliveredQuantity; List _attachments = []; + List timerList = []; static List deliveredQuantity = [ Lookup(name: "1", id: 1, value: 1), @@ -74,9 +77,8 @@ class _UpdateGasRefillRequestState extends State { if (widget.gasRefillModel != null) { _formModel.fromGasRefillModel(widget.gasRefillModel!); - - totalWorkingHours = _formModel.gasRefillTimers?.fold(0.0, (sum, item) => (sum ?? 0) + DateTime.parse(item.endDate!).difference(DateTime.parse(item.startDate!)).inSeconds) ?? 0; _commentController.text = _formModel.techComment ?? ""; + calculateWorkingTime(); try { _deliveredQuantity = deliveredQuantity.singleWhere((element) => element.value == _formModel.gasRefillDetails![0].deliverdQty); _currentDetails.deliverdQty = _deliveredQuantity!.value; @@ -86,7 +88,29 @@ class _UpdateGasRefillRequestState extends State { _attachments.addAll(_formModel.gasRefillAttachments!.map((e) => GenericAttachmentModel(id:e.id,name:e.attachmentName!)).toList()); } } + void calculateWorkingTime() { + final timers = _formModel.gasRefillTimers ?? []; + totalWorkingHours = timers.fold(0.0, (sum, item) { + if (item.startDate == null || item.endDate == null) return sum; + try { + final start = DateTime.parse(item.startDate!); + final end = DateTime.parse(item.endDate!); + final diffInHours = end.difference(start).inSeconds / 3600.0; // convert to hours + return sum + diffInHours; + } catch (_) { + return sum; + } + }); + timerList = timers.map((e) { + return TimerHistoryModel( + id: e.id, + startTime: e.startDate, + endTime: e.endDate, + workingHours: e.workingHours, + ); + }).toList(); + } @override void setState(VoidCallback fn) { if (mounted) super.setState(() {}); @@ -204,7 +228,6 @@ class _UpdateGasRefillRequestState extends State { clientName = _userProvider.user?.clientName; } } - double totalWorkingHours = _formModel.gasRefillTimers?.fold(0.0, (sum, item) => (sum ?? 0) + DateTime.parse(item.endDate!).difference(DateTime.parse(item.startDate!)).inSeconds) ?? 0; return Scaffold( appBar: DefaultAppBar( @@ -329,17 +352,9 @@ class _UpdateGasRefillRequestState extends State { ), if (totalWorkingHours > 0.0) ...[ 12.height, - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - 'Total Working Time:'.bodyText2(context).custom(color: AppColor.neutral120), - 8.width, - Text( - ServiceRequestUtils.formatTimerDuration(totalWorkingHours.round()), - style: AppTextStyles.bodyText.copyWith(color:context.isDark?AppColor.white10: AppColor.neutral50, fontWeight: FontWeight.w600), - ), - ], + WorkingTimeTile( + timerList: timerList, + totalWorkingTime: totalWorkingHours, ), ], ], diff --git a/lib/views/widgets/total_working_time_detail_bottomsheet.dart b/lib/views/widgets/total_working_time_detail_bottomsheet.dart index f89c68b9..6f6f9720 100644 --- a/lib/views/widgets/total_working_time_detail_bottomsheet.dart +++ b/lib/views/widgets/total_working_time_detail_bottomsheet.dart @@ -9,75 +9,77 @@ import 'package:test_sa/modules/cm_module/utilities/service_request_utils.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; class TotalWorkingTimeDetailBottomSheet extends StatelessWidget { - final List activityMaintenanceTimers; + final List timerList; - const TotalWorkingTimeDetailBottomSheet({Key? key, this.activityMaintenanceTimers = const []}) : super(key: key); + const TotalWorkingTimeDetailBottomSheet({Key? key, this.timerList = const []}) : super(key: key); @override Widget build(BuildContext context) { - return Container( - height: MediaQuery.of(context).size.height * 0.5, - padding: const EdgeInsets.all(16), - child: Column( - children: [ - Container( - width: 40.toScreenWidth, - height: 5.toScreenHeight, - decoration: BoxDecoration(color: AppColor.neutral40, borderRadius: BorderRadius.circular(30)), - ), - 8.height, - Align( - alignment: AlignmentDirectional.centerStart, - child: Text( - "Total Working Time", - style: AppTextStyles.heading3.copyWith(fontWeight: FontWeight.w600, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + return SafeArea( + child: Container( + height: MediaQuery.of(context).size.height * 0.5, + padding: const EdgeInsets.all(16), + child: Column( + children: [ + Container( + width: 40.toScreenWidth, + height: 5.toScreenHeight, + decoration: BoxDecoration(color: AppColor.neutral40, borderRadius: BorderRadius.circular(30)), ), - ), - ListView.separated( - itemCount: activityMaintenanceTimers.length, - separatorBuilder: (cxt, index) => const Divider().defaultStyle(context), - padding: const EdgeInsets.only(top: 16, bottom: 16), - itemBuilder: (cxt, index) { - int totalWorkingHours = DateTime.parse(activityMaintenanceTimers[index].endTime!).difference(DateTime.parse(activityMaintenanceTimers[index].startTime!)).inSeconds; - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - RichText( - text: TextSpan( - text: "From: ", - style: Theme.of(context).textTheme.bodySmall?.copyWith(color: context.isDark ? null : AppColor.neutral20, fontWeight: FontWeight.w500), - children: [TextSpan(text: activityMaintenanceTimers[index].startTime!.toServiceRequestDetailsFormat, style: Theme.of(context).textTheme.bodySmall)])), - 4.height, - RichText( - text: TextSpan( - text: "To: ", - style: Theme.of(context).textTheme.bodySmall?.copyWith(color: context.isDark ? null : AppColor.neutral20, fontWeight: FontWeight.w500), - children: [TextSpan(text: activityMaintenanceTimers[index].endTime!.toServiceRequestDetailsFormat, style: Theme.of(context).textTheme.bodySmall)])), - 4.height, - RichText( - text: TextSpan( - text: "Duration: ", - style: Theme.of(context).textTheme.bodySmall?.copyWith(color: context.isDark ? null : AppColor.neutral20, fontWeight: FontWeight.w500), - children: [TextSpan(text: " ${ServiceRequestUtils.formatTimerDuration(totalWorkingHours.round())}", style: Theme.of(context).textTheme.bodySmall)])), - ], - ); - }).expanded, - ], + 8.height, + Align( + alignment: AlignmentDirectional.centerStart, + child: Text( + "Total Working Time", + style: AppTextStyles.heading3.copyWith(fontWeight: FontWeight.w600, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + ), + ), + ListView.separated( + itemCount: timerList.length, + separatorBuilder: (cxt, index) => const Divider().defaultStyle(context), + padding: const EdgeInsets.only(top: 16, bottom: 16), + itemBuilder: (cxt, index) { + int totalWorkingHours = DateTime.parse(timerList[index].endTime!).difference(DateTime.parse(timerList[index].startTime!)).inSeconds; + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + RichText( + text: TextSpan( + text: "From: ", + style: Theme.of(context).textTheme.bodySmall?.copyWith(color: context.isDark ? null : AppColor.neutral20, fontWeight: FontWeight.w500), + children: [TextSpan(text: timerList[index].startTime!.toServiceRequestDetailsFormat, style: Theme.of(context).textTheme.bodySmall)])), + 4.height, + RichText( + text: TextSpan( + text: "To: ", + style: Theme.of(context).textTheme.bodySmall?.copyWith(color: context.isDark ? null : AppColor.neutral20, fontWeight: FontWeight.w500), + children: [TextSpan(text: timerList[index].endTime!.toServiceRequestDetailsFormat, style: Theme.of(context).textTheme.bodySmall)])), + 4.height, + RichText( + text: TextSpan( + text: "Duration: ", + style: Theme.of(context).textTheme.bodySmall?.copyWith(color: context.isDark ? null : AppColor.neutral20, fontWeight: FontWeight.w500), + children: [TextSpan(text: " ${ServiceRequestUtils.formatTimerDuration(totalWorkingHours.round())}", style: Theme.of(context).textTheme.bodySmall)])), + ], + ); + }).expanded, + ], + ), ), ); } } -class TimerModel { +class TimerHistoryModel { int? id; String? startTime; String? endTime; dynamic workingHours; - TimerModel({this.id, this.startTime, this.endTime, this.workingHours}); + TimerHistoryModel({this.id, this.startTime, this.endTime, this.workingHours}); - TimerModel.fromJson(Map json) { + TimerHistoryModel.fromJson(Map json) { id = json['id']; startTime = json['startTime']; endTime = json['endTime'];