Merge branch 'refs/heads/main_production_ai_enhancement' into ui_ux_rollout_merge

# Conflicts:
#	lib/modules/cm_module/views/components/service_request_detail_view.dart
#	lib/modules/incident_module/incident_detail_page.dart
#	lib/modules/traf_module/traf_request_detail_page.dart
ui_ux_rollout_merge_audio_video_call
Sikander Saleem 4 weeks ago
commit bdadd2fe7b

@ -1,7 +1,7 @@
class URLs {
URLs._();
static const String appReleaseBuildNumber = "42";
static const String appReleaseBuildNumber = "44";
// static const host1 = "https://atomsm.hmg.com"; // production url
// static const host1 = "https://atomsmdev.hmg.com"; // local DEV url
@ -74,10 +74,15 @@ class URLs {
//One Time Task Urls cleaner module new flow urls ....
static get getTaskType => "$_baseUrl/OneTimeTasks/GetTaskTypes";
static get scanQRCode => "$_baseUrl/OneTimeTasks/ScanQRCode";
static get proceedRequest => "$_baseUrl/OneTimeTasks/Proceed";
static get completeRequest => "$_baseUrl/OneTimeTasks/Complete";
static get getRequestById => "$_baseUrl/OneTimeTasks/GetById";
//service request new flow urls.
static get nurseDashboardCountUrl => '$_baseUrl/ServiceRequest/GetDashboardNurseCount';
@ -161,6 +166,8 @@ class URLs {
static get uploadWorkOrderAttachmentsUrl => '$_baseUrl/ServiceRequest/UploadAttachmentsWorkOrder';
static get workOrderByAssetIdAutoComplete => '$_baseUrl/ServiceRequest/WorkOrderByAssetIdAutoComplete';
static get getArrivalVerificationTypeUrl => '$_baseUrl/ArrivalVerificationType/GetArrivalVerificationType';
static get sendOtpUrl => '$_baseUrl/SmsNotification/SendOTP/';

@ -0,0 +1,11 @@
class WoByAssetIdModel {
int? id;
String? workOrderNo;
WoByAssetIdModel({this.id, this.workOrderNo});
WoByAssetIdModel.fromJson(dynamic json) {
id = json['id'];
workOrderNo = json['workOrderNo'];
}
}

@ -17,6 +17,7 @@ import 'package:test_sa/models/new_models/dashboard_detail.dart';
import 'package:test_sa/models/new_models/work_order_detail_model.dart';
import 'package:test_sa/models/service_request/spare_parts.dart';
import 'package:test_sa/models/service_request/supplier_details.dart';
import 'package:test_sa/models/wo_by_asset_id_model.dart';
import 'cm_request_utils.dart';
@ -1003,4 +1004,19 @@ class CMDetailProvider extends ChangeNotifier {
return -1;
}
}
Future<List<WoByAssetIdModel>> getWorkOrderByAssetId(String searchText, int assetId) async {
late Response response;
try {
response = await ApiManager.instance.post(URLs.workOrderByAssetIdAutoComplete, body: {"pageSize": 1, "pageNumber": 20, "search": searchText, "assetId": assetId});
List<WoByAssetIdModel> woList = [];
if (response.statusCode >= 200 && response.statusCode < 300) {
List categoriesListJson = json.decode(response.body)["data"];
woList = categoriesListJson.map((wo) => WoByAssetIdModel.fromJson(wo)).toList();
}
return woList;
} catch (error) {
return [];
}
}
}

@ -198,6 +198,7 @@ class _ServiceRequestDetailViewState extends State<ServiceRequestDetailView> {
InfoTextWidget(label: context.translation.serialNo, value: workOrder.asset!.assetSerialNo),
InfoTextWidget(label: context.translation.manufacture, value: workOrder.manufacturer?.name?.cleanupWhitespace.capitalizeFirstOfEach),
InfoTextWidget(label: context.translation.model, value: workOrder.model?.name!.cleanupWhitespace.capitalizeFirstOfEach),
InfoTextWidget(label: context.translation.assetType, value: workOrder.assetType?.name!.cleanupWhitespace.capitalizeFirstOfEach),
InfoTextWidget(label: context.translation.site, value: workOrder.site?.siteName?.cleanupWhitespace.capitalizeFirstOfEach),
InfoTextWidget(label: context.translation.building, value: workOrder.building?.name?.cleanupWhitespace.capitalizeFirstOfEach),
InfoTextWidget(label: context.translation.floor, value: workOrder.floor?.name?.cleanupWhitespace.capitalizeFirstOfEach ?? ""),

@ -16,12 +16,14 @@ import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/models/new_models/building.dart';
import 'package:test_sa/models/new_models/floor.dart';
import 'package:test_sa/models/new_models/site.dart';
import 'package:test_sa/models/wo_by_asset_id_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/incident_module/incident_lookup_provider.dart';
import 'package:test_sa/modules/incident_module/incident_provider.dart';
import 'package:test_sa/modules/loan_module/models/medical_department_model.dart';
import 'package:test_sa/modules/loan_module/provider/medical_department_provider.dart';
import 'package:test_sa/modules/traf_module/asset_auto_complete_field.dart';
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_text_form_field.dart';
@ -31,6 +33,7 @@ import 'package:test_sa/providers/gas_request_providers/site_provider.dart';
import 'package:test_sa/views/widgets/date_and_time/time_picker.dart';
import 'package:test_sa/providers/loading_list_notifier.dart';
import 'package:test_sa/providers/lookups/yes_no_lookup_provider.dart';
import 'package:test_sa/views/widgets/auto_complete/wo_auto_complete_field.dart';
import 'package:test_sa/views/widgets/date_and_time/date_picker.dart';
import 'package:test_sa/views/widgets/equipment/asset_picker.dart';
import 'package:test_sa/views/widgets/images/multi_image_picker.dart';
@ -64,6 +67,7 @@ class _CreateIncidentRequestPageState extends State<CreateIncidentRequestPage> {
Department? _selectedDepartment;
Asset? device;
WoByAssetIdModel? woByAssetId;
Lookup? incidentType;
Lookup? ovrSystem;
@ -128,10 +132,10 @@ class _CreateIncidentRequestPageState extends State<CreateIncidentRequestPage> {
showBorder: true,
onPick: (asset) async {
device = asset;
payload["assetId"] = asset.modelDefinition?.id;
payload["assetId"] = asset?.id;
payload["oracleCode"] = asset.modelDefinition?.oracleCodes?.first.codeValue;
payload["model"] = asset.modelDefinition?.modelName;
payload["manufacturer"] = asset.modelDefinition?.manufacturerId;
payload["manufacturer"] = asset.modelDefinition?.manufacturerName;
payload["assetOrigin"] = asset.modelDefinition?.assetName;
payload["siteId"] = asset.site?.id;
payload["buildingId"] = asset.building?.id;
@ -139,7 +143,19 @@ class _CreateIncidentRequestPageState extends State<CreateIncidentRequestPage> {
payload["departmentId"] = asset.department?.id;
setState(() {});
},
)
),
12.height,
WoAutoCompleteField(
clearAfterPick: false,
byName: true,
enabled: device != null,
assetId: device?.id?.toInt(),
initialValue: "",
onPick: (asset) {
woByAssetId = asset;
setState(() {});
},
),
],
if (incidentType?.value != null && incidentType?.value != 1) ...[
16.height,
@ -358,6 +374,7 @@ class _CreateIncidentRequestPageState extends State<CreateIncidentRequestPage> {
onSelect: (value) {
incidentType = value;
payload["incidentClassificationId"] = value?.id;
payload["workOrderId"] = null;
setState(() {});
},
),
@ -661,17 +678,19 @@ class _CreateIncidentRequestPageState extends State<CreateIncidentRequestPage> {
"Please scan or pick asset".showToast;
return;
}
if (incidentType?.value == 1 && device != null && woByAssetId?.id == null) {
"Please choose CM Work Order".showToast;
return;
}
if (ovrSystem?.value == 1 && isOvrVerified == false) {
"Please verify OVR Ticket Number".showToast;
return;
}
if (occurrenceDate == null) {
"Please select occurrence Data".showToast;
return;
}
payload["workOrderId"] = woByAssetId?.id;
payload["occurrenceDate"] = occurrenceDate!.toIso8601String();
_formKey.currentState!.save();

@ -74,6 +74,8 @@ class IncidentDataModel {
String? occurrenceDate;
String? createdDate;
List<IncidentAttachments>? incidentAttachments;
int? workOrderId;
String? workOrderNo;
IncidentDataModel(
{this.id,
@ -148,6 +150,8 @@ class IncidentDataModel {
this.approvalSignature,
this.occurrenceDate,
this.createdDate,
this.workOrderId,
this.workOrderNo,
this.incidentAttachments});
IncidentDataModel.fromJson(Map<String, dynamic> json) {
@ -223,6 +227,8 @@ class IncidentDataModel {
approvalSignature = json['approvalSignature'];
occurrenceDate = json['occurrenceDate'];
createdDate = json['createdDate'];
workOrderId = json['workOrderId'];
workOrderNo = json['workOrderNo'];
if (json['incidentAttachments'] != null) {
incidentAttachments = <IncidentAttachments>[];
json['incidentAttachments'].forEach((v) {
@ -305,6 +311,8 @@ class IncidentDataModel {
data['approvalSignature'] = this.approvalSignature;
data['occurrenceDate'] = this.occurrenceDate;
data['createdDate'] = this.createdDate;
data['workOrderId'] = this.workOrderId;
data['workOrderNo'] = this.workOrderNo;
if (this.incidentAttachments != null) {
data['incidentAttachments'] = this.incidentAttachments!.map((v) => v.toJson()).toList();
}

@ -1,3 +1,6 @@
import 'dart:developer';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/api_routes/urls.dart';
@ -6,6 +9,8 @@ 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/modules/cm_module/cm_detail_page.dart';
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart';
import 'package:test_sa/modules/incident_module/incident_attachment_model.dart';
import 'package:test_sa/modules/incident_module/incident_data_model.dart';
import 'package:test_sa/modules/incident_module/incident_provider.dart';
@ -88,6 +93,18 @@ class IncidentDetailPage extends StatelessWidget {
InfoTextWidget(label: 'Floor', value: incidentData.floorName ?? "-"),
InfoTextWidget(label: 'Department', value: incidentData.departmentName ?? "-"),
InfoTextWidget(label: 'Occurrence Date', value: incidentData.occurrenceDate?.toServiceRequestDetailsFormat ?? "-"),
if (incidentData.workOrderId != null)
RichText(
text: TextSpan(text: "Work Order: ", style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral20), children: [
TextSpan(
text: "${incidentData.workOrderNo}",
style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context), decoration: TextDecoration.underline),
recognizer: TapGestureRecognizer()
..onTap = () async {
await Navigator.of(context).push(MaterialPageRoute(builder: (_) => CMDetailPage(requestId: incidentData.workOrderId!, moduleId: 3)));
}),
]),
),
InfoTextWidget(label: 'Root Cause', value: incidentData.rootCauseName ?? "-"),
if (incidentData.rootCauseValue == 5) InfoTextWidget(label: 'Root reason', value: incidentData.otherRootCause ?? "-"),
InfoTextWidget(label: 'Comments', value: incidentData.comments ?? "-"),

@ -1,3 +1,7 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/providers/api/all_requests_provider.dart';
@ -6,6 +10,8 @@ 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/generic_attachment_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/medical_gas_inspection/models/delivery_notes_form_model.dart';
import 'package:test_sa/modules/medical_gas_inspection/models/medical_gas_data_model.dart';
@ -38,9 +44,7 @@ class _UpdateDeliveryNotesState extends State<UpdateDeliveryNotes> {
bool pressureTestAcknowledgement = false;
bool leakTestAcknowledgement = false;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
// List<MedicalGasRequestDetailModel> selectedItemList = [];
List<GenericAttachmentModel> _attachments = [];
@override
void initState() {
@ -63,9 +67,13 @@ class _UpdateDeliveryNotesState extends State<UpdateDeliveryNotes> {
isInspectedGasLeak: dataModel.isInspectedGasLeak,
isPressureChecked: dataModel.isPressureChecked,
deliveredDate: dataModel.deliveredDate,
attachments: dataModel.deliveryNoteAttachmentDto,
// attachments: dataModel.deliveryNoteAttachmentDto,
isSubmitted: dataModel.isSubmitted,
deliveryNoteNumber: dataModel.deliveryNote);
if (dataModel.deliveryNoteAttachmentDto.isNotEmpty) {
_attachments.addAll(dataModel.deliveryNoteAttachmentDto.map((e) => GenericAttachmentModel(id: e.id, name: e.name)).toList());
}
//Need to Confirm this condition
// selectedItemList = dataModel.requestDetailDtos.where((item) => item.deliveredQuantity != null && item.deliveredQuantity! > 0).toList();
setState(() {});
@ -202,7 +210,7 @@ class _UpdateDeliveryNotesState extends State<UpdateDeliveryNotes> {
AttachmentPicker(
label: 'Upload Attachment',
attachment: formModel.attachments,
attachment: _attachments,
buttonColor: AppColor.primary10,
// showAsListView: true,
onlyImages: false,
@ -319,6 +327,15 @@ class _UpdateDeliveryNotesState extends State<UpdateDeliveryNotes> {
deliveredQuantity: item.deliveredQuantity,
));
}
formModel.attachments = [];
try {
for (var item in _attachments) {
String fileName = CMRequestUtils.isLocalUrl(item.name ?? '') ? ("${item.name ?? ''.split("/").last}|${base64Encode(File(item.name ?? '').readAsBytesSync())}") : item.name ?? '';
formModel.attachments.add(GenericAttachmentModel(id: item.id, name: fileName));
}
} catch (error) {
print(error);
}
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
await medicalGasInspectionProvider.updatedDeliveryNotes(model: formModel).then((status) async {
Navigator.pop(context);

@ -125,8 +125,8 @@ class _TrafRequestDetailPageState extends State<TrafRequestDetailPage> {
4.height,
'Requested Quantity: ${data.qty ?? '-'}'.bodyText(context), //todo @waseem replace with infotext
],
InfoTextWidget(label: 'How would the requested technology solve the current situation and/or serve the purpose?', value: data.purposeAnswer ?? '-'),
InfoTextWidget(label: 'What is the current practice?', value: data.currentPractise ?? '-'),
InfoTextWidget(label: 'How would the requested technology solve the current situation and/or serve the purpose?', value: "\n" + data.purposeAnswer ?? '-'),
InfoTextWidget(label: 'What is the current practice?', value: "\n" + data?.currentPractise ?? '-'),
InfoTextWidget(label: 'Census Q1', value: data.censusQ1 ?? '-'),
InfoTextWidget(label: 'Census Q2', value: data.censusQ2 ?? '-'),
InfoTextWidget(label: 'Census Q3', value: data.censusQ3 ?? '-'),
@ -136,15 +136,15 @@ class _TrafRequestDetailPageState extends State<TrafRequestDetailPage> {
value: "\n${data.trafContacts?.map((item) => item.name).toList().toString() ?? '-'}"),
InfoTextWidget(label: 'Is the requesting department going to use the technology solely or shared with other departments?', value: "\n${data.usingSolelyOrSharedName ?? '-'}"),
if ((data.departments ?? []).isNotEmpty) ...[
InfoTextWidget(label: 'Technology used with other departments', value: data.departments?.map((item) => item.departmentName).toList().toString() ?? '-'),
InfoTextWidget(label: 'Technology used with other departments', value:"\n" + data.departments?.map((item) => item.departmentName).toList().toString() ?? '-'),
],
InfoTextWidget(label: 'Would other services be effected by acquiring the new equipment?', value: data.isEffectedName ?? '-'),
InfoTextWidget(label: 'Would other services be effected by acquiring the new equipment?', value: "\n" +data.isEffectedName ?? '-'),
if ((data.effectedServices ?? "").isNotEmpty) ...[
InfoTextWidget(label: 'List of services would it be effected', value: data.effectedServices ?? '-'),
InfoTextWidget(label: 'List of services would it be effected', value:"\n" + data.effectedServices ?? '-'),
],
InfoTextWidget(label: 'Is the equipment going to be used with combination of other equipment to accomplish a specific procedure?', value: data.isCombinationName ?? '-'),
InfoTextWidget(label: 'Is the equipment going to be used with combination of other equipment to accomplish a specific procedure?', value:"\n" + data.isCombinationName ?? '-'),
if ((data.usedWithCombination ?? "").isNotEmpty) ...[
InfoTextWidget(label: 'Equipment going to be used with combination of other equipments', value: data.usedWithCombination ?? '-'),
InfoTextWidget(label: 'Equipment going to be used with combination of other equipments', value:"\n" + data.usedWithCombination ?? '-'),
],
if (_attachments.isNotEmpty) ...[
4.height,

@ -0,0 +1,136 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/providers/api/oracle_code_provider.dart';
import 'package:test_sa/controllers/providers/api/parts_provider.dart';
import 'package:test_sa/controllers/providers/api/user_provider.dart';
import 'package:test_sa/controllers/providers/settings/setting_provider.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/models/new_models/asset_nd_auto_complete_by_dynamic_codes_model.dart';
import 'package:test_sa/models/wo_by_asset_id_model.dart';
import 'package:test_sa/modules/cm_module/cm_detail_provider.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/views/app_style/sizing.dart';
import '../../../extensions/text_extensions.dart';
import '../../../models/service_request/spare_parts.dart';
import '../../../new_views/app_style/app_text_style.dart';
class WoAutoCompleteField extends StatefulWidget {
final String initialValue;
final int? assetId;
final bool clearAfterPick, byName, enabled;
final Function(WoByAssetIdModel) onPick;
const WoAutoCompleteField({Key? key, required this.byName, required this.initialValue, this.assetId, required this.onPick, this.clearAfterPick = true, this.enabled = true}) : super(key: key);
@override
_WoAutoCompleteFieldState createState() => _WoAutoCompleteFieldState();
}
class _WoAutoCompleteFieldState extends State<WoAutoCompleteField> {
late CMDetailProvider _cmDetailProvider;
late TextEditingController _controller;
bool loading = false;
@override
void initState() {
_controller = TextEditingController(text: widget.initialValue);
super.initState();
_cmDetailProvider = Provider.of<CMDetailProvider>(context, listen: false);
}
@override
void didUpdateWidget(covariant WoAutoCompleteField oldWidget) {
if (widget.initialValue != oldWidget.initialValue) {
_controller = TextEditingController(text: widget.initialValue);
}
super.didUpdateWidget(oldWidget);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final border = UnderlineInputBorder(borderSide: BorderSide.none, borderRadius: BorderRadius.circular(10));
return Container(
decoration: BoxDecoration(
color: AppColor.background(context),
borderRadius: BorderRadius.circular(AppStyle.borderRadius * AppStyle.getScaleFactor(context)),
// boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)],
),
child: Autocomplete<WoByAssetIdModel>(
optionsBuilder: (TextEditingValue textEditingValue) async {
if (textEditingValue.text.isEmpty) {
if (loading) {
setState(() {
loading = false;
});
}
return const Iterable<WoByAssetIdModel>.empty();
}
if (!loading) {
setState(() {
loading = true;
});
}
List<WoByAssetIdModel> workOrders = (await _cmDetailProvider.getWorkOrderByAssetId(textEditingValue.text, widget.assetId!));
setState(() {
loading = false;
});
return workOrders;
},
displayStringForOption: (WoByAssetIdModel option) => widget.byName ? option.workOrderNo ?? "" : option.workOrderNo ?? "",
fieldViewBuilder: (BuildContext context, TextEditingController fieldTextEditingController, FocusNode fieldFocusNode, VoidCallback onFieldSubmitted) {
return TextField(
controller: _controller,
focusNode: fieldFocusNode,
style: AppTextStyles.bodyText.copyWith(color: AppColor.black10),
textAlign: TextAlign.start,
decoration: InputDecoration(
border: border,
disabledBorder: border,
focusedBorder: border,
enabledBorder: border,
errorBorder: border,
contentPadding: EdgeInsets.symmetric(vertical: 8.toScreenHeight, horizontal: 16.toScreenWidth),
constraints: const BoxConstraints(),
suffixIconConstraints: const BoxConstraints(maxHeight: 24, maxWidth: 24 + 8),
filled: true,
enabled: widget.enabled,
fillColor: AppColor.fieldBgColor(context),
errorStyle: AppTextStyle.tiny.copyWith(color: context.isDark ? AppColor.red50 : AppColor.red60),
floatingLabelStyle: AppTextStyle.body1.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? null : AppColor.neutral20),
labelText: "CM Work Order Number",
labelStyle: AppTextStyles.tinyFont.copyWith(color: AppColor.textColor(context)),
suffixIcon: loading ? const CircularProgressIndicator(color: AppColor.primary10, strokeWidth: 3.0).paddingOnly(end: 8) : null,
),
textInputAction: TextInputAction.search,
onChanged: (text) {
fieldTextEditingController.text = text;
},
onSubmitted: (String value) {
onFieldSubmitted();
},
);
},
onSelected: (WoByAssetIdModel selection) {
if (widget.clearAfterPick) {
_controller.clear();
} else {
_controller.text = widget.byName ? (selection.workOrderNo ?? "") : (selection.workOrderNo ?? "");
}
widget.onPick(selection);
},
),
);
}
}

@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.7.7+42
version: 1.7.8+44
environment:
sdk: ">=3.5.0 <4.0.0"

Loading…
Cancel
Save