Merge branch 'design_3.0_incident_module' into design_3.0_demo_module

# Conflicts:
#	lib/controllers/api_routes/urls.dart
#	lib/main.dart
#	lib/new_views/common_widgets/single_item_drop_down_menu.dart
#	lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart
#	lib/new_views/pages/land_page/my_request/my_requests_page.dart
#	lib/new_views/pages/land_page/requests/request_paginated_listview.dart
#	lib/new_views/pages/land_page/widgets/request_item_view_list.dart
design_3.0_demo_module
Sikander Saleem 20 hours ago
commit 65c0566eef

@ -4,12 +4,11 @@ class URLs {
static const String appReleaseBuildNumber = "33";
// static const host1 = "https://atomsm.hmg.com"; // production url
static const host1 = "https://atomsmdev.hmg.com"; // local DEV url
// static const host1 = "https://atomsmuat.hmg.com"; // local UAT url
// static const host1 = "https://atomsmdev.hmg.com"; // local DEV url
static const host1 = "https://atomsmuat.hmg.com"; // local UAT url
// static final String _baseUrl = "$_host/mobile"; // host local UAT
static final String _baseUrl = "$_host/v2/mobile"; // new V2 apis
// static final String _baseUrl = "$_host/mobile"; // host local UAT and for internal audit dev
// static final String _baseUrl = "$_host/v3/mobile"; // v3 for production CM,PM,TM
// static final String _baseUrl = "$_host/v4/mobile"; // v4 for Demo module
// static final String _baseUrl = "$_host/v5/mobile"; // v5 for data segregation
@ -257,6 +256,12 @@ class URLs {
static get getYesNoRequestTypeLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=4";
static get getDemoPeriodLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=4101";
static get incidentStatusLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=3060";
static get incidentTypeLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=3061";
static get incidentRootCauseLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=3062";
static get incidentPersonInvolvedLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=3063";
static get clinicalNonClinicalLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=3064";
static get genderLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=3065";
static get getDemoDocumentTypes => "$_baseUrl/Lookups/GetLookup?lookupEnum=4106";
@ -408,11 +413,15 @@ class URLs {
static get getLoanPeriod => "$_baseUrl/Lookups/GetLookup?lookupEnum=3051";
static get getMedDepartmentBasedOnSite => "$_baseUrl/TRAFDataSource/GetDepartmentBasedOnSite";
static get getFlowMedicalDepartments => "$_baseUrl/FlowDepartments/All";
static get getLoanById => '$_baseUrl/Loan/GetLoanById';
static get getLoanByWorkOrderId => '$_baseUrl/Loan/GetLoanByWorkOrderId';
static get getIncidentById => '$_baseUrl/Incident/GetIncidentById';
static get getOvrTicketDetails => '$_baseUrl/Incident/GetOvrTicketDetails';
static get addLoan => '$_baseUrl/Loan/AddLoan';
static get addIncident => '$_baseUrl/Incident/AddIncident';
static get addDemoTrialOutcome => '$_baseUrl/DemoRequest/addDemoTrialOutcome';

@ -172,6 +172,10 @@ class AllRequestsProvider extends ChangeNotifier {
}
// loan module
if (context.settingProvider.isUserFlowMedical && !context.userProvider.isNurse) {
list = [16];
}
return list;
}

@ -4,6 +4,7 @@ import 'package:provider/provider.dart';
import 'package:test_sa/controllers/providers/api/user_provider.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/new_views/swipe_module/dialoge/confirm_dialog.dart';
import 'package:test_sa/new_views/swipe_module/dialoge/info_dialog.dart';
import '../controllers/providers/settings/setting_provider.dart';
@ -36,6 +37,11 @@ extension BuildContextExtension on BuildContext {
builder: (BuildContext cxt) => ConfirmDialog(message: message, onTap: onTap, title: title, okTitle: okTitle),
);
void showInfoDialog(String message, {String? title, VoidCallback? onTap, String? okTitle, List<InfoContent> content = const []}) => showDialog(
context: this,
builder: (BuildContext cxt) => InfoDialog(message: message, onTap: onTap, title: title, okTitle: okTitle, content: content),
);
Future showBottomSheet(Widget childWidget, {bool? isDismissible, String? title}) => showModalBottomSheet(
context: this,
useSafeArea: true,

@ -40,6 +40,8 @@ import 'package:test_sa/modules/cx_module/chat/chat_provider.dart';
import 'package:test_sa/modules/demo_module/create_demo_request_page.dart';
import 'package:test_sa/modules/demo_module/provider/demo_period_lookup_provider.dart';
import 'package:test_sa/modules/demo_module/provider/demo_provider.dart';
import 'package:test_sa/modules/incident_module/flow_medical_department_provider.dart';
import 'package:test_sa/modules/incident_module/incident_provider.dart';
import 'package:test_sa/modules/internal_audit_module/pages/equipment_internal_audit/create_equipment_internal_audit_form.dart';
import 'package:test_sa/modules/internal_audit_module/pages/system_internal_audit/create_system_internal_audit_form.dart';
import 'package:test_sa/modules/internal_audit_module/pages/system_internal_audit/update_system_internal_audit_page.dart';
@ -120,6 +122,8 @@ import 'controllers/providers/api/user_provider.dart';
import 'controllers/providers/settings/setting_provider.dart';
import 'dashboard_latest/dashboard_provider.dart';
import 'modules/demo_module/demo_document_lookup_provider.dart';
import 'modules/incident_module/create_incident_request_page.dart';
import 'modules/incident_module/incident_lookup_provider.dart';
import 'modules/internal_audit_module/pages/equipment_internal_audit/update_equipment_internal_audit_page.dart';
import 'modules/internal_audit_module/provider/internal_audit_checklist_provider.dart';
import 'modules/cx_module/survey/survey_provider.dart';
@ -332,6 +336,7 @@ class MyApp extends StatelessWidget {
ChangeNotifierProvider(create: (_) => LoanProvider()),
ChangeNotifierProvider(create: (_) => LoanPeriodProvider()),
ChangeNotifierProvider(create: (_) => MedicalDepartmentProvider()),
ChangeNotifierProvider(create: (_) => FlowMedicalDepartmentProvider()),
ChangeNotifierProvider(create: (_) => AssetDeliveryProvider()),
ChangeNotifierProvider(create: (_) => CostCenterProvider()),
ChangeNotifierProvider(create: (_) => PowerSupplyTypeProvider()),
@ -345,6 +350,13 @@ class MyApp extends StatelessWidget {
ChangeNotifierProvider(create: (_) => DemoProvider()),
ChangeNotifierProvider(create: (_) => DemoPeriodLookupProvider()),
ChangeNotifierProvider(create: (_) => DemoDocumentLookupProvider()),
ChangeNotifierProvider(create: (_) => IncidentStatusLookupProvider()),
ChangeNotifierProvider(create: (_) => IncidentTypeLookupProvider()),
ChangeNotifierProvider(create: (_) => IncidentRootCauseLookupProvider()),
ChangeNotifierProvider(create: (_) => IncidentPersonInvolvedLookupProvider()),
ChangeNotifierProvider(create: (_) => ClinicalNonClinicalLookupProvider()),
ChangeNotifierProvider(create: (_) => GenderLookupProvider()),
ChangeNotifierProvider(create: (_) => IncidentProvider()),
],
child: GestureDetector(
onTap: () {
@ -400,7 +412,7 @@ class MyApp extends StatelessWidget {
AssetDetailPage.id: (_) => const AssetDetailPage(),
DeviceSearchScreen.id: (_) => const DeviceSearchScreen(),
DeviceFilterScreen.id: (_) => const DeviceFilterScreen(),
// WorkOrderDetailsPage.id: (_) => const WorkOrderDetailsPage(),
CreateIncidentRequestPage.id: (_) => CreateIncidentRequestPage(),
// UpdateServiceRequestPage.id: (_) => const UpdateServiceRequestPage(),
SettingsPage.id: (_) => const SettingsPage(),

@ -14,7 +14,7 @@ class AssetByIdModel {
AssetReplace? isParent;
AssetInfo? parentAsset;
AssetReplace? assetType;
String? demoRequest;
DemoRequest? demoRequest;
String? missionCritical;
Site? site;
AssetReplace? building;
@ -143,7 +143,7 @@ class AssetByIdModel {
isParent = json['isParent'] != null ? AssetReplace.fromJson(json['isParent']) : null;
parentAsset = json['parentAsset'] != null ? AssetInfo.fromJson(json['parentAsset']) : null;
assetType = json['assetType'] != null ? AssetReplace.fromJson(json['assetType']) : null;
demoRequest = json['demoRequest'];
demoRequest = json['demoRequest'] != null ? DemoRequest.fromJson(json['demoRequest']) : null;
missionCritical = json['missionCritical'];
site = json['site'] != null ? Site.fromJson(json['site']) : null;
building = json['building'] != null ? AssetReplace.fromJson(json['building']) : null;
@ -229,7 +229,7 @@ class AssetByIdModel {
if (assetType != null) {
data['assetType'] = assetType?.toJson();
}
data['demoRequest'] = demoRequest;
data['demoRequest'] = demoRequest?.toJson();
data['missionCritical'] = missionCritical;
if (site != null) {
data['site'] = site?.toJson();
@ -706,3 +706,22 @@ class AssetInfo {
return map;
}
}
class DemoRequest {
int? id;
String? label;
DemoRequest({this.id, this.label});
DemoRequest.fromJson(Map<String, dynamic> json) {
id = json['id'];
label = json['label'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['label'] = label;
return data;
}
}

@ -37,6 +37,13 @@ class ModelDefinition {
modelDefRelatedDefects!.add(ModelDefRelatedDefects.fromJson(v)); // Use '!' since modelDefRelatedDefects is initialized here
});
}
if (json['oracleCodes'] != null) {
oracleCodes = [];
json['oracleCodes'].forEach((v) {
oracleCodes!.add(OracleCode.fromJson(v)); // Use '!' since modelDefRelatedDefects is initialized here
});
}
if (json['suppliers'] != null) {
suppliers = [];
json['suppliers'].forEach((v) {
@ -59,6 +66,7 @@ class ModelDefinition {
num? lifeSpan; // Now nullable
List<ModelDefRelatedDefects>? modelDefRelatedDefects; // Now nullable
List<Supplier>? suppliers; // Now nullable
List<OracleCode>? oracleCodes; // Now nullable
// ... (copyWith method remains the same, just with nullable parameters)
@ -108,3 +116,34 @@ class ModelDefRelatedDefects {
return map;
}
}
class OracleCode {
int? id;
int? assetNDId;
int? codeTypeId;
String? codeTypeName;
int? codeTypeValue;
String? codeValue;
OracleCode({this.id, this.assetNDId, this.codeTypeId, this.codeTypeName, this.codeTypeValue, this.codeValue});
OracleCode.fromJson(Map<String, dynamic> json) {
id = json['id'];
assetNDId = json['assetNDId'];
codeTypeId = json['codeTypeId'];
codeTypeName = json['codeTypeName'];
codeTypeValue = json['codeTypeValue'];
codeValue = json['codeValue'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['assetNDId'] = this.assetNDId;
data['codeTypeId'] = this.codeTypeId;
data['codeTypeName'] = this.codeTypeName;
data['codeTypeValue'] = this.codeTypeValue;
data['codeValue'] = this.codeValue;
return data;
}
}

@ -0,0 +1,710 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/validator/validator.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/helper/utils.dart';
import 'package:test_sa/models/device/asset.dart';
import 'package:test_sa/models/generic_attachment_model.dart';
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/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/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';
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/providers/gas_request_providers/site_provider.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/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';
import '../../models/new_models/department.dart';
import '../../new_views/swipe_module/dialoge/info_dialog.dart';
import 'flow_medical_department_provider.dart';
import 'incident_attachment_model.dart';
class CreateIncidentRequestPage extends StatefulWidget {
static const String id = "/create-incident";
CreateIncidentRequestPage({Key? key}) : super(key: key);
@override
_CreateIncidentRequestPageState createState() {
return _CreateIncidentRequestPageState();
}
}
class _CreateIncidentRequestPageState extends State<CreateIncidentRequestPage> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
List<GenericAttachmentModel> attachments = [];
MedicalDepartmentModel? _medicalDepartmentModel;
Site? _empSite;
Site? _selectedSite;
Building? _selectedBuilding;
Floor? _selectedFloor;
Department? _selectedDepartment;
Asset? device;
Lookup? incidentType;
Lookup? ovrSystem;
Lookup? rootCause;
Lookup? involvedPerson;
Lookup? gender;
DateTime? occurrenceDate;
Map<String, dynamic> payload = {};
Map<String, dynamic>? ovrDetails;
bool get isOvrVerified => ovrDetails != null;
bool ovrVerifying = false;
String ovrTicketNumber = "";
TextEditingController ovrTicketController = TextEditingController();
@override
void initState() {
super.initState();
ovrSystem = Lookup(id: 81, name: "No", value: 0);
payload["id"] = 0;
payload["isFromMobile"] = true;
payload["requestorUserId"] = context.userProvider.user!.userID;
payload["requestorSiteId"] = context.userProvider.user!.clientId;
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: DefaultAppBar(title: 'Incident'.addTranslation),
body: Form(
key: _formKey,
child: Column(
children: [
SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
...empSiteSection(),
16.height,
...incidentInfoSection(),
if (incidentType?.value == 1) ...[
16.height,
"Asset Details".bodyText(context).custom(color: AppColor.black10),
8.height,
AssetPicker(
device: device,
showLoading: false,
borderColor: AppColor.black20,
onPick: (asset) async {
device = asset;
payload["assetId"] = asset.modelDefinition?.id;
payload["oracleCode"] = asset.modelDefinition?.oracleCodes?.first.codeValue;
payload["model"] = asset.modelDefinition?.modelName;
payload["manufacturer"] = asset.modelDefinition?.manufacturerId;
payload["assetOrigin"] = asset.modelDefinition?.assetName;
payload["siteId"] = asset.site?.id;
payload["buildingId"] = asset.building?.id;
payload["floorId"] = asset.floor?.id;
payload["departmentId"] = asset.department?.id;
setState(() {});
},
)
],
if (incidentType?.value != null && incidentType?.value != 1) ...[
16.height,
...siteSection(),
],
16.height,
...detailsSection(),
16.height,
...optionQuestions(),
16.height,
"Attachments".bodyText(context).custom(color: AppColor.black10),
8.height,
AttachmentPicker(
label: context.translation.attachments,
attachment: attachments,
buttonColor: AppColor.black10,
onlyImages: false,
showAsListView: true,
buttonIcon: 'image-plus'.toSvgAsset(color: context.isDark ? AppColor.primary10 : AppColor.neutral120),
onChange: (_attachments) {
attachments = _attachments;
setState(() {});
},
),
],
).toShadowContainer(context, borderRadius: 20),
).expanded,
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
buttonColor: AppColor.primary10,
label: context.translation.submitRequest,
onPressed: _submit,
// buttonColor: AppColor.primary10,
),
),
],
),
),
);
}
List<Widget> optionQuestions() {
return [
'Additional Information & Comments'.addTranslation.bodyText(context).custom(color: AppColor.black10),
8.height,
AppTextFormField(
initialValue: "",
labelText: "Some Comments",
style: TextStyle(fontWeight: FontWeight.w500, fontSize: context.isTablet() ? 16 : 12, color: context.isDark ? Colors.white : const Color(0xff3B3D4A)),
labelStyle: TextStyle(fontWeight: FontWeight.w500, fontSize: context.isTablet() ? 16 : 12, color: context.isDark ? Colors.white : const Color(0xff767676)),
floatingLabelStyle: TextStyle(fontWeight: FontWeight.w500, fontSize: context.isTablet() ? 16 : 12, color: context.isDark ? Colors.white : const Color(0xff3B3D4A)),
backgroundColor: AppColor.fieldBgColor(context),
showShadow: false,
textInputType: TextInputType.multiline,
makeMultiLinesNull: false,
onChange: (value) {
payload["comments"] = value;
},
),
];
}
List<Widget> siteSection() {
return [
'Site Details'.addTranslation.bodyText(context).custom(color: AppColor.black10),
8.height,
SingleItemDropDownMenu<Site, SiteProvider>(
context: context,
title: context.translation.site,
initialValue: _selectedSite,
showShadow: false,
validator: (value) {
if (value == null) return "Please select a site";
return null;
},
backgroundColor: AppColor.fieldBgColor(context),
showAsBottomSheet: true,
onSelect: (value) {
_selectedSite = value;
payload["siteId"] = value?.id;
setState(() {});
},
),
8.height,
SingleItemDropDownMenu<Building, NullableLoadingProvider>(
context: context,
title: context.translation.building,
initialValue: _selectedBuilding,
showShadow: false,
showAsBottomSheet: true,
validator: (value) {
if (value == null) return "Please select a building";
return null;
},
backgroundColor: AppColor.fieldBgColor(context),
enabled: _selectedSite?.buildings?.isNotEmpty ?? false,
staticData: _selectedSite?.buildings ?? [],
onSelect: (value) {
_selectedBuilding = value;
_selectedFloor = null;
_selectedDepartment = null;
payload["buildingId"] = value?.id;
setState(() {});
},
),
8.height,
SingleItemDropDownMenu<Floor, NullableLoadingProvider>(
context: context,
title: context.translation.floor,
showShadow: false,
showAsBottomSheet: true,
initialValue: _selectedFloor,
backgroundColor: AppColor.fieldBgColor(context),
enabled: _selectedBuilding?.floors?.isNotEmpty ?? false,
staticData: _selectedBuilding?.floors ?? [],
validator: (value) {
if (value == null) return "Please select a floor";
return null;
},
onSelect: (value) {
_selectedFloor = value;
_selectedDepartment = null;
payload["floorId"] = value?.id;
setState(() {});
},
),
8.height,
SingleItemDropDownMenu<Department, NullableLoadingProvider>(
context: context,
title: context.translation.department,
showShadow: false,
showAsBottomSheet: true,
initialValue: _selectedDepartment,
backgroundColor: AppColor.fieldBgColor(context),
enabled: _selectedFloor?.departments?.isNotEmpty ?? false,
staticData: _selectedFloor?.departments ?? [],
validator: (value) {
if (value == null) return "Please select a department";
return null;
},
onSelect: (value) {
_selectedDepartment = value;
payload["departmentId"] = value?.id;
setState(() {});
},
),
];
}
List<Widget> empSiteSection() {
return [
SingleItemDropDownMenu<Site, SiteProvider>(
context: context,
title: "Employee Site",
initialValue: _empSite,
showShadow: false,
validator: (value) {
if (value == null) return "Please select a site";
return null;
},
backgroundColor: AppColor.fieldBgColor(context),
showAsBottomSheet: true,
onSelect: (value) {
_empSite = value;
payload["requestorSiteId"] = value?.id;
setState(() {});
},
),
8.height,
SingleItemDropDownMenu<MedicalDepartmentModel, FlowMedicalDepartmentProvider>(
context: context,
title: "Employee Department",
showShadow: false,
validator: (value) {
if (value == null) return "Please select a department";
return null;
},
showAsBottomSheet: true,
initialValue: _medicalDepartmentModel,
requestById: context.userProvider.user?.clientId,
backgroundColor: AppColor.fieldBgColor(context),
onSelect: (value) {
_medicalDepartmentModel = value;
payload["requestorFlowDepartmentId"] = value?.id;
setState(() {});
},
),
];
}
List<Widget> incidentInfoSection() {
return [
"Incident Details".bodyText(context).custom(color: AppColor.black10),
8.height,
SingleItemDropDownMenu<Lookup, IncidentTypeLookupProvider>(
context: context,
height: 56.toScreenHeight,
title: "Incident Type",
initialValue: incidentType,
showShadow: false,
validator: (value) {
if (value == null) return "Mandatory";
return null;
},
infoCallback: () {
context.showInfoDialog("Select the category that best describes the incident:", content: [
InfoContent(title: "Medical Equipment", message: "Malfunction, failure, or issue involving a device."),
InfoContent(title: "EHS (Environment, Health, Safety)", message: "Incidents related to safety hazards, spills, injuries."),
InfoContent(title: "Late Delivery", message: "Delayed receipt of medical equipment, materials, or supplies."),
InfoContent(title: "Mis-conduct", message: "FLOW staff behavior that violates rules, ethics, or procedures."),
]);
},
backgroundColor: AppColor.fieldBgColor(context),
showAsBottomSheet: true,
onSelect: (value) {
incidentType = value;
payload["incidentClassificationId"] = value?.id;
setState(() {});
},
),
8.height,
SingleItemDropDownMenu<Lookup, YesNoLookupProvider>(
context: context,
height: 56.toScreenHeight,
title: "Is there any existing OVR System?",
initialValue: ovrSystem,
showShadow: false,
validator: (value) {
if (value == null) return "Mandatory";
return null;
},
backgroundColor: AppColor.fieldBgColor(context),
showAsBottomSheet: true,
onSelect: (value) {
ovrSystem = value;
payload["existingOvrTicketId"] = value?.id;
setState(() {});
},
),
if (ovrSystem?.value == 1) ...[
8.height,
AppTextFormField(
initialValue: "",
controller: ovrTicketController,
labelText: "OVR Ticket Number",
validator: (value) {
if ((value ?? "").isEmpty) return "Mandatory";
return null;
},
style: Theme.of(context).textTheme.titleMedium,
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
showShadow: false,
suffixIcon: Row(
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
onPressed: () {
if (ovrVerifying || isOvrVerified) return;
if (ovrTicketNumber.isEmpty) {
"Please enter OVR ticket number".showToast;
return;
}
ovrVerifying = true;
setState(() {});
context.read<IncidentProvider>().getOvrTicketDetails(ovrTicketNumber).then((details) {
ovrDetails = details;
if (ovrDetails != null) {
payload["ovrTicketNumber"] = ovrTicketNumber;
"OVR Ticket verified successfully".showToast;
}
}).whenComplete(() {
ovrVerifying = false;
setState(() {});
});
},
child: ovrVerifying
? SizedBox(
width: 16.toScreenHeight,
height: 16.toScreenHeight,
child: const CircularProgressIndicator(color: AppColor.primary10, strokeWidth: 2),
)
: isOvrVerified
? const Row(
mainAxisSize: MainAxisSize.min,
children: [Icon(Icons.done), Text("Verified")],
)
: const Text("Verify"),
),
if (isOvrVerified)
const Icon(Icons.clear, color: AppColor.red30).paddingOnly(end: 8).onPress(() {
ovrDetails = null;
ovrTicketNumber = "";
ovrTicketController.clear();
payload.remove("ovrTicketNumber");
setState(() {});
}),
],
),
textInputType: TextInputType.name,
textInputAction: TextInputAction.next,
onChange: (value) {
ovrTicketNumber = value;
},
),
],
8.height,
AppTextFormField(
initialValue: "",
labelText: "Incident Title",
validator: (value) {
if ((value ?? "").isEmpty) return "Mandatory";
return null;
},
suffixIcon: const Icon(Icons.info_outline_rounded, color: AppColor.neutral120).paddingOnly(end: 8).onPress(() {
context.showInfoDialog("Brief explanation of the incident, e.g., “Damaged patient monitor screen“");
}),
style: Theme.of(context).textTheme.titleMedium,
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
showShadow: false,
textInputType: TextInputType.name,
textInputAction: TextInputAction.next,
onChange: (value) {
payload["incidentTitle"] = value;
},
),
8.height,
AppTextFormField(
initialValue: "",
labelText: "Incident Description",
validator: (value) {
if ((value ?? "").isEmpty) return "Mandatory";
return null;
},
suffixIcon: const Icon(Icons.info_outline_rounded, color: AppColor.neutral120).paddingOnly(end: 8).onPress(() {
context.showInfoDialog(
"Provide a clear and factual description of the incident, including how the issue was identified. Ex. “The ECG cable connector was damaged due to continuos forcfull disconnetionnby end-users. Multiple bent pins were noted during inspection after repeated complains of poor signal quality“");
}),
style: Theme.of(context).textTheme.titleMedium,
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
showShadow: false,
textInputType: TextInputType.name,
textInputAction: TextInputAction.next,
onChange: (value) {
payload["incidentDescription"] = value;
},
),
];
}
List<Widget> detailsSection() {
return [
'Details'.addTranslation.bodyText(context).custom(color: AppColor.black10),
8.height,
SingleItemDropDownMenu<Lookup, IncidentRootCauseLookupProvider>(
context: context,
height: 56.toScreenHeight,
title: "Root Cause",
initialValue: rootCause,
showShadow: false,
validator: (value) {
if (value == null) return "Mandatory";
return null;
},
infoCallback: () {
context.showInfoDialog("Select the primary cause that best explains why the incident occurred. If “Other“ is selected, specify the details in the text box provided.", content: [
InfoContent(
title: "Misuse",
message: "Clear, improper use of the device by the end-user despite available knowledge or instructions. Misuse implies that correct handling was possible but not followed."),
InfoContent(title: "Lack of Awareness", message: "End-user was unaware of proper handling or procedures due to missing information or communication."),
InfoContent(title: "Insufficient Training", message: "Formal training on the device was not provided, incomplete, or outdated, leading to misuse."),
InfoContent(
title: "Utility Failure",
message: "A utility issue that disrupted the medical device operation, degrade, performance, or create unsafe conditions. such as (power outage, HVAC, or environment)."),
InfoContent(title: "Other", message: "Any cause that does not fit into the categories above (please specify)."),
]);
},
backgroundColor: AppColor.fieldBgColor(context),
showAsBottomSheet: true,
onSelect: (value) {
rootCause = value;
payload["rootCauseId"] = value?.id;
setState(() {});
},
),
if (rootCause?.value == 5) ...[
8.height,
AppTextFormField(
initialValue: "",
labelText: "Root Cause (Other)",
validator: (value) {
if ((value ?? "").isEmpty) return "Mandatory";
return null;
},
style: Theme.of(context).textTheme.titleMedium,
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
showShadow: false,
textInputType: TextInputType.name,
textInputAction: TextInputAction.next,
onChange: (value) {
payload["otherRootCause"] = value;
},
),
],
8.height,
SingleItemDropDownMenu<Lookup, IncidentPersonInvolvedLookupProvider>(
context: context,
height: 56.toScreenHeight,
title: "Person Involved",
initialValue: involvedPerson,
showShadow: false,
validator: (value) {
if (value == null) return "Mandatory";
return null;
},
infoCallback: () {
context.showInfoDialog("Ensure to provide information of person in Charge (e.g., Charge Nurse, Unit Team Leader), not a general staff member.", content: []);
},
backgroundColor: AppColor.fieldBgColor(context),
showAsBottomSheet: true,
onSelect: (value) {
involvedPerson = value;
payload["personInvolvedId"] = value?.id;
},
),
8.height,
SingleItemDropDownMenu<Lookup, GenderLookupProvider>(
context: context,
height: 56.toScreenHeight,
title: "Gender",
initialValue: gender,
showShadow: false,
validator: (value) {
if (value == null) return "Mandatory";
return null;
},
backgroundColor: AppColor.fieldBgColor(context),
showAsBottomSheet: true,
onSelect: (value) {
gender = value;
payload["genderId"] = value?.id;
},
),
8.height,
AppTextFormField(
initialValue: "",
labelText: "Person Involved ID",
validator: (value) {
if (involvedPerson?.value == 3 && (value ?? "").isEmpty) return "Mandatory";
return null;
},
suffixIcon: const Icon(Icons.info_outline_rounded, color: AppColor.neutral120).paddingOnly(end: 8).onPress(() {
context.showInfoDialog(
"Required if the person is an Employee. Ensure the correct Employee ID of the Person in Charge (e.g., Charge Nurse, Unit Team Leader) is provided, not a general staff member. (Ex. 34567)");
}),
style: Theme.of(context).textTheme.titleMedium,
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
showShadow: false,
textInputType: TextInputType.number,
textInputAction: TextInputAction.next,
onChange: (value) {
payload["personInvolvedEmployeeId"] = value;
},
),
8.height,
AppTextFormField(
initialValue: "",
labelText: "Person Involved Name",
validator: (value) {
if (involvedPerson?.value == 3 && (value ?? "").isEmpty) return "Mandatory";
return null;
},
suffixIcon: const Icon(Icons.info_outline_rounded, color: AppColor.neutral120).paddingOnly(end: 8).onPress(() {
context.showInfoDialog("State the end-user Employee/Patient Full name. Required if the person is an Employee. (Ex. Mary-Anne Joseph)");
}),
style: Theme.of(context).textTheme.titleMedium,
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
showShadow: false,
textInputType: TextInputType.name,
textInputAction: TextInputAction.next,
onChange: (value) {
payload["personInvolvedEmployeeName"] = value;
},
),
8.height,
ADatePicker(
label: "Occurrence Date & Time",
hideShadow: true,
backgroundColor: context.isDark ? AppColor.neutral20 : AppColor.neutral90,
date: occurrenceDate,
from: DateTime.now().subtract(const Duration(days: 365)),
// enable: widget.enabled ? _tempPickerTimer == null : false,
formatDateWithTime: true,
onDatePicker: (selectedDate) {
showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
builder: (BuildContext context, Widget? child) {
final ThemeData currentTheme = Theme.of(context);
return Theme(
data: currentTheme.copyWith(
timePickerTheme: TimePickerThemeData(
dialHandColor: AppColor.primary10,
dialBackgroundColor: Colors.grey.withOpacity(0.1),
hourMinuteColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? AppColor.primary10 : Colors.grey.withOpacity(0.1)),
dayPeriodColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? AppColor.primary10 : Colors.transparent),
dayPeriodTextColor: MaterialStateColor.resolveWith((states) => states.contains(MaterialState.selected) ? Colors.white : AppColor.primary10),
dayPeriodBorderSide: BorderSide(color: Colors.grey.withOpacity(0.2)),
entryModeIconColor: AppColor.primary10,
),
textButtonTheme: TextButtonThemeData(style: TextButton.styleFrom(foregroundColor: AppColor.primary10)),
),
child: child!,
);
},
).then((selectedTime) {
if (selectedTime != null) {
occurrenceDate = DateTime(selectedDate.year, selectedDate.month, selectedDate.day, selectedTime.hour, selectedTime.minute);
setState(() {});
}
});
},
),
];
}
Future<void> _submit() async {
FocusScope.of(context).unfocus();
if (_formKey.currentState!.validate()) {
if (incidentType?.value == 1 && device == null) {
"Please scan or pick asset".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["occurrenceDate"] = occurrenceDate!.toIso8601String();
_formKey.currentState!.save();
List<IncidentAttachments> attachmentList = [];
for (var item in attachments) {
String fileName = CMRequestUtils.isLocalUrl(item.name ?? '') ? ("${item.name ?? ''.split("/").last}|${base64Encode(File(item.name ?? '').readAsBytesSync())}") : item.name ?? '';
attachmentList.add(IncidentAttachments(id: 0, attachmentName: fileName, incidentId: 0));
}
Utils.showLoading(context);
IncidentProvider incidentProvider = Provider.of<IncidentProvider>(context, listen: false);
payload["incidentAttachments"] = attachmentList.map((v) => v.toJson()).toList();
bool isSuccess = await incidentProvider.addIncidentRequest(payload);
Utils.hideLoading(context);
if (isSuccess) {
Navigator.pop(context);
}
}
}
}

@ -0,0 +1,32 @@
import 'dart:convert';
import 'package:http/http.dart';
import 'package:test_sa/controllers/api_routes/api_manager.dart';
import 'package:test_sa/controllers/api_routes/urls.dart';
import 'package:test_sa/modules/loan_module/models/medical_department_model.dart';
import 'package:test_sa/providers/loading_list_notifier.dart';
class FlowMedicalDepartmentProvider extends LoadingListNotifier<MedicalDepartmentModel> {
@override
Future getData({int? id}) async {
if (loading == true) return -2;
loading = true;
notifyListeners();
try {
Response response = await ApiManager.instance.get(URLs.getFlowMedicalDepartments);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
List categoriesListJson = json.decode(response.body)["data"];
items = categoriesListJson.map((item) => MedicalDepartmentModel.fromJson(item)).toList();
}
loading = false;
notifyListeners();
return response.statusCode;
} catch (error) {
loading = false;
stateCode = -1;
notifyListeners();
return -1;
}
}
}

@ -0,0 +1,24 @@
class IncidentAttachments {
num? id;
num? incidentId;
String? attachmentName;
String? attachmentDescription;
IncidentAttachments({this.id, this.attachmentName, this.incidentId, this.attachmentDescription});
IncidentAttachments.fromJson(dynamic json) {
id = json['id'];
incidentId = json['incidentId'];
attachmentName = json['attachmentName'];
attachmentDescription = json['attachmentDescription'];
}
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['id'] = id;
map['attachmentName'] = attachmentName;
map['incidentId'] = incidentId;
map['attachmentDescription'] = attachmentDescription;
return map;
}
}

@ -0,0 +1,313 @@
import 'package:test_sa/modules/incident_module/incident_attachment_model.dart';
class IncidentDataModel {
int? id;
int? reqNo;
String? reqCode;
int? incidentStatusId;
String? incidentStatusName;
int? incidentStatusValue;
int? incidentClassificationId;
String? incidentClassificationName;
int? incidentClassificationValue;
int? existingOvrTicketId;
String? existingOvrTicketName;
int? existingOvrTicketValue;
String? ovrTicketNumber;
String? incidentTitle;
String? incidentDescription;
int? assetId;
String? assetNumber;
String? oracleCode;
String? model;
String? manufacturer;
String? assetOrigin;
int? siteId;
String? siteName;
int? buildingId;
String? buildingName;
int? departmentId;
String? departmentName;
int? floorId;
String? floorName;
int? rootCauseId;
String? rootCauseName;
int? rootCauseValue;
String? otherRootCause;
String? comments;
int? personInvolvedId;
String? personInvolvedName;
int? personInvolvedValue;
int? genderId;
String? genderName;
int? personInvolvedEmployeeId;
String? personInvolvedEmployeeName;
String? assignedQAId;
String? assignedQAName;
String? assignedSecondaryQAId;
String? assignedSecondaryQAName;
String? qaLeaderId;
String? qaLeaderName;
String? correctiveAction;
int? escalateToOvrId;
String? escalateToOvrName;
int? immediateActionId;
String? immediateActionName;
int? clinicalOrNonClinicalId;
String? clinicalOrNonClinicalName;
String? recommendation;
String? requestorUserId;
String? requesterEmployeeId;
String? requesterEmployeeName;
String? requesterEmployeeEmail;
String? requesterPosition;
String? requesterContactNo;
String? requesterExtensionNo;
int? requestorSiteId;
String? requestorSiteName;
int? requestorDepartmentId;
String? requestorDepartmentName;
int? requestorFlowDepartmentId;
String? requestorFlowDepartmentName;
String? requestorFlowDepartmentShortCode;
String? approvalSignature;
String? occurrenceDate;
String? createdDate;
List<IncidentAttachments>? incidentAttachments;
IncidentDataModel(
{this.id,
this.reqNo,
this.reqCode,
this.incidentStatusId,
this.incidentStatusName,
this.incidentStatusValue,
this.incidentClassificationId,
this.incidentClassificationName,
this.incidentClassificationValue,
this.existingOvrTicketId,
this.existingOvrTicketName,
this.existingOvrTicketValue,
this.ovrTicketNumber,
this.incidentTitle,
this.incidentDescription,
this.assetId,
this.assetNumber,
this.oracleCode,
this.model,
this.manufacturer,
this.assetOrigin,
this.siteId,
this.siteName,
this.buildingId,
this.buildingName,
this.departmentId,
this.departmentName,
this.floorId,
this.floorName,
this.rootCauseId,
this.rootCauseName,
this.rootCauseValue,
this.otherRootCause,
this.comments,
this.personInvolvedId,
this.personInvolvedName,
this.personInvolvedValue,
this.genderId,
this.genderName,
this.personInvolvedEmployeeId,
this.personInvolvedEmployeeName,
this.assignedQAId,
this.assignedQAName,
this.assignedSecondaryQAId,
this.assignedSecondaryQAName,
this.qaLeaderId,
this.qaLeaderName,
this.correctiveAction,
this.escalateToOvrId,
this.escalateToOvrName,
this.immediateActionId,
this.immediateActionName,
this.clinicalOrNonClinicalId,
this.clinicalOrNonClinicalName,
this.recommendation,
this.requestorUserId,
this.requesterEmployeeId,
this.requesterEmployeeName,
this.requesterEmployeeEmail,
this.requesterPosition,
this.requesterContactNo,
this.requesterExtensionNo,
this.requestorSiteId,
this.requestorSiteName,
this.requestorDepartmentId,
this.requestorDepartmentName,
this.requestorFlowDepartmentId,
this.requestorFlowDepartmentName,
this.requestorFlowDepartmentShortCode,
this.approvalSignature,
this.occurrenceDate,
this.createdDate,
this.incidentAttachments});
IncidentDataModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
reqNo = json['reqNo'];
reqCode = json['reqCode'];
incidentStatusId = json['incidentStatusId'];
incidentStatusName = json['incidentStatusName'];
incidentStatusValue = json['incidentStatusValue'];
incidentClassificationId = json['incidentClassificationId'];
incidentClassificationName = json['incidentClassificationName'];
incidentClassificationValue = json['incidentClassificationValue'];
existingOvrTicketId = json['existingOvrTicketId'];
existingOvrTicketName = json['existingOvrTicketName'];
existingOvrTicketValue = json['existingOvrTicketValue'];
ovrTicketNumber = json['ovrTicketNumber'];
incidentTitle = json['incidentTitle'];
incidentDescription = json['incidentDescription'];
assetId = json['assetId'];
assetNumber = json['assetNumber'];
oracleCode = json['oracleCode'];
model = json['model'];
manufacturer = json['manufacturer'];
assetOrigin = json['assetOrigin'];
siteId = json['siteId'];
siteName = json['siteName'];
buildingId = json['buildingId'];
buildingName = json['buildingName'];
departmentId = json['departmentId'];
departmentName = json['departmentName'];
floorId = json['floorId'];
floorName = json['floorName'];
rootCauseId = json['rootCauseId'];
rootCauseName = json['rootCauseName'];
rootCauseValue = json['rootCauseValue'];
otherRootCause = json['otherRootCause'];
comments = json['comments'];
personInvolvedId = json['personInvolvedId'];
personInvolvedName = json['personInvolvedName'];
personInvolvedValue = json['personInvolvedValue'];
genderId = json['genderId'];
genderName = json['genderName'];
personInvolvedEmployeeId = json['personInvolvedEmployeeId'];
personInvolvedEmployeeName = json['personInvolvedEmployeeName'];
assignedQAId = json['assignedQAId'];
assignedQAName = json['assignedQAName'];
assignedSecondaryQAId = json['assignedSecondaryQAId'];
assignedSecondaryQAName = json['assignedSecondaryQAName'];
qaLeaderId = json['qaLeaderId'];
qaLeaderName = json['qaLeaderName'];
correctiveAction = json['correctiveAction'];
escalateToOvrId = json['escalateToOvrId'];
escalateToOvrName = json['escalateToOvrName'];
immediateActionId = json['immediateActionId'];
immediateActionName = json['immediateActionName'];
clinicalOrNonClinicalId = json['clinicalOrNonClinicalId'];
clinicalOrNonClinicalName = json['clinicalOrNonClinicalName'];
recommendation = json['recommendation'];
requestorUserId = json['requestorUserId'];
requesterEmployeeId = json['requesterEmployeeId'];
requesterEmployeeName = json['requesterEmployeeName'];
requesterEmployeeEmail = json['requesterEmployeeEmail'];
requesterPosition = json['requesterPosition'];
requesterContactNo = json['requesterContactNo'];
requesterExtensionNo = json['requesterExtensionNo'];
requestorSiteId = json['requestorSiteId'];
requestorSiteName = json['requestorSiteName'];
requestorDepartmentId = json['requestorDepartmentId'];
requestorDepartmentName = json['requestorDepartmentName'];
requestorFlowDepartmentId = json['requestorFlowDepartmentId'];
requestorFlowDepartmentName = json['requestorFlowDepartmentName'];
requestorFlowDepartmentShortCode = json['requestorFlowDepartmentShortCode'];
approvalSignature = json['approvalSignature'];
occurrenceDate = json['occurrenceDate'];
createdDate = json['createdDate'];
if (json['incidentAttachments'] != null) {
incidentAttachments = <IncidentAttachments>[];
json['incidentAttachments'].forEach((v) {
incidentAttachments!.add(IncidentAttachments.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['reqNo'] = this.reqNo;
data['reqCode'] = this.reqCode;
data['incidentStatusId'] = this.incidentStatusId;
data['incidentStatusName'] = this.incidentStatusName;
data['incidentStatusValue'] = this.incidentStatusValue;
data['incidentClassificationId'] = this.incidentClassificationId;
data['incidentClassificationName'] = this.incidentClassificationName;
data['incidentClassificationValue'] = this.incidentClassificationValue;
data['existingOvrTicketId'] = this.existingOvrTicketId;
data['existingOvrTicketName'] = this.existingOvrTicketName;
data['existingOvrTicketValue'] = this.existingOvrTicketValue;
data['ovrTicketNumber'] = this.ovrTicketNumber;
data['incidentTitle'] = this.incidentTitle;
data['incidentDescription'] = this.incidentDescription;
data['assetId'] = this.assetId;
data['assetNumber'] = this.assetNumber;
data['oracleCode'] = this.oracleCode;
data['model'] = this.model;
data['manufacturer'] = this.manufacturer;
data['assetOrigin'] = this.assetOrigin;
data['siteId'] = this.siteId;
data['siteName'] = this.siteName;
data['buildingId'] = this.buildingId;
data['buildingName'] = this.buildingName;
data['departmentId'] = this.departmentId;
data['departmentName'] = this.departmentName;
data['floorId'] = this.floorId;
data['floorName'] = this.floorName;
data['rootCauseId'] = this.rootCauseId;
data['rootCauseName'] = this.rootCauseName;
data['rootCauseValue'] = this.rootCauseValue;
data['otherRootCause'] = this.otherRootCause;
data['comments'] = this.comments;
data['personInvolvedId'] = this.personInvolvedId;
data['personInvolvedName'] = this.personInvolvedName;
data['personInvolvedValue'] = this.personInvolvedValue;
data['genderId'] = this.genderId;
data['genderName'] = this.genderName;
data['personInvolvedEmployeeId'] = this.personInvolvedEmployeeId;
data['personInvolvedEmployeeName'] = this.personInvolvedEmployeeName;
data['assignedQAId'] = this.assignedQAId;
data['assignedQAName'] = this.assignedQAName;
data['assignedSecondaryQAId'] = this.assignedSecondaryQAId;
data['assignedSecondaryQAName'] = this.assignedSecondaryQAName;
data['qaLeaderId'] = this.qaLeaderId;
data['qaLeaderName'] = this.qaLeaderName;
data['correctiveAction'] = this.correctiveAction;
data['escalateToOvrId'] = this.escalateToOvrId;
data['escalateToOvrName'] = this.escalateToOvrName;
data['immediateActionId'] = this.immediateActionId;
data['immediateActionName'] = this.immediateActionName;
data['clinicalOrNonClinicalId'] = this.clinicalOrNonClinicalId;
data['clinicalOrNonClinicalName'] = this.clinicalOrNonClinicalName;
data['recommendation'] = this.recommendation;
data['requestorUserId'] = this.requestorUserId;
data['requesterEmployeeId'] = this.requesterEmployeeId;
data['requesterEmployeeName'] = this.requesterEmployeeName;
data['requesterEmployeeEmail'] = this.requesterEmployeeEmail;
data['requesterPosition'] = this.requesterPosition;
data['requesterContactNo'] = this.requesterContactNo;
data['requesterExtensionNo'] = this.requesterExtensionNo;
data['requestorSiteId'] = this.requestorSiteId;
data['requestorSiteName'] = this.requestorSiteName;
data['requestorDepartmentId'] = this.requestorDepartmentId;
data['requestorDepartmentName'] = this.requestorDepartmentName;
data['requestorFlowDepartmentId'] = this.requestorFlowDepartmentId;
data['requestorFlowDepartmentName'] = this.requestorFlowDepartmentName;
data['requestorFlowDepartmentShortCode'] = this.requestorFlowDepartmentShortCode;
data['approvalSignature'] = this.approvalSignature;
data['occurrenceDate'] = this.occurrenceDate;
data['createdDate'] = this.createdDate;
if (this.incidentAttachments != null) {
data['incidentAttachments'] = this.incidentAttachments!.map((v) => v.toJson()).toList();
}
return data;
}
}

@ -0,0 +1,200 @@
import 'dart:developer';
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/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';
import 'package:test_sa/modules/internal_audit_module/models/internal_audit_attachment_model.dart';
import 'package:test_sa/modules/internal_audit_module/models/system_internal_audit_data_model.dart';
import 'package:test_sa/modules/internal_audit_module/pages/equipment_internal_audit/update_equipment_internal_audit_page.dart';
import 'package:test_sa/modules/internal_audit_module/pages/system_internal_audit/update_system_internal_audit_page.dart';
import 'package:test_sa/modules/internal_audit_module/provider/internal_audit_provider.dart';
import 'package:test_sa/modules/loan_module/models/loan_attachment_model.dart';
import 'package:test_sa/modules/loan_module/models/loan_request_model.dart';
import 'package:test_sa/modules/loan_module/provider/loan_provider.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/default_app_bar.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/loaders/no_data_found.dart';
import 'package:test_sa/views/widgets/requests/request_status.dart';
class IncidentDetailPage extends StatelessWidget {
static const String id = "/incident-detail-page";
final int incidentId;
IncidentDetailPage({Key? key, required this.incidentId}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const DefaultAppBar(title: "Request Details"),
body: FutureBuilder<IncidentDataModel?>(
future: Provider.of<IncidentProvider>(context, listen: false).getIncidentById(incidentId),
builder: (BuildContext context, AsyncSnapshot<IncidentDataModel?> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) return const CircularProgressIndicator(color: AppColor.primary10).center;
if (snapshot.data == null) return const NoDataFound().center;
List<IncidentAttachments> allAttachments = snapshot.data!.incidentAttachments!;
return ListView(
padding: const EdgeInsets.all(16),
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
StatusLabel(
label: snapshot.data!.incidentStatusName!,
textColor: AppColor.getRequestStatusTextColorByName(context, snapshot.data!.incidentStatusName!),
backgroundColor: AppColor.getRequestStatusColorByName(context, snapshot.data!.incidentStatusName!),
),
1.width.expanded,
Text(
snapshot.data!.createdDate?.toServiceRequestCardFormat ?? "-",
textAlign: TextAlign.end,
style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50),
),
],
),
12.height,
...requesterDetails(context, snapshot.data!),
const Divider().defaultStyle(context),
...requestDetails(context, snapshot.data!),
if (snapshot.data!.ovrTicketNumber?.isNotEmpty ?? false) ovrInformation(context, snapshot.data!.ovrTicketNumber!),
const Divider().defaultStyle(context),
...assetDetails(context, snapshot.data!),
const Divider().defaultStyle(context),
...personInvolvedDetails(context, snapshot.data!),
if (allAttachments.isNotEmpty) ...[
const Divider().defaultStyle(context),
Text(
"Attachments".addTranslation,
style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
FilesList(images: allAttachments.map((e) => URLs.getFileUrl(e.attachmentName ?? '') ?? '').toList() ?? []),
],
],
).toShadowContainer(context, padding: 12),
],
);
}));
}
List<Widget> requestDetails(BuildContext context, IncidentDataModel incidentData) {
return [
Text("Request Details", style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)),
'Incident No: ${incidentData.reqCode ?? "-"}'.bodyText(context),
'Incident Type: ${incidentData.incidentClassificationName ?? "-"}'.bodyText(context),
'Incident Title: ${incidentData.incidentTitle ?? "-"}'.bodyText(context),
'Incident Description: ${incidentData.incidentDescription ?? "-"}'.bodyText(context),
'Exist OVR: ${incidentData.existingOvrTicketName ?? "-"}'.bodyText(context),
'Site: ${incidentData.siteName ?? "-"}'.bodyText(context),
'Building: ${incidentData.buildingName ?? "-"}'.bodyText(context),
'Floor: ${incidentData.floorName ?? "-"}'.bodyText(context),
'Department: ${incidentData.departmentName ?? "-"}'.bodyText(context),
'Occurrence Date: ${incidentData.occurrenceDate?.toServiceRequestDetailsFormat ?? "-"}'.bodyText(context),
'Root Cause: ${incidentData.rootCauseName ?? "-"}'.bodyText(context),
if (incidentData.rootCauseValue == 5) 'Root reason: ${incidentData.otherRootCause ?? "-"}'.bodyText(context),
'Comments: ${incidentData.comments ?? "-"}'.bodyText(context),
];
}
Widget ovrInformation(BuildContext context, String ovrNumber) {
return FutureBuilder<Map<String, dynamic>?>(
future: Provider.of<IncidentProvider>(context, listen: false).getOvrTicketDetails(ovrNumber),
builder: (BuildContext context, AsyncSnapshot<Map<String, dynamic>?> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) return const CircularProgressIndicator(color: AppColor.primary10).center;
if (snapshot.data == null) return const NoDataFound().center;
Map<String, dynamic>? data = snapshot.data;
if (data == null) return const SizedBox();
List<String> keysList = data.keys.toList();
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Divider().defaultStyle(context),
Text("OVR Ticket Information", style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)),
'OVR Ticket Number: $ovrNumber'.bodyText(context),
for (int i = 0; i < keysList.length; i++) '${keysList[i]}: ${data[keysList[i]] ?? "-"}'.bodyText(context),
],
);
});
}
List<Widget> personInvolvedDetails(BuildContext context, IncidentDataModel incidentData) {
return [
Text("Person Involved Information", style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)),
'Person Involved: ${incidentData.personInvolvedName ?? "-"}'.bodyText(context),
'Gender: ${incidentData.genderName ?? "-"}'.bodyText(context),
'Person Involved ID: ${incidentData.personInvolvedEmployeeId ?? "-"}'.bodyText(context),
'Person Involved Name: ${incidentData.personInvolvedEmployeeName ?? "-"}'.bodyText(context),
];
}
List<Widget> assetDetails(BuildContext context, IncidentDataModel loanData) {
return [
Text("Asset Information", style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)),
'${context.translation.assetName}: ${loanData.assetOrigin ?? "-"}'.bodyText(context),
'${context.translation.assetNo}: ${loanData.assetNumber ?? "-"}'.bodyText(context),
// '${context.translation.serialNumber}: ${loanData.assetSerialNumber ?? "-"}'.bodyText(context),
'${context.translation.manufacture}: ${loanData.manufacturer ?? "-"}'.bodyText(context),
'${context.translation.model}: ${loanData.model ?? "-"}'.bodyText(context),
];
}
List<Widget> requesterDetails(BuildContext context, IncidentDataModel incidentData) {
return [
Text("Requester Information", style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50)),
'Name: ${incidentData.requesterEmployeeName ?? "-"}'.bodyText(context),
'Email: ${incidentData.requesterEmployeeEmail ?? "-"}'.bodyText(context),
'Contact: ${incidentData.requesterContactNo ?? "-"}'.bodyText(context),
'Extension: ${incidentData.requesterExtensionNo ?? "-"}'.bodyText(context),
'Site: ${incidentData.requestorSiteName ?? "-"}'.bodyText(context),
'Department: ${incidentData.requestorFlowDepartmentName ?? "-"}'.bodyText(context),
];
}
Widget labelValueText(BuildContext context, String label, String? value) {
if (value == null || value.isEmpty) return const SizedBox.shrink();
return Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Text(
'$label: $value',
style: AppTextStyles.bodyText.copyWith(
color: context.isDark ? AppColor.neutral30 : AppColor.neutral120,
),
),
);
}
Widget checklistWidget(BuildContext context, {required String value}) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Checkbox(
value: true,
activeColor: AppColor.neutral120,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: const VisualDensity(horizontal: -4, vertical: -3),
onChanged: (value) {},
),
value.bodyText(context),
],
);
}
}

@ -0,0 +1,118 @@
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/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/dashboard_detail.dart';
import 'package:test_sa/modules/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_detail_page.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/views/widgets/requests/request_status.dart';
import 'incident_detail_page.dart';
class IncidentItemView extends StatelessWidget {
final Data? requestData;
final RequestsDetails? requestDetails;
final bool showShadow;
const IncidentItemView({Key? key, this.requestData, this.requestDetails, this.showShadow = true}) : super(key: key);
@override
Widget build(BuildContext context) {
if (requestData != null) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
StatusLabel(
label: requestData!.statusName!,
textColor: AppColor.getRequestStatusTextColorByName(context, requestData!.statusName!),
backgroundColor: AppColor.getRequestStatusColorByName(context, requestData!.statusName!),
),
1.width.expanded,
Text(
requestData!.transactionDate?.toServiceRequestCardFormat ?? "",
textAlign: TextAlign.end,
style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50),
),
],
),
8.height,
(requestData?.typeTransaction ?? "Loan Request").heading5(context),
infoWidget(label: context.translation.requestType, value: requestData?.requestTypeName, context: context),
infoWidget(label: "Incident No", value: requestData?.requestNo, context: context),
// infoWidget(label: context.translation.site, value: requestData?., context: context),
8.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.push(context, MaterialPageRoute(builder: (context) => IncidentDetailPage(incidentId: requestDetails!.id!)));
});
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
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 ?? "Loan Request").heading5(context),
8.height,
infoWidget(label: context.translation.requestType, value: requestDetails!.requestType, context: context),
infoWidget(label: "Incident No", value: requestDetails!.requestNo, context: context),
infoWidget(label: context.translation.assetName, value: requestDetails!.assetName, context: context),
infoWidget(label: context.translation.site, value: requestDetails!.site, context: context),
8.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.push(context, MaterialPageRoute(builder: (context) => IncidentDetailPage(incidentId: requestDetails!.id!)));
});
}
Widget infoWidget({required String label, String? value, required BuildContext context}) {
if (value != null && value.isNotEmpty) {
return '$label: $value'.bodyText(context);
}
return const SizedBox();
}
}

@ -0,0 +1,158 @@
import 'dart:convert';
import 'package:http/http.dart';
import 'package:test_sa/providers/loading_list_notifier.dart';
import '../../controllers/api_routes/api_manager.dart';
import '../../controllers/api_routes/urls.dart';
import '../../models/lookup.dart';
class IncidentStatusLookupProvider extends LoadingListNotifier<Lookup> {
@override
Future getData({int? id}) async {
if (loading == true) return -2;
loading = true;
notifyListeners();
try {
Response response = await ApiManager.instance.get(URLs.incidentStatusLookup);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
List categoriesListJson = json.decode(response.body)["data"];
items = categoriesListJson.map((item) => Lookup.fromJson(item)).toList();
}
loading = false;
notifyListeners();
return response.statusCode;
} catch (error) {
loading = false;
stateCode = -1;
notifyListeners();
return -1;
}
}
}
class IncidentTypeLookupProvider extends LoadingListNotifier<Lookup> {
@override
Future getData({int? id}) async {
if (loading == true) return -2;
loading = true;
notifyListeners();
try {
Response response = await ApiManager.instance.get(URLs.incidentTypeLookup);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
List categoriesListJson = json.decode(response.body)["data"];
items = categoriesListJson.map((item) => Lookup.fromJson(item)).toList();
}
loading = false;
notifyListeners();
return response.statusCode;
} catch (error) {
loading = false;
stateCode = -1;
notifyListeners();
return -1;
}
}
}
class IncidentRootCauseLookupProvider extends LoadingListNotifier<Lookup> {
@override
Future getData({int? id}) async {
if (loading == true) return -2;
loading = true;
notifyListeners();
try {
Response response = await ApiManager.instance.get(URLs.incidentRootCauseLookup);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
List categoriesListJson = json.decode(response.body)["data"];
items = categoriesListJson.map((item) => Lookup.fromJson(item)).toList();
}
loading = false;
notifyListeners();
return response.statusCode;
} catch (error) {
loading = false;
stateCode = -1;
notifyListeners();
return -1;
}
}
}
class IncidentPersonInvolvedLookupProvider extends LoadingListNotifier<Lookup> {
@override
Future getData({int? id}) async {
if (loading == true) return -2;
loading = true;
notifyListeners();
try {
Response response = await ApiManager.instance.get(URLs.incidentPersonInvolvedLookup);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
List categoriesListJson = json.decode(response.body)["data"];
items = categoriesListJson.map((item) => Lookup.fromJson(item)).toList();
}
loading = false;
notifyListeners();
return response.statusCode;
} catch (error) {
loading = false;
stateCode = -1;
notifyListeners();
return -1;
}
}
}
class ClinicalNonClinicalLookupProvider extends LoadingListNotifier<Lookup> {
@override
Future getData({int? id}) async {
if (loading == true) return -2;
loading = true;
notifyListeners();
try {
Response response = await ApiManager.instance.get(URLs.clinicalNonClinicalLookup);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
List categoriesListJson = json.decode(response.body)["data"];
items = categoriesListJson.map((item) => Lookup.fromJson(item)).toList();
}
loading = false;
notifyListeners();
return response.statusCode;
} catch (error) {
loading = false;
stateCode = -1;
notifyListeners();
return -1;
}
}
}
class GenderLookupProvider extends LoadingListNotifier<Lookup> {
@override
Future getData({int? id}) async {
if (loading == true) return -2;
loading = true;
notifyListeners();
try {
Response response = await ApiManager.instance.get(URLs.genderLookup);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
List categoriesListJson = json.decode(response.body)["data"];
items = categoriesListJson.map((item) => Lookup.fromJson(item)).toList();
}
loading = false;
notifyListeners();
return response.statusCode;
} catch (error) {
loading = false;
stateCode = -1;
notifyListeners();
return -1;
}
}
}

@ -0,0 +1,56 @@
import 'package:flutter/cupertino.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:http/http.dart';
import 'package:test_sa/controllers/api_routes/api_manager.dart';
import 'package:test_sa/controllers/api_routes/urls.dart';
import 'package:test_sa/modules/loan_module/models/loan_request_model.dart';
import 'dart:convert';
import 'incident_data_model.dart';
class IncidentProvider extends ChangeNotifier {
Future<bool> addIncidentRequest(Map<String, dynamic> body) async {
try {
Response response = await ApiManager.instance.post(URLs.addIncident, body: body);
if (response.statusCode >= 200 && response.statusCode < 300) {
String message = (jsonDecode(response.body)["data"] ?? "") + " " + (jsonDecode(response.body)["message"] ?? "");
Fluttertoast.showToast(msg: message ?? "", toastLength: Toast.LENGTH_LONG);
return true;
}
return false;
} catch (error) {
return false;
}
}
bool isLoading = false;
Future<IncidentDataModel?> getIncidentById(int id) async {
IncidentDataModel? loanData;
try {
Response response = await ApiManager.instance.get(URLs.getIncidentById + "?incidentId=$id");
if (response.statusCode >= 200 && response.statusCode < 300) {
loanData = IncidentDataModel.fromJson(json.decode(response.body)["data"]);
}
} catch (error) {
print(error);
}
return loanData;
}
Future<Map<String, dynamic>?> getOvrTicketDetails(String ticketNumber) async {
try {
Response response = await ApiManager.instance.post(URLs.getOvrTicketDetails, body: {"ticketNumber": ticketNumber}, showToast: false);
if (response.statusCode >= 200 && response.statusCode < 300) {
if (json.decode(response.body)["statusCode"] == 404) {
Fluttertoast.showToast(msg: json.decode(response.body)["message"] ?? "OVR Ticket not found");
return null;
}
return json.decode(response.body)["data"];
}
} catch (error) {
print(error);
}
return null;
}
}

@ -0,0 +1,33 @@
import 'dart:convert';
import 'package:http/http.dart';
import 'package:test_sa/providers/loading_list_notifier.dart';
import '../../controllers/api_routes/api_manager.dart';
import '../../controllers/api_routes/urls.dart';
import '../../models/lookup.dart';
class DemoPeriodLookupProvider extends LoadingListNotifier<Lookup> {
@override
Future getData({int? id}) async {
if (loading == true) return -2;
loading = true;
notifyListeners();
try {
Response response = await ApiManager.instance.get(URLs.incidentTypeLookup);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
List categoriesListJson = json.decode(response.body)["data"];
items = categoriesListJson.map((item) => Lookup.fromJson(item)).toList();
}
loading = false;
notifyListeners();
return response.statusCode;
} catch (error) {
loading = false;
stateCode = -1;
notifyListeners();
return -1;
}
}
}

@ -31,6 +31,7 @@ class SingleItemDropDownMenu<T extends Base, X extends LoadingListNotifier> exte
final Color? titleTextColor; // Now nullable
final bool? loading; // Now nullable
final int? requestById;
final VoidCallback? infoCallback;
final String? Function(T?)? validator;
final AutovalidateMode? autovalidateMode;
final DropDownMatchType matchBy;
@ -46,6 +47,7 @@ class SingleItemDropDownMenu<T extends Base, X extends LoadingListNotifier> exte
this.disableValue,
this.enabled = true,
this.height,
this.infoCallback,
this.showAsBottomSheet = false,
this.showAsFullScreenDialog = true,
this.staticData, // Provide a default empty list
@ -187,7 +189,7 @@ class _SingleItemDropDownMenuState<T extends Base, X extends LoadingListNotifier
},
child: Container(
height: widget.height ?? 60.toScreenHeight,
padding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth),
padding: EdgeInsets.only(left: 16.toScreenWidth, right: 8.toScreenWidth),
decoration: BoxDecoration(
color: context.isDark && (widget.enabled == false || isEmpty)
? AppColor.neutral20
@ -197,101 +199,106 @@ class _SingleItemDropDownMenuState<T extends Base, X extends LoadingListNotifier
borderRadius: BorderRadius.circular(10),
boxShadow: widget.showShadow ? [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)] : null,
),
child: Stack(
alignment: Alignment.center,
child: Row(
children: [
if (widget.enabled) const PositionedDirectional(end: 0, child: Icon(Icons.keyboard_arrow_down_rounded)),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
Stack(
alignment: Alignment.center,
children: [
Flexible(
child: Text(
widget.title,
style: Theme.of(context).textTheme.bodySmall?.copyWith(color: context.isDark ? null : widget.titleTextColor ?? AppColor.neutral20, fontWeight: FontWeight.w500),
),
),
DropdownButton<T>(
value: _selectedItem,
iconSize: 24,
isDense: true,
icon: const SizedBox.shrink(),
dropdownColor: AppColor.neutral100,
elevation: 0,
isExpanded: true,
hint: Text(
isEmpty || widget.enabled == false ? context.translation.noDataFound : context.translation.select,
style: Theme.of(context).textTheme.bodyLarge,
),
style: TextStyle(color: Theme.of(context).primaryColor, fontSize: 12),
underline: const SizedBox.shrink(),
onChanged: (widget.enabled == false || widget.showAsBottomSheet || widget.showAsFullScreenDialog)
? null
: (T? newValue) {
// Now accepts nullable values
final isNull = newValue?.identifier == "-1";
setState(() {
_selectedItem = isNull ? null : newValue;
});
fieldState.didChange(isNull ? null : newValue);
widget.onSelect!(isNull ? null : newValue);
},
items: ((X == NullableLoadingProvider) ? widget.staticData : provider?.items as List<T>)?.map<DropdownMenuItem<T>>((value) {
return DropdownMenuItem<T>(
value: value,
if (widget.enabled) const PositionedDirectional(end: 0, child: Icon(Icons.keyboard_arrow_down_rounded)),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Flexible(
child: Text(
value.name ?? "", // Null-aware operator for value.name
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.white10 : AppColor.black10),
widget.title,
style: Theme.of(context).textTheme.bodySmall?.copyWith(color: context.isDark ? null : widget.titleTextColor ?? AppColor.neutral20, fontWeight: FontWeight.w500),
),
);
}).toList(),
),
),
DropdownButton<T>(
value: _selectedItem,
iconSize: 24,
isDense: true,
icon: const SizedBox.shrink(),
dropdownColor: AppColor.neutral100,
elevation: 0,
isExpanded: true,
hint: Text(
isEmpty || widget.enabled == false ? context.translation.noDataFound : context.translation.select,
style: Theme.of(context).textTheme.bodyLarge,
),
style: TextStyle(color: Theme.of(context).primaryColor, fontSize: 12),
underline: const SizedBox.shrink(),
onChanged: (widget.enabled == false || widget.showAsBottomSheet || widget.showAsFullScreenDialog)
? null
: (T? newValue) {
// Now accepts nullable values
final isNull = newValue?.identifier == "-1";
setState(() {
_selectedItem = isNull ? null : newValue;
});
fieldState.didChange(isNull ? null : newValue);
widget.onSelect!(isNull ? null : newValue);
},
items: ((X == NullableLoadingProvider) ? widget.staticData : provider?.items as List<T>)?.map<DropdownMenuItem<T>>((value) {
return DropdownMenuItem<T>(
value: value,
child: Text(
value.name ?? "", // Null-aware operator for value.name
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.white10 : AppColor.black10),
),
);
}).toList(),
),
],
).onPress(widget.enabled && widget.showAsFullScreenDialog
? () {
openDialog(fieldState);
}
: widget.enabled
? (widget.showAsBottomSheet
? () async {
final selectedT = await showModalBottomSheet<T?>(
// Specify return type
context: context,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
clipBehavior: Clip.antiAliasWithSaveLayer,
builder: (BuildContext context) => SelectionBottomSheet<T>(
// Specify generic type
items: ((X == NullableLoadingProvider) ? widget.staticData : provider?.items as List<T>) ?? [],
// Provide default empty list if null
selectedItem: _selectedItem,
title: widget.title,
showCancel: widget.showCancel,
onSelect: (selectedT) {
setState(() {
_selectedItem = selectedT;
});
fieldState.didChange(selectedT);
widget.onSelect!(selectedT);
},
builderString: (emp) => emp?.name ?? "", // Null-aware operator for emp.name
),
);
// if (selectedT != null) {
// setState(() {
// _selectedItem = selectedT;
// });
// widget.onSelect!(selectedT); // Non-null assertion after null check
// }
}
: null)
: null),
],
).onPress(widget.enabled && widget.showAsFullScreenDialog
? () {
openDialog(fieldState);
}
: widget.enabled
? (widget.showAsBottomSheet
? () async {
final selectedT = await showModalBottomSheet<T?>(
// Specify return type
context: context,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
clipBehavior: Clip.antiAliasWithSaveLayer,
builder: (BuildContext context) => SelectionBottomSheet<T>(
// Specify generic type
items: ((X == NullableLoadingProvider) ? widget.staticData : provider?.items as List<T>) ?? [],
// Provide default empty list if null
selectedItem: _selectedItem,
title: widget.title,
showCancel: widget.showCancel,
onSelect: (selectedT) {
setState(() {
_selectedItem = selectedT;
});
fieldState.didChange(selectedT);
widget.onSelect!(selectedT);
},
builderString: (emp) => emp?.name ?? "", // Null-aware operator for emp.name
),
);
// if (selectedT != null) {
// setState(() {
// _selectedItem = selectedT;
// });
// widget.onSelect!(selectedT); // Non-null assertion after null check
// }
}
: null)
: null),
).expanded,
if (widget.infoCallback != null) const Icon(Icons.info_outline_rounded, color: AppColor.neutral120).onPress(widget.infoCallback),
],
),
),

@ -11,6 +11,7 @@ import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/models/enums/user_types.dart';
import 'package:test_sa/modules/cm_module/create_cm_request.dart';
import 'package:test_sa/modules/demo_module/create_demo_request_page.dart';
import 'package:test_sa/modules/incident_module/create_incident_request_page.dart';
import 'package:test_sa/modules/internal_audit_module/pages/equipment_internal_audit/create_equipment_internal_audit_form.dart';
import 'package:test_sa/modules/internal_audit_module/pages/system_internal_audit/create_system_internal_audit_form.dart';
import 'package:test_sa/modules/loan_module/pages/create_loan_request_page.dart';
@ -142,6 +143,9 @@ class CreateRequestModel {
list.add(CreateRequestModel('Loan Equipment Request'.addTranslation, "add_icon", CreateLoanRequestPage.id));
list.add(CreateRequestModel('Demo Request'.addTranslation, "add_icon", CreateDemoRequestPage.id));
}
if (!context.userProvider.isNurse && context.settingProvider.isUserFlowMedical) {
list.add(CreateRequestModel("Incident Management", "add_icon", CreateIncidentRequestPage.id));
}
return list;
}
}

@ -121,6 +121,9 @@ class _AllRequestsFilterPageState extends State<AllRequestsFilterPage> {
'Demo Request': 15,
};
}
if (context.settingProvider.isUserFlowMedical && !context.userProvider.isNurse) {
types = {'Incidents': 16};
}
final statuses = {
"All WO": 0,

@ -100,6 +100,10 @@ class _MyRequestsPageState extends State<MyRequestsPage> {
];
}
if (context.settingProvider.isUserFlowMedical && !context.userProvider.isNurse) {
requestsList.add(Request(16, "Incidents"));
}
_provider = Provider.of<AllRequestsProvider>(context, listen: false);
_provider!.reset();
WidgetsBinding.instance.addPostFrameCallback((_) {

@ -3,6 +3,7 @@ import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/models/new_models/dashboard_detail.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_item_view.dart';
import 'package:test_sa/modules/demo_module/demo_request_item_view.dart';
import 'package:test_sa/modules/incident_module/incident_item_view.dart';
import 'package:test_sa/modules/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_item_view.dart';
import 'package:test_sa/modules/internal_audit_module/pages/system_internal_audit/system_internal_audit_item_view.dart';
import 'package:test_sa/modules/asset_inventory_module/pages/inventory_session_item_view.dart';
@ -84,6 +85,8 @@ class RequestPaginatedListview extends StatelessWidget {
return MedicalGasInspectionItemView(requestData: request);
case 15:
return DemoRequestItemView(requestData: request);
case 16:
return IncidentItemView(requestData: request);
default:
return Container(
height: 100,

@ -8,6 +8,7 @@ import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/models/all_requests_and_count_model.dart';
import 'package:test_sa/modules/asset_delivery_module/pages/asset_delivery_item_view.dart';
import 'package:test_sa/modules/demo_module/demo_request_item_view.dart';
import 'package:test_sa/modules/incident_module/incident_item_view.dart';
import 'package:test_sa/modules/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_item_view.dart';
import 'package:test_sa/modules/internal_audit_module/pages/system_internal_audit/system_internal_audit_item_view.dart';
import 'package:test_sa/modules/asset_inventory_module/pages/inventory_session_item_view.dart';
@ -73,6 +74,8 @@ class RequestItemViewList extends StatelessWidget {
return MedicalGasInspectionItemView(requestDetails: list[index]);
case 15:
return DemoRequestItemView(requestDetails: list[index]);
case 16:
return IncidentItemView(requestDetails: list[index]);
default:
Container(
height: 100,

@ -0,0 +1,86 @@
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/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
class InfoContent {
final String? title;
final String? message;
InfoContent({this.title, this.message});
}
class InfoDialog extends StatelessWidget {
final String? title;
final String? message;
final List<InfoContent> content;
final String? okTitle;
final VoidCallback? onTap;
final VoidCallback? onCloseTap;
const InfoDialog({Key? key, this.title, this.message, this.content = const [], this.okTitle, this.onTap, this.onCloseTap}) : super(key: key);
@override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: AppColor.background(context),
shape: const RoundedRectangleBorder(),
insetPadding: const EdgeInsets.only(left: 21, right: 21),
child: Padding(
padding: const EdgeInsets.only(left: 20, right: 20, top: 18, bottom: 28),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
title ?? "Information",
style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: AppColor.headingTextColor(context), height: 35 / 24, letterSpacing: -0.96),
).paddingOnly(top: 16),
),
IconButton(
padding: EdgeInsets.zero,
icon: const Icon(Icons.close),
color: AppColor.iconColor(context),
constraints: const BoxConstraints(),
onPressed: () => onCloseTap ?? Navigator.pop(context),
// onPressed: () => Navigator.pop(context),
)
],
),
message != null ? message!.bodyText(context).custom(color: context.isDark ? AppColor.neutral10 : AppColor.neutral20) : const SizedBox(),
8.height,
ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (cxt, index) => RichText(
text: TextSpan(
text: "${content[index].title}: ",
style: AppTextStyles.heading6.copyWith(fontWeight: FontWeight.w600, color: AppColor.textColor(context)),
children: [
TextSpan(text: content[index].message, style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral20)),
],
),
),
separatorBuilder: (cxt, index) => 6.height,
itemCount: content.length),
24.height,
AppFilledButton(
label: okTitle ?? "OK",
onPressed: onTap ?? () => Navigator.pop(context),
textColor: Colors.white,
//color: Ap.green,
),
],
),
),
);
}
}

@ -63,7 +63,7 @@ class AssetDetailBottomSheet extends StatelessWidget {
"${context.translation.building}: ${asset?.building?.name?.cleanupWhitespace.capitalizeFirstOfEach ?? "-"}".bodyText(context),
"${context.translation.floor}: ${asset?.floor?.name?.cleanupWhitespace.capitalizeFirstOfEach ?? "-"}".bodyText(context),
"${context.translation.md}: ${asset?.department?.departmentName?.cleanupWhitespace.capitalizeFirstOfEach ?? "-"}".bodyText(context),
"${context.translation.room}: ${asset?.room?.value ?? "-"}".bodyText(context),
"${context.translation.room}: ${asset?.room?.name ?? "-"}".bodyText(context),
],
).expanded,
],

Loading…
Cancel
Save