You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cloudsolutions-atoms/lib/modules/tm_module/tasks/task_request_detail_view.dart

401 lines
20 KiB
Dart

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<TaskRequestDetailsView> {
UserProvider? userProvider;
TaskRequestProvider? taskProvider;
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
getTaskData();
});
super.initState();
}
@override
void dispose() {
super.dispose();
}
Future<void> getTaskData() async {
taskProvider = Provider.of<TaskRequestProvider>(context, listen: false);
await taskProvider?.getTaskById(id: widget.taskId);
}
@override
Widget build(BuildContext context) {
userProvider ??= Provider.of<UserProvider>(context, listen: false);
return Scaffold(
appBar: DefaultAppBar(
title: context.translation.taskRequest,
actions: [
if (context.userProvider.isEngineer || context.userProvider.isNurse)
Selector<TaskRequestProvider, TaskData?>(
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<TaskRequestProvider>(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<TaskJobActivityEngineerTimer> timer) {
if (context.userProvider.user!.type == UsersTypes.normal_user) return const SizedBox();
double totalWorkingHours = timer.fold<double>(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();
}
}