Merge remote-tracking branch 'origin/design_3.0_ppm_new_flow' into design_3.0_ppm_new_flow
# Conflicts: # lib/views/pages/user/ppm/ppm_details_page.dart # lib/views/pages/user/ppm/ppm_work_order/recurrent_wo/recurrent_work_order_view.dartdesign_3.0_latest
commit
84dd801ed1
@ -0,0 +1,4 @@
|
||||
enum RecurrentTaskInspectionDataTypeEnum {
|
||||
bool, //bool
|
||||
number, // number
|
||||
}
|
||||
@ -0,0 +1,342 @@
|
||||
import 'package:test_sa/extensions/enum_extensions.dart';
|
||||
import 'package:test_sa/models/enums/recurrent_task_inspection_data_type.dart';
|
||||
import 'package:test_sa/models/lookup.dart';
|
||||
import 'package:test_sa/models/timer_model.dart';
|
||||
|
||||
class RecurrentWo {
|
||||
RecurrentWoData? recurrentWoData;
|
||||
String? message;
|
||||
String? title;
|
||||
String? innerMessage;
|
||||
int? responseCode;
|
||||
bool? isSuccess;
|
||||
|
||||
RecurrentWo({this.recurrentWoData, this.message, this.title, this.innerMessage, this.responseCode, this.isSuccess});
|
||||
|
||||
RecurrentWo.fromJson(Map<String, dynamic> json) {
|
||||
recurrentWoData = json['data'] != null ? RecurrentWoData.fromJson(json['data']) : null;
|
||||
message = json['message'];
|
||||
title = json['title'];
|
||||
innerMessage = json['innerMessage'];
|
||||
responseCode = json['responseCode'];
|
||||
isSuccess = json['isSuccess'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
if (recurrentWoData != null) {
|
||||
data['data'] = recurrentWoData!.toJson();
|
||||
}
|
||||
data['message'] = message;
|
||||
data['title'] = title;
|
||||
data['innerMessage'] = innerMessage;
|
||||
data['responseCode'] = responseCode;
|
||||
data['isSuccess'] = isSuccess;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class RecurrentWoData {
|
||||
int? id;
|
||||
Engineer? engineer;
|
||||
String? scheduleDate;
|
||||
Status? status;
|
||||
Site? site;
|
||||
Lookup? building;
|
||||
Lookup? floor;
|
||||
Lookup? department;
|
||||
Lookup? room;
|
||||
List<PlanRecurrentMedicalTaskRooms>? planRecurrentMedicalTaskRooms;
|
||||
List<PlanRecurrentTaskTimers>? planRecurrentTaskTimers;
|
||||
TimerModel? recurrentWoTimerModel = TimerModel();
|
||||
double? totalWorkingHours = 0.0;
|
||||
|
||||
RecurrentWoData(
|
||||
{this.id,
|
||||
this.engineer,
|
||||
this.scheduleDate,
|
||||
this.status,
|
||||
this.site,
|
||||
this.building,
|
||||
this.recurrentWoTimerModel,
|
||||
this.floor,
|
||||
this.department,
|
||||
this.room,
|
||||
this.planRecurrentMedicalTaskRooms,
|
||||
this.planRecurrentTaskTimers,
|
||||
this.totalWorkingHours});
|
||||
|
||||
RecurrentWoData.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
engineer = json['engineer'] != null ? new Engineer.fromJson(json['engineer']) : null;
|
||||
scheduleDate = json['scheduleDate'];
|
||||
status = json['status'] != null ? Status.fromJson(json['status']) : null;
|
||||
site = json['site'] != null ? Site.fromJson(json['site']) : null;
|
||||
building = json["building"] == null ? null : Lookup.fromJson(json["building"]);
|
||||
floor = json["floor"] == null ? null : Lookup.fromJson(json["floor"]);
|
||||
department = json["department"] == null ? null : Lookup.fromJson(json["department"]);
|
||||
room = json["room"] == null ? null : Lookup.fromJson(json["room"]);
|
||||
if (json['planRecurrentMedicalTaskRooms'] != null) {
|
||||
planRecurrentMedicalTaskRooms = <PlanRecurrentMedicalTaskRooms>[];
|
||||
json['planRecurrentMedicalTaskRooms'].forEach((v) {
|
||||
planRecurrentMedicalTaskRooms!.add(PlanRecurrentMedicalTaskRooms.fromJson(v));
|
||||
});
|
||||
}
|
||||
|
||||
if (json['planRecurrentTaskTimers'] != null) {
|
||||
planRecurrentTaskTimers = <PlanRecurrentTaskTimers>[];
|
||||
json['planRecurrentTaskTimers'].forEach((v) {
|
||||
planRecurrentTaskTimers?.add(PlanRecurrentTaskTimers.fromJson(v));
|
||||
});
|
||||
//TODO need to test this @waseem..
|
||||
totalWorkingHours = json['planRecurrentTaskTimers'].fold(0.0, (sum, item) => (sum ?? 0) + DateTime.parse(item['endDateTime']).difference(DateTime.parse(item['startDateTime'])).inSeconds) ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson({int? status = 0}) {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
List taskRoomTabAttributesJson = [];
|
||||
if (planRecurrentMedicalTaskRooms != null) {
|
||||
taskRoomTabAttributesJson = planRecurrentMedicalTaskRooms
|
||||
?.expand((room) => room.planRecurrentMedicalTaskRoomTabs ?? [])
|
||||
.expand((tab) => tab.planRecurrentMedicalTaskRoomTabAttributes ?? [])
|
||||
.map((attribute) => attribute.toJson())
|
||||
.toList() ??
|
||||
[];
|
||||
}
|
||||
data['id'] = id;
|
||||
data['statusValue'] = status;
|
||||
|
||||
if (planRecurrentTaskTimers != null) {
|
||||
data['planRecurrentTaskTimers'] = planRecurrentTaskTimers!.map((v) => v.toJson()).toList();
|
||||
}
|
||||
if (taskRoomTabAttributesJson.isNotEmpty) {
|
||||
data['attributes'] = taskRoomTabAttributesJson;
|
||||
} else {
|
||||
data['attributes'] = [];
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class Engineer {
|
||||
String? userId;
|
||||
String? userName;
|
||||
String? email;
|
||||
String? employeeId;
|
||||
int? languageId;
|
||||
|
||||
Engineer({this.userId, this.userName, this.email, this.employeeId, this.languageId});
|
||||
|
||||
Engineer.fromJson(Map<String, dynamic> json) {
|
||||
userId = json['userId'];
|
||||
userName = json['userName'];
|
||||
email = json['email'];
|
||||
employeeId = json['employeeId'];
|
||||
languageId = json['languageId'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['userId'] = userId;
|
||||
data['userName'] = userName;
|
||||
data['email'] = email;
|
||||
data['employeeId'] = employeeId;
|
||||
data['languageId'] = languageId;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class Status {
|
||||
int? id;
|
||||
String? name;
|
||||
int? value;
|
||||
|
||||
Status({this.id, this.name, this.value});
|
||||
|
||||
Status.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
name = json['name'];
|
||||
value = json['value'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['id'] = id;
|
||||
data['name'] = name;
|
||||
data['value'] = value;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class Site {
|
||||
int? id;
|
||||
String? siteName;
|
||||
|
||||
Site({this.id, this.siteName});
|
||||
|
||||
Site.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
siteName = json['siteName'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = {};
|
||||
data['id'] = id;
|
||||
data['siteName'] = siteName;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class PlanRecurrentMedicalTaskRooms {
|
||||
int? id;
|
||||
Room? room;
|
||||
List<PlanRecurrentMedicalTaskRoomTabs>? planRecurrentMedicalTaskRoomTabs;
|
||||
|
||||
PlanRecurrentMedicalTaskRooms({this.id, this.room, this.planRecurrentMedicalTaskRoomTabs});
|
||||
|
||||
PlanRecurrentMedicalTaskRooms.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
room = json['room'] != null ? Room.fromJson(json['room']) : null;
|
||||
if (json['planRecurrentMedicalTaskRoomTabs'] != null) {
|
||||
planRecurrentMedicalTaskRoomTabs = <PlanRecurrentMedicalTaskRoomTabs>[];
|
||||
json['planRecurrentMedicalTaskRoomTabs'].forEach((v) {
|
||||
planRecurrentMedicalTaskRoomTabs!.add(PlanRecurrentMedicalTaskRoomTabs.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = {};
|
||||
data['id'] = id;
|
||||
if (room != null) {
|
||||
data['room'] = room!.toJson();
|
||||
}
|
||||
if (planRecurrentMedicalTaskRoomTabs != null) {
|
||||
data['planRecurrentMedicalTaskRoomTabs'] = planRecurrentMedicalTaskRoomTabs!.map((v) => v.toJson()).toList();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class Room {
|
||||
int? id;
|
||||
String? roomId;
|
||||
|
||||
Room({this.id, this.roomId});
|
||||
|
||||
Room.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
roomId = json['roomId'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = {};
|
||||
data['id'] = id;
|
||||
data['roomId'] = roomId;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class PlanRecurrentMedicalTaskRoomTabs {
|
||||
int? id;
|
||||
String? tabName;
|
||||
int? tabMedicalRoomId;
|
||||
List<PlanRecurrentMedicalTaskRoomTabAttributes>? planRecurrentMedicalTaskRoomTabAttributes;
|
||||
|
||||
PlanRecurrentMedicalTaskRoomTabs({this.id, this.tabName, this.tabMedicalRoomId, this.planRecurrentMedicalTaskRoomTabAttributes});
|
||||
|
||||
PlanRecurrentMedicalTaskRoomTabs.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
tabName = json['tabName'];
|
||||
tabMedicalRoomId = json['tabMedicalRoomId'];
|
||||
if (json['planRecurrentMedicalTaskRoomTabAttributes'] != null) {
|
||||
planRecurrentMedicalTaskRoomTabAttributes = <PlanRecurrentMedicalTaskRoomTabAttributes>[];
|
||||
json['planRecurrentMedicalTaskRoomTabAttributes'].forEach((v) {
|
||||
planRecurrentMedicalTaskRoomTabAttributes!.add(PlanRecurrentMedicalTaskRoomTabAttributes.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = {};
|
||||
data['id'] = id;
|
||||
data['tabName'] = tabName;
|
||||
data['tabMedicalRoomId'] = tabMedicalRoomId;
|
||||
if (planRecurrentMedicalTaskRoomTabAttributes != null) {
|
||||
data['planRecurrentMedicalTaskRoomTabAttributes'] = planRecurrentMedicalTaskRoomTabAttributes!.map((v) => v.toJson()).toList();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class PlanRecurrentMedicalTaskRoomTabAttributes {
|
||||
int? id;
|
||||
Attribute? attribute;
|
||||
dynamic attributeValue;
|
||||
|
||||
PlanRecurrentMedicalTaskRoomTabAttributes({this.id, this.attribute, this.attributeValue});
|
||||
|
||||
PlanRecurrentMedicalTaskRoomTabAttributes.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
attribute = json['attribute'] != null ? Attribute.fromJson(json['attribute']) : null;
|
||||
attributeValue = json['attributeValue'] ?? (json['attribute']['type'] == 'bool' ? (json['attributeValue']?.toString() == 'true') : json['attributeValue']);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['id'] = id;
|
||||
data['attributeValue'] = attributeValue != null ? attributeValue.toString() : attributeValue;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class Attribute {
|
||||
String? name;
|
||||
String? type;
|
||||
String? key;
|
||||
RecurrentTaskInspectionDataTypeEnum? dataTypeEnum;
|
||||
|
||||
Attribute({this.name, this.type, this.key, this.dataTypeEnum});
|
||||
|
||||
Attribute.fromJson(Map<String, dynamic> json) {
|
||||
name = json['name'];
|
||||
type = json['type'];
|
||||
dataTypeEnum = json['type'] == null ? null : (json['type'] as String).toRecurrentTaskInspectionDataTypeEnum();
|
||||
key = json['key'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['name'] = name;
|
||||
data['type'] = type;
|
||||
data['key'] = key;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class PlanRecurrentTaskTimers {
|
||||
int? id;
|
||||
String? startTime;
|
||||
String? endTime;
|
||||
dynamic workingHours;
|
||||
|
||||
PlanRecurrentTaskTimers({this.id, this.startTime, this.endTime, this.workingHours});
|
||||
|
||||
PlanRecurrentTaskTimers.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
startTime = json['startDateTime'];
|
||||
endTime = json['endDateTime'];
|
||||
workingHours = json['workingHour'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['id'] = id;
|
||||
data['startDateTime'] = startTime;
|
||||
data['endDateTime'] = endTime;
|
||||
data['workingHour'] = workingHours;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:test_sa/controllers/providers/api/all_requests_provider.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/ppm/recurrent_wo.dart';
|
||||
import 'package:test_sa/new_views/app_style/app_color.dart';
|
||||
import 'package:test_sa/views/pages/user/ppm/ppm_work_order/recurrent_wo/recurrent_work_order_view.dart';
|
||||
import '../../../../views/widgets/requests/request_status.dart';
|
||||
|
||||
class RecurrentWoItemView extends StatelessWidget {
|
||||
final RequestsDetails? requestDetails;
|
||||
final bool showShadow;
|
||||
|
||||
const RecurrentWoItemView({Key? key, this.requestDetails, this.showShadow = true}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (requestDetails != null) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
StatusLabel(
|
||||
label: requestDetails!.priority!,
|
||||
textColor: AppColor.getRequestStatusTextColorByName(context, requestDetails!.priority!),
|
||||
backgroundColor: AppColor.getRequestStatusColorByName(context, requestDetails!.priority!),
|
||||
),
|
||||
8.width,
|
||||
StatusLabel(
|
||||
label: requestDetails!.status!,
|
||||
textColor: AppColor.getRequestStatusTextColorByName(context, requestDetails!.status!),
|
||||
backgroundColor: AppColor.getRequestStatusColorByName(context, requestDetails!.status!),
|
||||
),
|
||||
1.width.expanded,
|
||||
Text(
|
||||
requestDetails!.date?.toServiceRequestCardFormat ?? "",
|
||||
textAlign: TextAlign.end,
|
||||
style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50),
|
||||
),
|
||||
],
|
||||
),
|
||||
8.height,
|
||||
(requestDetails?.nameOfType ?? context.translation.ppmRequest).heading5(context),
|
||||
8.height,
|
||||
'${context.translation.taskNo}: ${requestDetails!.id}'.bodyText(context),
|
||||
'${context.translation.site}: ${requestDetails!.site}'.bodyText(context),
|
||||
// '${context.translation.code}: ${request.code}'.bodyText(context),
|
||||
'${context.translation.requestNo}: ${requestDetails!.requestNo}'.bodyText(context),
|
||||
16.height,
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
context.translation.viewDetails,
|
||||
style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context)),
|
||||
),
|
||||
4.width,
|
||||
Icon(Icons.arrow_forward, color: AppColor.blueStatus(context), size: 14)
|
||||
],
|
||||
),
|
||||
],
|
||||
).toShadowContainer(context, withShadow: showShadow).onPress(() async {
|
||||
Navigator.of(context).push(MaterialPageRoute(builder: (_) => RecurrentWorkOrderView(taskId: requestDetails!.id)));
|
||||
});
|
||||
}
|
||||
return const SizedBox();
|
||||
}
|
||||
}
|
||||
@ -1,116 +0,0 @@
|
||||
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/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/ppm/ppm.dart';
|
||||
import 'package:test_sa/models/timer_model.dart';
|
||||
import 'package:test_sa/new_views/app_style/app_color.dart';
|
||||
import 'package:test_sa/service_request_latest/utilities/service_request_utils.dart';
|
||||
import 'package:test_sa/views/widgets/requests/request_status.dart';
|
||||
import 'package:test_sa/views/widgets/timer/app_timer.dart';
|
||||
|
||||
class AssetInfoWidget extends StatelessWidget {
|
||||
DummyModel ?model;
|
||||
AssetInfoWidget({super.key,DummyModel ?model});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (model?.request?.priority != null)
|
||||
StatusLabel(
|
||||
label: model?.request?.priority,
|
||||
textColor: AppColor.getRequestStatusTextColorByName(context,model?.request?.priority),
|
||||
backgroundColor: AppColor.getRequestStatusColorByName(context,model?.request?.priority),
|
||||
),
|
||||
8.height,
|
||||
|
||||
model!.ppm!.assetName!.heading5(context),
|
||||
8.height,
|
||||
'${context.translation.taskNo}: ${model!.ppm!.assetName}'.bodyText(context),
|
||||
'${context.translation.site}: ${model!.ppm!.siteName!.cleanupWhitespace.capitalizeFirstOfEach}'.bodyText(context),
|
||||
'${context.translation.assignEngineer}: ${model!.ppm!.assignedEmployeeName ?? ""}'.bodyText(context),
|
||||
'${context.translation.scheduledDate}: ${'July 7, 2024' ?? ""}'.bodyText(context),
|
||||
],
|
||||
).toShadowContainer(context),
|
||||
12.height,
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
buildingInfoWidget(label: context.translation.department, value: model!.ppm!.departmentName!.cleanupWhitespace.capitalizeFirstOfEach,context: context),
|
||||
8.height,
|
||||
buildingInfoWidget(label: context.translation.floor, value: model!.ppm!.floorName!.cleanupWhitespace.capitalizeFirstOfEach,context: context),
|
||||
8.height,
|
||||
buildingInfoWidget(label: context.translation.room, value: model!.ppm!.roomName!.cleanupWhitespace.capitalizeFirstOfEach,context: context),
|
||||
8.height,
|
||||
AppTimer(
|
||||
label: context.translation.timer,
|
||||
timer: model!.timerModel,
|
||||
width:double.infinity,
|
||||
enabled: model!.timerModel?.endAt == null,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.neutral100,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
timerProgress: (isRunning) {},
|
||||
onChange: (timer) async {
|
||||
timer = timer;
|
||||
return true;
|
||||
},
|
||||
),
|
||||
// if (totalWorkingHours > 0.0) ...[
|
||||
11.height,
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Total Working Time: ",
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(color: context.isDark ? null : AppColor.neutral20, fontWeight: FontWeight.w500),
|
||||
),
|
||||
Text(
|
||||
" ${ServiceRequestUtils.formatTimerDuration(model!.totalWorkingHours.round())}",
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
8.height,
|
||||
],
|
||||
// ],
|
||||
).toShadowContainer(context,padding: 12),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildingInfoWidget({required String label, required String value, required BuildContext context}) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.neutral120),
|
||||
),
|
||||
3.height,
|
||||
Text(
|
||||
value,
|
||||
style: AppTextStyles.bodyText2.copyWith(color: AppColor.black10),
|
||||
)
|
||||
],
|
||||
).toShadowContainer(context, backgroundColor: AppColor.neutral100, borderRadius: 10, paddingObject: EdgeInsets.all(12.toScreenHeight), showShadow: false);
|
||||
}
|
||||
}
|
||||
|
||||
class DummyModel{
|
||||
RequestsDetails? request;
|
||||
Ppm? ppm;
|
||||
TimerModel? timerModel = TimerModel();
|
||||
double totalWorkingHours = 2;
|
||||
|
||||
DummyModel(this.request,this.ppm,this.timerModel,this.totalWorkingHours);
|
||||
}
|
||||
@ -0,0 +1,146 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:test_sa/dashboard_latest/dashboard_view.dart';
|
||||
import 'package:test_sa/extensions/int_extensions.dart';
|
||||
import 'package:test_sa/extensions/text_extensions.dart';
|
||||
import 'package:test_sa/extensions/widget_extensions.dart';
|
||||
import 'package:test_sa/models/enums/recurrent_task_inspection_data_type.dart';
|
||||
import 'package:test_sa/models/ppm/recurrent_wo.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';
|
||||
|
||||
class RoomInspectionCard extends StatefulWidget {
|
||||
final PlanRecurrentMedicalTaskRoomTabs? inspectionModel;
|
||||
|
||||
RoomInspectionCard({super.key, required this.inspectionModel});
|
||||
|
||||
@override
|
||||
State<RoomInspectionCard> createState() => _RoomInspectionCardState();
|
||||
}
|
||||
|
||||
class _RoomInspectionCardState extends State<RoomInspectionCard> {
|
||||
late List<bool> inspectionValues;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
widget.inspectionModel!.tabName!.bodyText(context).custom(color: AppColor.neutral50, fontWeight: FontWeight.w600),
|
||||
8.height,
|
||||
Column(
|
||||
children: widget.inspectionModel!.planRecurrentMedicalTaskRoomTabAttributes?.asMap().entries.map<Widget>((entry) {
|
||||
final model = entry.value;
|
||||
final attribute = model.attribute;
|
||||
|
||||
switch (attribute?.dataTypeEnum) {
|
||||
case RecurrentTaskInspectionDataTypeEnum.bool:
|
||||
return inspectionStatusRadioWidget(
|
||||
index: entry.key,
|
||||
model: model,
|
||||
context: context,
|
||||
);
|
||||
case RecurrentTaskInspectionDataTypeEnum.number:
|
||||
return inspectionStatusNumberWidget(
|
||||
index: entry.key,
|
||||
model: model,
|
||||
context: context,
|
||||
);
|
||||
default:
|
||||
return const SizedBox.shrink(); // Handles any unexpected cases gracefully
|
||||
}
|
||||
}).toList() ??
|
||||
[],
|
||||
)
|
||||
],
|
||||
).toShadowContainer(context).paddingOnly(top: 12);
|
||||
}
|
||||
|
||||
Widget inspectionStatusRadioWidget({
|
||||
required int index,
|
||||
required PlanRecurrentMedicalTaskRoomTabAttributes model,
|
||||
required BuildContext context,
|
||||
}) {
|
||||
bool status = model.attribute != null
|
||||
? model.attributeValue == 'true'
|
||||
? true
|
||||
: false
|
||||
: false;
|
||||
String statusString = status ? 'Pass' : 'Fail';
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
model.attribute!.name!.bodyText2(context).custom(color: AppColor.white936, fontWeight: FontWeight.w500),
|
||||
statusString.bodyText2(context).custom(color: AppColor.neutral120, fontWeight: FontWeight.w500),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: 24.toScreenHeight,
|
||||
width: 41.toScreenWidth,
|
||||
child: CupertinoSwitch(
|
||||
thumbColor: status ? AppColor.green50 : AppColor.red30,
|
||||
activeColor: AppColor.green30,
|
||||
trackColor: AppColor.red80,
|
||||
value: status,
|
||||
onChanged: (state) {
|
||||
setState(() {
|
||||
model.attributeValue = state;
|
||||
status = state;
|
||||
});
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
).paddingOnly(bottom: 12);
|
||||
}
|
||||
|
||||
Widget inspectionStatusNumberWidget({
|
||||
required int index,
|
||||
required PlanRecurrentMedicalTaskRoomTabAttributes model,
|
||||
required BuildContext context,
|
||||
}) {
|
||||
final border = OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(4), // Optional: Slight rounding
|
||||
borderSide: const BorderSide(
|
||||
color: AppColor.white70, // Border color
|
||||
width: 1, // 1px border
|
||||
),
|
||||
);
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
model.attribute!.name!.bodyText2(context).custom(color: AppColor.white936, fontWeight: FontWeight.w500),
|
||||
TextFormField(
|
||||
keyboardType: TextInputType.number,
|
||||
initialValue: model.attributeValue ?? '',
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 5.toScreenWidth),
|
||||
filled: true,
|
||||
fillColor: AppColor.neutral100,
|
||||
constraints: const BoxConstraints(
|
||||
maxHeight: 30,
|
||||
maxWidth: 50,
|
||||
),
|
||||
border: border,
|
||||
enabledBorder: border,
|
||||
focusedBorder: border,
|
||||
),
|
||||
onChanged: (value) {
|
||||
model.attributeValue = value;
|
||||
},
|
||||
),
|
||||
],
|
||||
).paddingOnly(bottom: 12);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,87 @@
|
||||
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/text_extensions.dart';
|
||||
import 'package:test_sa/extensions/widget_extensions.dart';
|
||||
import 'package:test_sa/models/ppm/recurrent_wo.dart';
|
||||
import 'package:test_sa/new_views/app_style/app_color.dart';
|
||||
import 'package:test_sa/views/pages/user/ppm/ppm_work_order/recurrent_wo/components/room_inspection_card.dart';
|
||||
import 'package:test_sa/views/widgets/loaders/app_loading.dart';
|
||||
|
||||
class RoomTabsWidget extends StatefulWidget {
|
||||
final RecurrentWoData? model;
|
||||
const RoomTabsWidget({Key? key,required this.model}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<RoomTabsWidget> createState() => _RoomTabsWidgetState();
|
||||
}
|
||||
|
||||
class _RoomTabsWidgetState extends State<RoomTabsWidget> {
|
||||
int selectedIndex = 0;
|
||||
bool isLoading = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: EdgeInsets.all(8.toScreenHeight),
|
||||
height: 57.toScreenHeight,
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: context.isDark ? AppColor.neutral50 : AppColor.white10,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: widget.model!.planRecurrentMedicalTaskRooms!.asMap().entries.map((entry) {
|
||||
final int index = entry.key;
|
||||
final String label = entry.value.room!.roomId!;
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
selectedIndex = index;
|
||||
//TODO replace with api response delay...
|
||||
Future.delayed(const Duration(seconds: 1)).whenComplete(() {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
// margin: EdgeInsets.only(right: 8.toScreenWidth),
|
||||
height: 49.toScreenHeight,
|
||||
padding: EdgeInsets.symmetric(
|
||||
// vertical: 20.toScreenHeight,
|
||||
horizontal: 30.toScreenWidth,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: selectedIndex == index ? (context.isDark ? AppColor.neutral60 : AppColor.neutral110) : Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(7),
|
||||
),
|
||||
child: label.bodyText(context).custom(
|
||||
color:AppColor.white936,
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
isLoading
|
||||
? SizedBox(height: 120.toScreenHeight, child: const ALoading().center)
|
||||
: ListView(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
children: widget.model!.planRecurrentMedicalTaskRooms![selectedIndex].planRecurrentMedicalTaskRoomTabs!.map<Widget>((card) {
|
||||
return RoomInspectionCard(inspectionModel: card);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,144 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:test_sa/controllers/providers/api/all_requests_provider.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/ppm/ppm.dart';
|
||||
import 'package:test_sa/models/ppm/recurrent_wo.dart';
|
||||
import 'package:test_sa/models/timer_model.dart';
|
||||
import 'package:test_sa/new_views/app_style/app_color.dart';
|
||||
import 'package:test_sa/service_request_latest/utilities/service_request_utils.dart';
|
||||
import 'package:test_sa/views/widgets/requests/request_status.dart';
|
||||
import 'package:test_sa/views/widgets/timer/app_timer.dart';
|
||||
|
||||
class RecurrentTaskInfoWidget extends StatelessWidget {
|
||||
final RecurrentWoData? model;
|
||||
|
||||
RecurrentTaskInfoWidget({super.key, required this.model});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (model?.status != null)
|
||||
StatusLabel(
|
||||
label: model?.status?.name,
|
||||
textColor: AppColor.getRequestStatusTextColorByName(context, model?.status?.name),
|
||||
backgroundColor: AppColor.getRequestStatusColorByName(context, model?.status?.name),
|
||||
),
|
||||
8.height,
|
||||
model!.site!.siteName!.bodyText(context).custom(color: AppColor.black10),
|
||||
2.height,
|
||||
'${context.translation.taskNo}: ${model!.id!}'.bodyText2(context).custom(color: AppColor.neutral120),
|
||||
'${context.translation.site}: ${model!.site!.siteName!}'.bodyText2(context).custom(color: AppColor.neutral120),
|
||||
'${context.translation.assignEngineer}: ${model!.engineer!.userName ?? ""}'.bodyText2(context).custom(color: AppColor.neutral120),
|
||||
'${context.translation.scheduledDate}: ${model!.scheduleDate!.toMonthYearFormat}'.bodyText2(context).custom(color: AppColor.neutral120),
|
||||
],
|
||||
).toShadowContainer(context),
|
||||
12.height,
|
||||
model!.planRecurrentMedicalTaskRooms!.isEmpty
|
||||
? Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
buildingInfoWidget(label: context.translation.department, value: model!.department!.name!.cleanupWhitespace.capitalizeFirstOfEach, context: context),
|
||||
8.height,
|
||||
buildingInfoWidget(label: context.translation.floor, value: model!.floor!.name!.cleanupWhitespace.capitalizeFirstOfEach, context: context),
|
||||
8.height,
|
||||
buildingInfoWidget(label: context.translation.room, value: model!.room!.name!.cleanupWhitespace.capitalizeFirstOfEach, context: context),
|
||||
8.height,
|
||||
_timerWidget(context, model!.totalWorkingHours!)
|
||||
],
|
||||
// ],
|
||||
).toShadowContainer(context, padding: 12)
|
||||
: _timerWidget(context, model!.totalWorkingHours!).toShadowContainer(context, padding: 12),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _timerWidget(BuildContext context, double totalWorkingHours) {
|
||||
return Consumer<AllRequestsProvider>(
|
||||
|
||||
builder: (context, snapshot,child) {
|
||||
return Column(
|
||||
children: [
|
||||
AppTimer(
|
||||
label: context.translation.timer,
|
||||
timer: snapshot.recurrentWoData?.recurrentWoTimerModel,
|
||||
width: double.infinity,
|
||||
enabled: snapshot.recurrentWoData?.recurrentWoTimerModel?.endAt == null,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.neutral100,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
timerProgress: (isRunning) {},
|
||||
onChange: (timer) async {
|
||||
snapshot.updateRecurrentWoTimer(timer: timer);
|
||||
return true;
|
||||
},
|
||||
),
|
||||
11.height,
|
||||
if (totalWorkingHours > 0.0) ...[
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
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,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
8.height,
|
||||
],
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget buildingInfoWidget({required String label, required String value, required BuildContext context}) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.neutral120),
|
||||
),
|
||||
3.height,
|
||||
Text(
|
||||
value,
|
||||
style: AppTextStyles.bodyText2.copyWith(color: AppColor.black10),
|
||||
)
|
||||
],
|
||||
).toShadowContainer(context, backgroundColor: AppColor.neutral100, borderRadius: 10, paddingObject: EdgeInsets.all(12.toScreenHeight), showShadow: false);
|
||||
}
|
||||
|
||||
class DummyModel {
|
||||
RequestsDetails? request;
|
||||
Ppm? ppm;
|
||||
TimerModel? timerModel = TimerModel();
|
||||
double totalWorkingHours = 2;
|
||||
|
||||
DummyModel(this.request, this.ppm, this.timerModel, this.totalWorkingHours);
|
||||
}
|
||||
Loading…
Reference in New Issue