import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/controllers/api_routes/urls.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/string_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/models/all_requests_and_count_model.dart'; import 'package:test_sa/models/new_models/task_request/task_request_model.dart'; import 'package:test_sa/modules/cm_module/cm_request_utils.dart'; import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; import 'package:test_sa/modules/cx_module/chat/chat_widget.dart'; import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; import 'package:test_sa/new_views/common_widgets/working_time_tile.dart'; import 'package:test_sa/providers/task_request_provider/task_request_provider.dart'; import 'package:test_sa/views/widgets/images/files_list.dart'; import 'package:test_sa/views/widgets/loaders/app_loading.dart'; import 'package:test_sa/views/widgets/requests/request_status.dart'; import 'package:test_sa/views/widgets/total_working_time_detail_bottomsheet.dart'; import '../../../../controllers/providers/api/user_provider.dart'; import '../../../../models/enums/user_types.dart'; import '../../../../new_views/app_style/app_color.dart'; import '../../../../new_views/common_widgets/app_filled_button.dart'; import 'update_task_request_view.dart'; class TaskRequestDetailsView extends StatefulWidget { static const String id = "/task-request-detail"; final int taskId; final RequestsDetails? requestDetails; final int moduleId; const TaskRequestDetailsView({Key? key, required this.taskId, this.requestDetails, required this.moduleId}) : super(key: key); @override _TaskRequestDetailsViewState createState() { return _TaskRequestDetailsViewState(); } } class _TaskRequestDetailsViewState extends State { UserProvider? userProvider; TaskRequestProvider? taskProvider; @override void initState() { WidgetsBinding.instance.addPostFrameCallback((_) { getTaskData(); }); super.initState(); } @override void dispose() { super.dispose(); } Future getTaskData() async { taskProvider = Provider.of(context, listen: false); await taskProvider?.getTaskById(id: widget.taskId); } @override Widget build(BuildContext context) { userProvider ??= Provider.of(context, listen: false); return Scaffold( appBar: DefaultAppBar( title: context.translation.taskRequest, actions: [ if (context.userProvider.isEngineer || context.userProvider.isNurse) Selector( selector: (_, myModel) => myModel.taskRequestModel, // Selects only the userName builder: (_, _taskReqModel, __) { if (_taskReqModel == null) return const SizedBox(); return ChatWidget( moduleId: widget.moduleId, isShow: _taskReqModel.taskJobStatus!.value! != 1, isReadOnly: _taskReqModel.taskJobStatus!.value! != 2, requestId: widget.taskId.toInt(), assigneeEmployeeNumber: _taskReqModel.assignedEngineer?.employeeId!, myLoginUserID: context.userProvider.user!.username!, contactEmployeeINumber: _taskReqModel.taskJobContactPersons!.first.user?.employeeId, ); }) ], ), body: Consumer(builder: (context, taskProvider, child) { TaskData? taskModel = taskProvider.taskRequestModel; return taskProvider.isLoading ? const ALoading() : Column(children: [ SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ StatusLabel( label: widget.requestDetails?.priority!, textColor: AppColor.getRequestStatusTextColorByName(context, widget.requestDetails?.priority!), backgroundColor: AppColor.getRequestStatusColorByName(context, widget.requestDetails?.priority!), ), 8.width, StatusLabel( label: widget.requestDetails?.status!, textColor: AppColor.getRequestStatusTextColorByName(context, widget.requestDetails?.status!), backgroundColor: AppColor.getRequestStatusColorByName(context, widget.requestDetails?.status!), ), 1.width.expanded, Text( widget.requestDetails?.date?.toServiceRequestCardFormat ?? "-", textAlign: TextAlign.end, style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50), ), ], ), 8.height, if (taskProvider.taskRequestModel?.taskType?.relatedTo?.value == 1) ...[ assetDetails(taskModel: taskProvider.taskRequestModel!), ], if (taskProvider.taskRequestModel?.taskType?.isInstallation == true) ...[installationWidget(taskModel: taskProvider.taskRequestModel!)], if (taskProvider.taskRequestModel?.taskType?.relatedTo?.value == 2) ...[ linkWithLocationWidget(taskModel: taskProvider.taskRequestModel!), ], const Divider().defaultStyle(context), Text( "Task Details".addTranslation, style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), ), '${context.translation.taskType}: ${taskModel?.taskType?.typeName ?? "-"}'.bodyText(context), '${context.translation.taskNo}: ${taskModel?.taskJobNo ?? "-"}'.bodyText(context), '${context.translation.createdBy}: ${taskModel?.userCreated?.userName ?? "-"}'.bodyText(context), if (taskProvider.taskRequestModel?.taskType?.isRecallAndAlert == true) ...[ recallAlertTypeWidget(taskModel: taskProvider.taskRequestModel!), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ 'Type of alert: ${taskProvider.taskRequestModel?.typeOfAlert?.name ?? '-'}'.bodyText(context), 'Risk Level: ${taskProvider.taskRequestModel?.riskLevel?.name ?? '-'}'.bodyText(context), 'Resource: ${taskProvider.taskRequestModel?.resource?.name ?? '-'}'.bodyText(context), 'Evaluator User: ${taskProvider.taskRequestModel?.evaluatorUser?.userName ?? '-'}'.bodyText(context) ], ), ], showWorkingTime(taskModel?.taskJobActivityEngineerTimers ?? []), if ((taskProvider.taskRequestModel?.taskJobContactPersons ?? []).isNotEmpty) ...[contactInfoCard(context, taskProvider.taskRequestModel!)], if (taskProvider.taskRequestModel?.callComment != null) ...[ const Divider().defaultStyle(context), Text( context.translation.comment, style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), ), 2.height, taskProvider.taskRequestModel!.callComment!.bodyText(context), ], if (taskProvider.taskRequestModel?.taskJobAttachments?.isNotEmpty ?? false) ...[ 8.height, const Divider().defaultStyle(context), Text( "Attachments".addTranslation, style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), ), FilesList(images: taskProvider.taskRequestModel!.taskJobAttachments!.map((toElement) => URLs.getFileUrl(toElement.name ?? '') ?? '').toList()), ], buildTechnicalComments(taskModel: taskModel), ], ).toShadowContainer(context).paddingAll(16), ).expanded, if (userProvider!.user!.type == UsersTypes.engineer && (taskModel?.taskJobStatus?.value != 4 && taskModel?.taskJobStatus?.value != 3)) FooterActionButton.footerContainer( context: context, child: AppFilledButton( onPressed: () async { if (taskProvider.taskRequestModel?.taskType?.isInstallation == true) { taskProvider.getSiteData(siteId: taskProvider.taskRequestModel?.asset?.siteId); } Navigator.of(context).push(MaterialPageRoute( builder: (_) => UpdateTaskRequest( taskId: widget.taskId, createdDate: widget.requestDetails?.date, ))); }, label: context.translation.updateRequest, ), ) ]); }), ); } Widget showWorkingTime(List timer) { if (context.userProvider.user!.type == UsersTypes.normal_user) return const SizedBox(); double totalWorkingHours = timer.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; } }); String totalHourString = (totalWorkingHours > 0.0) ? CMRequestUtils.formatTotalWorkingHours(totalWorkingHours) : totalWorkingHours.toString(); return RichText( text: TextSpan( text: "${context.translation.totalWorkingTime}: ", style: TextStyle( fontSize: 14.toScreenWidth, fontWeight: FontWeight.w500, fontStyle: FontStyle.normal, decoration: TextDecoration.none, color: context.isDark ? AppColor.neutral10 : AppColor.neutral20), children: [ TextSpan(text: "$totalHourString ", style: AppTextStyles.bodyText), const WidgetSpan( child: Icon(Icons.info_outline_rounded, color: AppColor.primary10, size: 14), ), ], ), ).onPress(() => showModalBottomSheet( context: context, isDismissible: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), clipBehavior: Clip.antiAliasWithSaveLayer, builder: (BuildContext context) => TotalWorkingTimeDetailBottomSheet( timerList: timer.map((e) { return TimerHistoryModel( id: e.id, startTime: e.startDate, endTime: e.endDate, workingHours: e.totalWorkingHour, ); }).toList()), )); } Widget contactInfoCard(BuildContext context, TaskData? taskModel) { return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ 8.height, const Divider().defaultStyle(context), Text( "Contact Info", style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), ), // 8.height, ListView.separated( physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, padding: EdgeInsets.zero, itemBuilder: (cxt, index) => Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ '${context.translation.name}: ${taskModel.taskJobContactPersons?[index].user?.userName ?? '-'}'.bodyText(context), '${context.translation.employeeId}: ${taskModel.taskJobContactPersons?[index].user?.employeeId ?? '-'}'.bodyText(context), '${context.translation.telephone}: ${taskModel.taskJobContactPersons?[index].user?.phoneNumber ?? '-'}'.bodyText(context), '${context.translation.email}: ${taskModel.taskJobContactPersons?[index].user?.email ?? '-'}'.bodyText(context), 'Extension: ${taskModel.taskJobContactPersons?[index].user?.extensionNo ?? '-'}'.bodyText(context), ], ), separatorBuilder: (cxt, index) => const Divider().defaultStyle(context), itemCount: taskModel!.taskJobContactPersons!.length), ], ); } Widget installationWidget({required TaskData taskModel}) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Divider().defaultStyle(context), Text( "Site Details".addTranslation, style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), ), '${context.translation.installationSite}: ${taskModel.asset?.siteName?.cleanupWhitespace.capitalizeFirstOfEach ?? "-"}'.bodyText(context), '${context.translation.installationBuilding}: ${taskModel.installationBuilding?.name?.cleanupWhitespace.capitalizeFirstOfEach ?? '-'}'.bodyText(context), '${context.translation.installationFloor}: ${taskModel.installationFloor?.name?.cleanupWhitespace.capitalizeFirstOfEach ?? '-'}'.bodyText(context), '${context.translation.installationDepartment}: ${taskModel.installationDepartment?.name?.cleanupWhitespace.capitalizeFirstOfEach ?? '-'}'.bodyText(context), '${context.translation.installationDate}: ${taskModel.installationDate?.toAssetDetailsFormat ?? "-"}'.bodyText(context), '${context.translation.serialNo}: ${taskModel.serialNo ?? '-'}'.bodyText(context), // const Divider().defaultStyle(context), ], ); } Widget buildTechnicalComments({TaskData? taskModel}) { String previousComments = taskModel?.taskJobActivityEngineerTimers?.map((e) => e.comment?.trim()).where((comment) => comment != null && comment.isNotEmpty).join('\n') ?? ''; if (previousComments.isNotEmpty) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Divider().defaultStyle(context), Text( "Technical Comments".addTranslation, style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), ), previousComments.bodyText2(context).custom(color: AppColor.neutral50), 8.height, ], ); } return const SizedBox(); } Widget linkWithLocationWidget({required TaskData taskModel}) { return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( "Location Details".addTranslation, style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), ), '${context.translation.site}: ${taskModel.site?.name ?? "-"}'.bodyText(context), '${context.translation.building}: ${taskModel.building?.name ?? "-"}'.bodyText(context), '${context.translation.floor}: ${taskModel.floor?.name ?? "-"}'.bodyText(context), '${context.translation.department}: ${taskModel.department?.name ?? "-"}'.bodyText(context), '${context.translation.room}: ${taskModel.room?.name ?? "-"}'.bodyText(context), ], ); } Widget assetDetails({required TaskData taskModel}) { return taskModel.asset != null ? Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( context.translation.assetDetails, style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), ), '${context.translation.assetName}: ${taskModel.asset?.assetName?.cleanupWhitespace.capitalizeFirstOfEach ?? "-"}'.bodyText(context), '${context.translation.assetNo}: ${taskModel.asset?.assetNumber ?? "-"}'.bodyText(context), '${context.translation.manufacture}: ${taskModel.asset?.manufacturer ?? "-"}'.bodyText(context), '${context.translation.model}: ${taskModel.asset?.model ?? "-"}'.bodyText(context), '${context.translation.serialNo}: ${taskModel.asset?.serialNo ?? "-"}'.bodyText(context), '${context.translation.assetType}: ${taskModel.asset?.assetTypeName ?? "-"}'.bodyText(context), '${context.translation.site}: ${taskModel.asset?.siteName ?? "-"}'.bodyText(context), '${context.translation.building}: ${taskModel.asset?.buildingName ?? "-"}'.bodyText(context), '${context.translation.floor}: ${taskModel.asset?.floorName ?? "-"}'.bodyText(context), '${context.translation.department}: ${taskModel.asset?.departmentName ?? "-"}'.bodyText(context), '${context.translation.room}: ${taskModel.asset?.roomName ?? "-"}'.bodyText(context), '${context.translation.supplier}: ${taskModel.asset?.supplierName ?? "-"}'.bodyText(context), 'PO Number: ${taskModel.asset?.poNumber ?? "-"}'.bodyText(context), '${context.translation.installationDate}: ${taskModel.asset?.installationDate?.toAssetDetailsFormat ?? "-"}'.bodyText(context), ], ) : const SizedBox(); } Widget recallAlertTypeWidget({required TaskData taskModel}) { if (taskModel.typeOfAlert != null) { switch (taskModel.typeOfAlert!.value) { case 1: return '${context.translation.acknowledge}: ${taskModel.isUserAcknowledge ?? "-"}'.bodyText(context); case 2: return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ // assetDetails(taskModel: taskModel), '${context.translation.completedActions}: ${taskModel.actionNeeded?.name ?? "-"}'.bodyText(context), if (taskModel.actionNeeded?.value == 1) ...[ '${context.translation.impactStatus}: ${taskModel.impactStatus?.name ?? "-"}'.bodyText(context), ], ], ); case 3: return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ // assetDetails(taskModel: taskModel), '${context.translation.acknowledge}: ${taskModel.isUserAcknowledge ?? "-"}'.bodyText(context), '${context.translation.completedActions}: ${taskModel.actionNeeded?.name ?? "-"}'.bodyText(context), if (taskModel.actionNeeded?.value == 1) ...[ '${context.translation.impactStatus}: ${taskModel.impactStatus?.name ?? "-"}'.bodyText(context), ], ], ); default: return const SizedBox(); } } return const SizedBox(); } }