traf module added, create, view details, update. cont

design_3.0_internal_audit_module
Sikander Saleem 3 weeks ago
parent a9973047d6
commit 56328b81b4

@ -4,8 +4,8 @@ class URLs {
static const String appReleaseBuildNumber = "26";
// 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 String _baseUrl = "$_host/mobile";
// static final String _baseUrl = "$_host/v2/mobile"; // new V2 apis
@ -225,6 +225,10 @@ class URLs {
static get getServiceReportRejectionReasons => "$_baseUrl/Lookups/GetLookup?lookupEnum=1303";
static get getServiceReportReasonsNew => "$_baseUrl/Lookups/GetLookupReasonNew?lookupEnum=505";
static get getTrafRequestTypeLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=416";
static get getYesNoRequestTypeLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=4";
static get getClassificationTypeLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=450";
static get getRecommendationTypeLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=451";
static get getWoFrames => "$_baseUrl/Lookups/GetLookup?lookupEnum=1254";
@ -247,6 +251,9 @@ class URLs {
static get getCallRequestForWorkOrder => "$_baseUrl/CallRequest/GetCallRequestForWorkOrder"; // get
static get attachmentBaseUrl => "https://atomsmdev.hmg.com/v2/mobile/Files/DownloadFile?fileName=";
//Traf
static get getTRAFById => "$_baseUrl/TRAF/GetTRAFById"; // get
//gas refill
static get getGasTypes => "$_baseUrl/Lookups/GetLookup?lookupEnum=606"; // get
// todo check edits with backend

@ -0,0 +1,119 @@
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.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/models/service_request/spare_parts.dart';
class OracleCodeProvider extends ChangeNotifier {
// number of items call in each request
final pageItemNumber = 20;
//reset provider data
void reset() {
_parts = null;
_stateCode = null;
}
// state code of current request to defied error message
// like 400 customer request failed
// 500 service not available
int? _stateCode;
int? get stateCode => _stateCode;
// true if there is next pagein product list and false if not
bool _nextPage = true;
bool get nextPage => _nextPage;
// contain user data
// when user not login or register _user = null
List<SparePartsWorkOrders>? _parts;
List<SparePartsWorkOrders>? get parts => _parts;
// when categories in-process _loading = true
// done _loading = true
// failed _loading = false
bool _loading = false;
bool get isLoading => _loading;
set isLoading(bool isLoading) {
_loading = isLoading;
notifyListeners();
}
/// return -2 if request in progress
/// return -1 if error happen when sending request
/// return state code if request complete may be 200, 404 or 403
/// for more details check http state manager
/// lib\controllers\http_status_manger\http_status_manger.dart
// Future<int> getParts({String? title}) async {
// if (_loading == true) return -2;
// _loading = true;
// notifyListeners();
// late Response response;
// try {
// response = await ApiManager.instance.post(URLs.getPartNumber, body: {if (title != null && title.isNotEmpty) "partName": title});
// _stateCode = response.statusCode;
// if (response.statusCode >= 200 && response.statusCode < 300) {
// // client's request was successfully received
// List categoriesListJson = json.decode(utf8.decode(response.bodyBytes));
// List<SparePart> page = categoriesListJson.map((part) => SparePart.fromJson(part)).toList();
// _parts ??= [];
// _parts!.addAll(page.map((e) => SparePartsWorkOrders(sparePart: e)).toList());
// _nextPage = page.length >= pageItemNumber;
// }
// _loading = false;
// notifyListeners();
// return response.statusCode;
// } catch (error) {
// _loading = false;
// _stateCode = -1;
// notifyListeners();
// return -1;
// }
// }
/// return -2 if request in progress
/// return -1 if error happen when sending request
/// return state code if request complete may be 200, 404 or 403
/// for more details check http state manager
/// lib\controllers\http_status_manger\http_status_manger.dart
Future<List<SparePart>> getAssetByOracleCode(String oracleCode) async {
late Response response;
try {
response = await ApiManager.instance.post(URLs.getPartNumber, body: {"oracleCode": oracleCode});
List<SparePart> page = [];
if (response.statusCode >= 200 && response.statusCode < 300) {
// client's request was successfully received
List categoriesListJson = json.decode(response.body)["data"];
page = categoriesListJson.map((part) => SparePart.fromJson(part, false)).toList();
}
return page;
} catch (error) {
return [];
}
}
// // Implement this only for spare part request for now show and search on display name ....
//
// Future<List<SparePart>> getPartsListByDisplayName({num? assetId, String? displayName}) async {
// late Response response;
// try {
// response = await ApiManager.instance.post(URLs.getPartNumber, body: {"displayName": displayName, });
// List<SparePart> page = [];
// if (response.statusCode >= 200 && response.statusCode < 300) {
// // client's request was successfully received
// List categoriesListJson = json.decode(response.body)["data"];
// page = categoriesListJson.map((part) => SparePart.fromJson(part,true)).toList();
// }
// return page;
// } catch (error) {
// return [];
// }
// }
}

@ -16,6 +16,7 @@ import 'package:test_sa/controllers/providers/api/devices_provider.dart';
import 'package:test_sa/controllers/providers/api/gas_refill_provider.dart';
import 'package:test_sa/controllers/providers/api/hospitals_provider.dart';
import 'package:test_sa/controllers/providers/api/notifications_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/ppm_provider.dart';
import 'package:test_sa/controllers/providers/api/service_requests_provider.dart';
@ -28,6 +29,9 @@ import 'package:test_sa/controllers/providers/api/status_drop_down/report/servic
import 'package:test_sa/modules/cm_module/service_request_detail_provider.dart';
import 'package:test_sa/modules/cm_module/views/nurse/create_new_request_view.dart';
import 'package:test_sa/modules/tm_module/tasks_wo/create_task_view.dart';
import 'package:test_sa/modules/traf_module/create_traf_request_page.dart';
import 'package:test_sa/modules/traf_module/traf_request_provider.dart';
import 'package:test_sa/modules/traf_module/update_traf_request_page.dart';
import 'package:test_sa/new_views/app_style/app_themes.dart';
import 'package:test_sa/new_views/pages/help_center_page.dart';
import 'package:test_sa/new_views/pages/land_page/land_page.dart';
@ -47,6 +51,7 @@ import 'package:test_sa/providers/gas_request_providers/gas_status_provider.dart
import 'package:test_sa/providers/gas_request_providers/gas_types_provider.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/recommendation_lookup_provider.dart';
import 'package:test_sa/providers/ppm_asset_availability_provider.dart';
import 'package:test_sa/providers/ppm_checklist_status_provider.dart';
import 'package:test_sa/providers/ppm_device_status_provider.dart';
@ -93,6 +98,9 @@ import 'controllers/providers/api/user_provider.dart';
import 'controllers/providers/settings/setting_provider.dart';
import 'dashboard_latest/dashboard_provider.dart';
import 'new_views/pages/gas_refill_request_form.dart';
import 'providers/lookups/classification_lookup_provider.dart';
import 'providers/lookups/request_type_lookup_provider.dart';
import 'providers/lookups/yes_no_lookup_provider.dart';
import 'providers/service_request_providers/loan_availability_provider.dart';
import 'providers/service_request_providers/reject_reason_provider.dart';
@ -121,7 +129,8 @@ void main() async {
} else {
await Firebase.initializeApp();
}
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(statusBarColor: Colors.transparent,
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
systemNavigationBarColor: Colors.white,
systemNavigationBarIconBrightness: Brightness.dark,
));
@ -179,21 +188,18 @@ class MyApp extends StatelessWidget {
//new providers according to new Api's..
ChangeNotifierProvider(create: (_) => DashBoardProvider()),
ChangeNotifierProvider(create: (_) => ServiceRequestDetailProvider()),
// ChangeNotifierProvider(create: (_) => PreventiveMaintenanceVisitsProvider()),
ChangeNotifierProvider(create: (_) => ClassificationLookupProvider()),
ChangeNotifierProvider(create: (_) => RecommendationLookupProvider()),
ChangeNotifierProvider(create: (_) => PpmProvider()),
ChangeNotifierProvider(create: (_) => PartsProvider()),
///todo deleted
//ChangeNotifierProvider(create: (_) => ServiceReportReasonsProvider()),
//ChangeNotifierProvider(create: (_) => ServiceReportStatusProvider()),
///todo deleted
//ChangeNotifierProvider(create: (_) => ServiceReportEquipmentStatusProvider()),
//ChangeNotifierProvider(create: (_) => ServiceReportTypesProvider()),
ChangeNotifierProvider(create: (_) => RequestTypeLookupProvider()),
ChangeNotifierProvider(create: (_) => YesNoLookupProvider()),
ChangeNotifierProvider(create: (_) => ServiceStatusProvider()),
ChangeNotifierProvider(create: (_) => ServiceReportLastCallsProvider()),
///todo deleted
//ChangeNotifierProvider(create: (_) => GasCylinderSizesProvider()),
ChangeNotifierProvider(create: (_) => OracleCodeProvider()),
///todo deleted
//ChangeNotifierProvider(create: (_) => GasCylinderTypesProvider()),
ChangeNotifierProvider(create: (_) => GasStatusProvider()),
@ -202,8 +208,8 @@ class MyApp extends StatelessWidget {
ChangeNotifierProvider(create: (_) => DeviceTransferProvider()),
ChangeNotifierProvider(create: (_) => AssetTransferStatusProvider()),
///todo deleted
//ChangeNotifierProvider(create: (_) => AssignedToProvider()),
ChangeNotifierProvider(create: (_) => TrafRequestProvider()),
///todo deleted
//ChangeNotifierProvider(create: (_) => PentryTaskStatusProvider()),
//ChangeNotifierProvider(create: (_) => PentryVisitStatusProvider()),
@ -312,7 +318,8 @@ class MyApp extends StatelessWidget {
GasRefillRequestForm.routeName: (_) => const GasRefillRequestForm(),
// ServiceRequestsPage.id: (_) => const ServiceRequestsPage(),
CreateTaskView.id: (_) => const CreateTaskView(),
//ReportIssuesPage.id: (_) => ReportIssuesPage(),
CreateTRAFRequestPage.id: (_) => CreateTRAFRequestPage(),
UpdateTrafRequestPage.id: (_) => UpdateTrafRequestPage(),
RequestGasRefill.id: (_) => const RequestGasRefill(),
UpdateGasRefillRequest.id: (_) => const UpdateGasRefillRequest(),
// CreateServiceRequestPage.id: (_) => const CreateServiceRequestPage(),

@ -0,0 +1,131 @@
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/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 AssetAutoCompleteField extends StatefulWidget {
final String initialValue;
final num? assetId;
final bool clearAfterPick, byName;
final Function(SparePartsWorkOrders) onPick;
const AssetAutoCompleteField({Key? key, required this.byName, required this.initialValue, this.assetId, required this.onPick, this.clearAfterPick = true}) : super(key: key);
@override
_AssetAutoCompleteFieldState createState() => _AssetAutoCompleteFieldState();
}
class _AssetAutoCompleteFieldState extends State<AssetAutoCompleteField> {
late OracleCodeProvider _oracleCodeProvider;
late TextEditingController _controller;
bool loading = false;
@override
void initState() {
_controller = TextEditingController(text: widget.initialValue);
super.initState();
_oracleCodeProvider = Provider.of<OracleCodeProvider>(context, listen: false);
}
@override
void didUpdateWidget(covariant AssetAutoCompleteField 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<SparePartsWorkOrders>(
optionsBuilder: (TextEditingValue textEditingValue) async {
if (textEditingValue.text.isEmpty) {
if (loading) {
setState(() {
loading = false;
});
}
return const Iterable<SparePartsWorkOrders>.empty();
}
if (!loading) {
setState(() {
loading = true;
});
}
List<SparePartsWorkOrders> workOrders = (await _oracleCodeProvider.getAssetByOracleCode(textEditingValue.text)).map((e) => SparePartsWorkOrders(sparePart: e)).toList();
setState(() {
loading = false;
});
return workOrders;
},
displayStringForOption: (SparePartsWorkOrders option) => widget.byName ? option.sparePart?.partName ?? "" : option.sparePart?.partNo ?? "",
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,
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: context.translation.oracleCode,
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: (SparePartsWorkOrders selection) {
if (widget.clearAfterPick) {
_controller.clear();
} else {
_controller.text = widget.byName ? (selection.sparePart?.partName ?? "") : (selection.sparePart?.partNo ?? "");
}
widget.onPick(selection);
},
),
);
}
}

@ -0,0 +1,381 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/string_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/models/device/asset.dart';
import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart';
import 'package:test_sa/modules/traf_module/asset_auto_complete_field.dart';
import 'package:test_sa/modules/traf_module/traf_request_detail_page.dart';
import 'package:test_sa/modules/traf_module/update_traf_request_page.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/multiple_item_drop_down_menu.dart';
import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart';
import 'package:test_sa/providers/loading_list_notifier.dart';
import 'package:test_sa/providers/lookups/request_type_lookup_provider.dart';
import 'package:test_sa/providers/lookups/yes_no_lookup_provider.dart';
import 'package:test_sa/views/widgets/equipment/asset_picker.dart';
import 'traf_request_model.dart';
class CreateTRAFRequestPage extends StatefulWidget {
static const String id = "/create-TRAF";
CreateTRAFRequestPage({Key? key}) : super(key: key);
@override
_CreateTRAFRequestPageState createState() {
return _CreateTRAFRequestPageState();
}
}
class _CreateTRAFRequestPageState extends State<CreateTRAFRequestPage> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
bool _acknowledgement = false;
Asset? asset;
Lookup? requestType;
Lookup? isUsedSolelyOrShared;
Lookup? otherServicesEffects;
Lookup? useInCombination;
TrafRequestDataModel? trafRequest;
List<Lookup> abc = [];
@override
void initState() {
super.initState();
trafRequest = TrafRequestDataModel();
resetProviders();
}
void resetProviders() {
Provider.of<RequestTypeLookupProvider>(context, listen: false).reset();
// Provider.of<YesNoLookupProvider>(context, listen: false).reset();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const DefaultAppBar(title: "TRAF Request"),
body: Form(
key: _formKey,
child: Column(
children: [
ListView(padding: const EdgeInsets.all(16), children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// SingleItemDropDownMenu<TaskType, TaskTypeProvider>(
// context: context,
// height: 56.toScreenHeight,
// title: context.translation.taskType,
// showShadow: false,
// backgroundColor: AppColor.fieldBgColor(context),
// showAsBottomSheet: true,
// initialValue: selectedType,
// onSelect: (type) {},
// ),
SingleItemDropDownMenu<Lookup, RequestTypeLookupProvider>(
context: context,
showAsBottomSheet: true,
backgroundColor: AppColor.neutral100,
showShadow: false,
title: context.translation.requestType,
initialValue: requestType,
onSelect: (value) {
requestType = value;
trafRequest?.requestTypeId = value?.value;
setState(() {});
},
),
if (requestType?.value == 1) ...[
12.height,
AssetAutoCompleteField(
// assetId: widget.assetId,
clearAfterPick: false,
byName: false,
initialValue: "",
onPick: (part) {
// model.partCatalogItem = PartCatalogItem(id: part.sparePart?.id, partNumber: part.sparePart?.partNo, partName: part.sparePart?.partName, oracleCode: part.sparePart?.oracleCode);
// setState(() {});
},
),
],
if (requestType?.value == 2) ...[
12.height,
AssetPicker(
device: asset,
editable: false,
showLoading: false,
borderColor: AppColor.black20,
backgroundColor: AppColor.white936,
onPick: (asset) async {
this.asset = asset;
setState(() {});
// pendingAssetServiceRequest = null;
// _serviceRequest.device = asset;
// await checkAssetForPendingServiceRequest(asset.id!.toInt());
// if (pendingAssetServiceRequest != null && pendingAssetServiceRequest!.details!.isNotEmpty) {
// showPendingRequestBottomSheet();
// }
},
),
],
12.height,
Text(
"Request Details",
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50, fontWeight: FontWeight.w600),
),
12.height,
AppTextFormField(
initialValue: "",
labelText: "How would the requested technology solve the current situation and/or serve the purpose?",
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
floatingLabelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
showShadow: false,
makeMultiLinesNull: true,
onChange: (value) {
trafRequest?.purposeAnswer = value;
},
),
12.height,
AppTextFormField(
initialValue: "",
labelText: "What is the current practice?",
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
showShadow: false,
makeMultiLinesNull: true,
onChange: (value) {
trafRequest?.currentPractise = value;
},
),
12.height,
AppTextFormField(
initialValue: "",
makeMultiLinesNull: true,
labelText: "Census Q1",
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
showShadow: false,
textInputType: TextInputType.number,
onChange: (value) {
trafRequest?.censusQ1 = int.tryParse(value);
},
),
12.height,
AppTextFormField(
initialValue: "",
makeMultiLinesNull: true,
labelText: "Census Q2",
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
showShadow: false,
textInputType: TextInputType.number,
onChange: (value) {
trafRequest?.censusQ2 = int.tryParse(value);
},
),
12.height,
AppTextFormField(
initialValue: "",
makeMultiLinesNull: true,
labelText: "Census Q3",
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
showShadow: false,
textInputType: TextInputType.number,
onChange: (value) {
trafRequest?.censusQ3 = int.tryParse(value);
},
),
12.height,
AppTextFormField(
initialValue: "",
makeMultiLinesNull: true,
labelText: "Census Q4",
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
showShadow: false,
textInputType: TextInputType.number,
onChange: (value) {
trafRequest?.censusQ4 = int.tryParse(value);
},
),
12.height,
SingleItemDropDownMenu<Lookup, YesNoLookupProvider>(
context: context,
showAsBottomSheet: true,
backgroundColor: AppColor.neutral100,
showShadow: false,
height: 80,
title: "List down names & contact information of users going to use the technology & specify if they are part-time or full time?",
initialValue: isUsedSolelyOrShared,
onSelect: (value) {
// isUsedSolelyOrShared = value;
setState(() {});
},
),
12.height,
SingleItemDropDownMenu<Lookup, YesNoLookupProvider>(
context: context,
showAsBottomSheet: true,
backgroundColor: AppColor.neutral100,
showShadow: false,
height: 80,
title: "Is the requesting department going to use the technology solely or shared with other departments?",
initialValue: isUsedSolelyOrShared,
onSelect: (value) {
isUsedSolelyOrShared = value;
trafRequest?.usingSolelyOrSharedId = value?.value;
setState(() {});
},
),
if (isUsedSolelyOrShared?.value == 1) ...[
12.height,
MultipleItemDropDownMenu<Lookup, RequestTypeLookupProvider>(
context: context,
showAsBottomSheet: true,
backgroundColor: AppColor.neutral100,
showShadow: false,
showCancel: true,
title: "Please specify departments and relations",
initialValue: abc,
onSelect: (value) {
abc = value ?? [];
// setState(() {
//
// });
},
),
],
12.height,
SingleItemDropDownMenu<Lookup, YesNoLookupProvider>(
context: context,
showAsBottomSheet: true,
backgroundColor: AppColor.neutral100,
showShadow: false,
height: 80,
title: "Would other services be effected by acquiring the new equipment?",
initialValue: otherServicesEffects,
onSelect: (value) {
otherServicesEffects = value;
trafRequest?.isEffectedId = value?.value;
setState(() {});
},
),
if (otherServicesEffects?.value == 1) ...[
12.height,
AppTextFormField(
initialValue: "",
// makeMultiLinesNull: true,
textInputType: TextInputType.multiline,
alignLabelWithHint: true,
labelText: "List down these services and stat how would it be effected",
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
showShadow: false,
onChange: (value) {
trafRequest?.effectedServices = value;
},
),
],
12.height,
SingleItemDropDownMenu<Lookup, YesNoLookupProvider>(
context: context,
showAsBottomSheet: true,
backgroundColor: AppColor.neutral100,
showShadow: false,
height: 80,
title: "Is the equipment going to be used with combination of other equipment to accomplish a specific procedure?",
initialValue: useInCombination,
onSelect: (value) {
useInCombination = value;
trafRequest?.isCombinationId = value?.value;
setState(() {});
},
),
if (useInCombination?.value == 1) ...[
12.height,
AppTextFormField(
initialValue: "",
textInputType: TextInputType.multiline,
alignLabelWithHint: true,
labelText: "kindly describe in detail",
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
showShadow: false,
onChange: (value) {
trafRequest?.usedWithCombination = value;
},
),
]
// 23.height,
// AttachmentPicker(
// label: context.translation.attachImage,
// attachment: attachments,
// buttonColor: AppColor.black10,
// onlyImages: false,
// buttonIcon: 'image-plus'.toSvgAsset(color: context.isDark ? AppColor.primary10 : AppColor.neutral120),
// //verify this if not required delete this ..
// onChange: (attachments) {
// attachments = attachments;
// setState(() {});
// },
// ),
],
).toShadowContainer(context, padding: 12, borderRadius: 20),
16.height,
Row(
children: [
Checkbox(
value: _acknowledgement,
visualDensity: const VisualDensity(horizontal: -4.0, vertical: -4.0),
activeColor: AppColor.blueStatus(context),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
onChanged: (value) {
setState(() {
_acknowledgement = value!;
});
}),
12.width,
"I acknowledge the information filled above is correct".addTranslation.bodyText(context).custom(color: context.isDark ? AppColor.primary50 : AppColor.neutral120).expanded,
],
),
]).expanded,
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
buttonColor: AppColor.primary10,
label: context.translation.submitRequest,
onPressed: _acknowledgement ? _submit : null,
// buttonColor: AppColor.primary10,
),
),
],
),
),
);
}
void _submit() {
Navigator.push(context, MaterialPageRoute(builder: (context) => TrafRequestDetailPage(trafId: 27)));
}
}

@ -0,0 +1,161 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/providers/api/user_provider.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/models/enums/user_types.dart';
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart';
import 'package:test_sa/modules/traf_module/traf_request_provider.dart';
import 'package:test_sa/modules/traf_module/update_traf_request_page.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/loaders/app_loading.dart';
import 'traf_request_model.dart';
class TrafRequestDetailPage extends StatefulWidget {
static const String id = "/details-TRAF";
final int trafId;
TrafRequestDetailPage({Key? key, required this.trafId}) : super(key: key);
@override
_TrafRequestDetailPageState createState() {
return _TrafRequestDetailPageState();
}
}
class _TrafRequestDetailPageState extends State<TrafRequestDetailPage> {
@override
void initState() {
super.initState();
Provider.of<TrafRequestProvider>(context, listen: false).getTRAFById(widget.trafId);
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
bool isEngineer = (Provider.of<UserProvider>(context, listen: false).user?.type) == UsersTypes.engineer;
return Scaffold(
appBar: const DefaultAppBar(title: "TRAF Request"),
body: Selector<TrafRequestProvider, bool>(
selector: (_, myModel) => myModel.isLoading, // Selects only the userName
builder: (_, isLoading, __) {
if (isLoading) return const ALoading();
TrafRequestProvider trafProvider = Provider.of<TrafRequestProvider>(context, listen: false);
return Column(
children: [
ListView(
padding: const EdgeInsets.all(16),
children: [
requesterInformation(trafProvider.trafRequestDataModel!),
12.height,
requestInformation(trafProvider.trafRequestDataModel!),
if (trafProvider.trafRequestDataModel!.requestTypeId == 733) ...[
12.height,
assetInformation(trafProvider.trafRequestDataModel!),
]
],
).expanded,
// if (isEngineer)
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
buttonColor: AppColor.primary10,
label: "Update",
onPressed: () {
Navigator.pushNamed(context, UpdateTrafRequestPage.id);
}),
),
],
);
},
));
}
Widget requesterInformation(TrafRequestDataModel data) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Requester Information",
style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
8.height,
'${context.translation.employeeId}: ${data.employeeId ?? '-'}'.bodyText(context),
'${context.translation.name}: ${data.employeeId ?? '-'}'.bodyText(context), // todo ask shaheer
'${context.translation.email}: ${data.employeeId ?? '-'}'.bodyText(context), // todo ask shaheer
'Position: ${data.employeeId ?? '-'}'.bodyText(context), // todo ask shaheer
'${context.translation.site}: ${data.siteName ?? '-'}'.bodyText(context),
'${context.translation.department}: ${data.departments ?? '-'}'.bodyText(context),
'Requester Extension: ${data.requesterExtensionName ?? '-'}'.bodyText(context),
'${context.translation.extensionNo}: ${data.requesterExtensionNumber ?? '-'}'.bodyText(context),
],
).toShadowContainer(context, borderRadius: 20, padding: 12);
}
Widget requestInformation(TrafRequestDataModel data) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"TRAF Request Information",
style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
8.height,
'TRAF No: ${data.reqCode ?? '-'}'.bodyText(context),
'Request type: ${data.requestTypeName ?? '-'}'.bodyText(context),
4.height,
'What is the current practice?:\n${data.currentPractise ?? '-'}'.bodyText(context),
4.height,
'Census Q1: ${data.censusQ1 ?? '-'}'.bodyText(context),
'Census Q2: ${data.censusQ2 ?? '-'}'.bodyText(context),
'Census Q3: ${data.censusQ3 ?? '-'}'.bodyText(context),
'Census Q4: ${data.censusQ4 ?? '-'}'.bodyText(context),
4.height,
'List down names & contact information of users going to use the technology & specify if they are part-time or full time?:\n${data.trafContacts ?? '-'}'.bodyText(context),
4.height,
'Is the requesting department going to use the technology solely or shared with other departments?:\n${data.usingSolelyOrSharedId ?? '-'}'.bodyText(context),
4.height,
'Would other services be effected by acquiring the new equipment?:\n${data.effectedServices ?? '-'}'.bodyText(context),
4.height,
'Is the equipment going to be used with combination of other equipment to accomplish a specific procedure?:\n${data.usedWithCombination ?? '-'}'.bodyText(context),
],
).toShadowContainer(context, borderRadius: 20, padding: 12);
}
Widget assetInformation(TrafRequestDataModel data) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Asset Information",
style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50),
),
8.height,
'Last price & PO: ${data.poNumber ?? '-'}'.bodyText(context),
'The quantity of the same asset: ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer
'Existing asset under SLA: ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer
'Age of the asset: ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer
'Total Maintenance Cost (TMC): ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer
'Net Book Value (NBV): ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer
'Mean Time Between Failure (MTBF): ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer
'Down Time (DT): ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer
'Up Time (UT): ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer
'Purchased Price: ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer
],
).toShadowContainer(context, borderRadius: 20, padding: 12);
}
}

@ -0,0 +1,551 @@
class TrafRequestModel {
List<TrafRequestDataModel>? trafRequestDataModel;
int? totalRows;
int? count;
String? message;
String? title;
String? innerMessage;
int? responseCode;
bool? isSuccess;
TrafRequestModel({this.trafRequestDataModel, this.totalRows, this.count, this.message, this.title, this.innerMessage, this.responseCode, this.isSuccess});
TrafRequestModel.fromJson(Map<String, dynamic> json) {
if (json['TrafRequestDataModel'] != null) {
trafRequestDataModel = <TrafRequestDataModel>[];
json['TrafRequestDataModel'].forEach((v) {
trafRequestDataModel!.add(new TrafRequestDataModel.fromJson(v));
});
}
totalRows = json['totalRows'];
count = json['count'];
message = json['message'];
title = json['title'];
innerMessage = json['innerMessage'];
responseCode = json['responseCode'];
isSuccess = json['isSuccess'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.trafRequestDataModel != null) {
data['TrafRequestDataModel'] = this.trafRequestDataModel!.map((v) => v.toJson()).toList();
}
data['totalRows'] = this.totalRows;
data['count'] = this.count;
data['message'] = this.message;
data['title'] = this.title;
data['innerMessage'] = this.innerMessage;
data['responseCode'] = this.responseCode;
data['isSuccess'] = this.isSuccess;
return data;
}
}
class TrafRequestDataModel {
int? reqNo;
String? reqCode;
String? employeeId;
String? employeeName;
String? employeeEmail;
int? positionId;
String? positionName;
List<Departments>? departments;
int? siteId;
String? siteName;
String? requesterExtensionNumber;
String? requesterExtensionName;
int? requesterExtensionPositionId;
String? requesterExtensionPositionName;
int? requestTypeId;
String? requestTypeName;
int? assetNDId;
int? modelId;
String? modelName;
int? manufacturerId;
String? manufacturerName;
int? assetId;
String? assetSerialNo;
String? assetNumber;
String? assetName;
int? assetAssetNDId;
int? qty;
String? purposeAnswer;
String? currentPractise;
int? censusQ1;
int? censusQ2;
int? censusQ3;
int? censusQ4;
List<TrafContacts>? trafContacts;
int? usingSolelyOrSharedId;
String? usingSolelyOrSharedName;
int? isEffectedId;
String? isEffectedName;
String? effectedServices;
int? isCombinationId;
String? isCombinationName;
String? usedWithCombination;
String? comment;
List<Attachments>? attachments;
int? isBudgetId;
String? isBudgetName;
String? firstLineManagerId;
String? firstLineManagerName;
int? firstLineManagerApprovalId;
String? firstLineManagerApprovalName;
int? tlapiId;
String? tlapiName;
String? assessorEmployeeId;
String? assessorEmployeeName;
String? secondLineManagerId;
String? secondLineManagerName;
int? secondLineManagerApprovalId;
String? secondLineManagerApprovalName;
String? hospitalManagementId;
String? hospitalManagementName;
int? hospitalManagementApprovalId;
String? hospitalManagementApprovalName;
List<OracleCodes>? oracleCodes;
String? assessorTeamLeaderId;
String? assessorTeamLeaderName;
int? assessorTeamLeaderApprovalId;
String? assessorTeamLeaderApprovalName;
int? statusOfDRId;
String? statusOfDRName;
int? statusOfRequesterId;
String? statusOfRequesterName;
bool? approved;
String? approvalUserId;
String? approvalUserName;
String? approvalDate;
String? docDate;
String? approvalDocDate;
String? isAssigned;
String? poNumber;
int? apiDirectorId;
String? apiDirectorName;
int? apiDirectorApprovalId;
String? apiDirectorApprovalName;
List<int>? trafAssessIds;
List<int>? trafPRIds;
List<int>? trafOfferIds;
int? id;
String? createdBy;
String? createdDate;
String? modifiedBy;
String? modifiedDate;
TrafRequestDataModel(
{this.reqNo,
this.reqCode,
this.employeeId,
this.employeeName,
this.employeeEmail,
this.positionId,
this.positionName,
this.departments,
this.siteId,
this.siteName,
this.requesterExtensionNumber,
this.requesterExtensionName,
this.requesterExtensionPositionId,
this.requesterExtensionPositionName,
this.requestTypeId,
this.requestTypeName,
this.assetNDId,
this.modelId,
this.modelName,
this.manufacturerId,
this.manufacturerName,
this.assetId,
this.assetSerialNo,
this.assetNumber,
this.assetName,
this.assetAssetNDId,
this.qty,
this.purposeAnswer,
this.currentPractise,
this.censusQ1,
this.censusQ2,
this.censusQ3,
this.censusQ4,
this.trafContacts,
this.usingSolelyOrSharedId,
this.usingSolelyOrSharedName,
this.isEffectedId,
this.isEffectedName,
this.effectedServices,
this.isCombinationId,
this.isCombinationName,
this.usedWithCombination,
this.comment,
this.attachments,
this.isBudgetId,
this.isBudgetName,
this.firstLineManagerId,
this.firstLineManagerName,
this.firstLineManagerApprovalId,
this.firstLineManagerApprovalName,
this.tlapiId,
this.tlapiName,
this.assessorEmployeeId,
this.assessorEmployeeName,
this.secondLineManagerId,
this.secondLineManagerName,
this.secondLineManagerApprovalId,
this.secondLineManagerApprovalName,
this.hospitalManagementId,
this.hospitalManagementName,
this.hospitalManagementApprovalId,
this.hospitalManagementApprovalName,
this.oracleCodes,
this.assessorTeamLeaderId,
this.assessorTeamLeaderName,
this.assessorTeamLeaderApprovalId,
this.assessorTeamLeaderApprovalName,
this.statusOfDRId,
this.statusOfDRName,
this.statusOfRequesterId,
this.statusOfRequesterName,
this.approved,
this.approvalUserId,
this.approvalUserName,
this.approvalDate,
this.docDate,
this.approvalDocDate,
this.isAssigned,
this.poNumber,
this.apiDirectorId,
this.apiDirectorName,
this.apiDirectorApprovalId,
this.apiDirectorApprovalName,
this.trafAssessIds,
this.trafPRIds,
this.trafOfferIds,
this.id,
this.createdBy,
this.createdDate,
this.modifiedBy,
this.modifiedDate});
TrafRequestDataModel.fromJson(Map<String, dynamic> json) {
reqNo = json['reqNo'];
reqCode = json['reqCode'];
employeeId = json['employeeId'];
employeeName = json['employeeName'];
employeeEmail = json['employeeEmail'];
positionId = json['positionId'];
positionName = json['positionName'];
if (json['departments'] != null) {
departments = <Departments>[];
json['departments'].forEach((v) {
departments!.add(new Departments.fromJson(v));
});
}
siteId = json['siteId'];
siteName = json['siteName'];
requesterExtensionNumber = json['requesterExtensionNumber'];
requesterExtensionName = json['requesterExtensionName'];
requesterExtensionPositionId = json['requesterExtensionPositionId'];
requesterExtensionPositionName = json['requesterExtensionPositionName'];
requestTypeId = json['requestTypeId'];
requestTypeName = json['requestTypeName'];
assetNDId = json['assetNDId'];
modelId = json['modelId'];
modelName = json['modelName'];
manufacturerId = json['manufacturerId'];
manufacturerName = json['manufacturerName'];
assetId = json['assetId'];
assetSerialNo = json['assetSerialNo'];
assetNumber = json['assetNumber'];
assetName = json['assetName'];
assetAssetNDId = json['assetAssetNDId'];
qty = json['qty'];
purposeAnswer = json['purposeAnswer'];
currentPractise = json['currentPractise'];
censusQ1 = json['censusQ1'];
censusQ2 = json['censusQ2'];
censusQ3 = json['censusQ3'];
censusQ4 = json['censusQ4'];
if (json['trafContacts'] != null) {
trafContacts = <TrafContacts>[];
json['trafContacts'].forEach((v) {
trafContacts!.add(new TrafContacts.fromJson(v));
});
}
usingSolelyOrSharedId = json['usingSolelyOrSharedId'];
usingSolelyOrSharedName = json['usingSolelyOrSharedName'];
isEffectedId = json['isEffectedId'];
isEffectedName = json['isEffectedName'];
effectedServices = json['effectedServices'];
isCombinationId = json['isCombinationId'];
isCombinationName = json['isCombinationName'];
usedWithCombination = json['usedWithCombination'];
comment = json['comment'];
if (json['attachments'] != null) {
attachments = <Attachments>[];
json['attachments'].forEach((v) {
attachments!.add(new Attachments.fromJson(v));
});
}
isBudgetId = json['isBudgetId'];
isBudgetName = json['isBudgetName'];
firstLineManagerId = json['firstLineManagerId'];
firstLineManagerName = json['firstLineManagerName'];
firstLineManagerApprovalId = json['firstLineManagerApprovalId'];
firstLineManagerApprovalName = json['firstLineManagerApprovalName'];
tlapiId = json['tlapiId'];
tlapiName = json['tlapiName'];
assessorEmployeeId = json['assessorEmployeeId'];
assessorEmployeeName = json['assessorEmployeeName'];
secondLineManagerId = json['secondLineManagerId'];
secondLineManagerName = json['secondLineManagerName'];
secondLineManagerApprovalId = json['secondLineManagerApprovalId'];
secondLineManagerApprovalName = json['secondLineManagerApprovalName'];
hospitalManagementId = json['hospitalManagementId'];
hospitalManagementName = json['hospitalManagementName'];
hospitalManagementApprovalId = json['hospitalManagementApprovalId'];
hospitalManagementApprovalName = json['hospitalManagementApprovalName'];
if (json['oracleCodes'] != null) {
oracleCodes = <OracleCodes>[];
json['oracleCodes'].forEach((v) {
oracleCodes!.add(new OracleCodes.fromJson(v));
});
}
assessorTeamLeaderId = json['assessorTeamLeaderId'];
assessorTeamLeaderName = json['assessorTeamLeaderName'];
assessorTeamLeaderApprovalId = json['assessorTeamLeaderApprovalId'];
assessorTeamLeaderApprovalName = json['assessorTeamLeaderApprovalName'];
statusOfDRId = json['statusOfDRId'];
statusOfDRName = json['statusOfDRName'];
statusOfRequesterId = json['statusOfRequesterId'];
statusOfRequesterName = json['statusOfRequesterName'];
approved = json['approved'];
approvalUserId = json['approvalUserId'];
approvalUserName = json['approvalUserName'];
approvalDate = json['approvalDate'];
docDate = json['docDate'];
approvalDocDate = json['approvalDocDate'];
isAssigned = json['isAssigned'];
poNumber = json['poNumber'];
apiDirectorId = json['apiDirectorId'];
apiDirectorName = json['apiDirectorName'];
apiDirectorApprovalId = json['apiDirectorApprovalId'];
apiDirectorApprovalName = json['apiDirectorApprovalName'];
trafAssessIds = json['trafAssessIds'].cast<int>();
trafPRIds = json['trafPRIds'].cast<int>();
trafOfferIds = json['trafOfferIds'].cast<int>();
id = json['id'];
createdBy = json['createdBy'];
createdDate = json['createdDate'];
modifiedBy = json['modifiedBy'];
modifiedDate = json['modifiedDate'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['reqNo'] = this.reqNo;
data['reqCode'] = this.reqCode;
data['employeeId'] = this.employeeId;
data['employeeName'] = this.employeeName;
data['employeeEmail'] = this.employeeEmail;
data['positionId'] = this.positionId;
data['positionName'] = this.positionName;
if (this.departments != null) {
data['departments'] = this.departments!.map((v) => v.toJson()).toList();
}
data['siteId'] = this.siteId;
data['siteName'] = this.siteName;
data['requesterExtensionNumber'] = this.requesterExtensionNumber;
data['requesterExtensionName'] = this.requesterExtensionName;
data['requesterExtensionPositionId'] = this.requesterExtensionPositionId;
data['requesterExtensionPositionName'] = this.requesterExtensionPositionName;
data['requestTypeId'] = this.requestTypeId;
data['requestTypeName'] = this.requestTypeName;
data['assetNDId'] = this.assetNDId;
data['modelId'] = this.modelId;
data['modelName'] = this.modelName;
data['manufacturerId'] = this.manufacturerId;
data['manufacturerName'] = this.manufacturerName;
data['assetId'] = this.assetId;
data['assetSerialNo'] = this.assetSerialNo;
data['assetNumber'] = this.assetNumber;
data['assetName'] = this.assetName;
data['assetAssetNDId'] = this.assetAssetNDId;
data['qty'] = this.qty;
data['purposeAnswer'] = this.purposeAnswer;
data['currentPractise'] = this.currentPractise;
data['censusQ1'] = this.censusQ1;
data['censusQ2'] = this.censusQ2;
data['censusQ3'] = this.censusQ3;
data['censusQ4'] = this.censusQ4;
if (this.trafContacts != null) {
data['trafContacts'] = this.trafContacts!.map((v) => v.toJson()).toList();
}
data['usingSolelyOrSharedId'] = this.usingSolelyOrSharedId;
data['usingSolelyOrSharedName'] = this.usingSolelyOrSharedName;
data['isEffectedId'] = this.isEffectedId;
data['isEffectedName'] = this.isEffectedName;
data['effectedServices'] = this.effectedServices;
data['isCombinationId'] = this.isCombinationId;
data['isCombinationName'] = this.isCombinationName;
data['usedWithCombination'] = this.usedWithCombination;
data['comment'] = this.comment;
if (this.attachments != null) {
data['attachments'] = this.attachments!.map((v) => v.toJson()).toList();
}
data['isBudgetId'] = this.isBudgetId;
data['isBudgetName'] = this.isBudgetName;
data['firstLineManagerId'] = this.firstLineManagerId;
data['firstLineManagerName'] = this.firstLineManagerName;
data['firstLineManagerApprovalId'] = this.firstLineManagerApprovalId;
data['firstLineManagerApprovalName'] = this.firstLineManagerApprovalName;
data['tlapiId'] = this.tlapiId;
data['tlapiName'] = this.tlapiName;
data['assessorEmployeeId'] = this.assessorEmployeeId;
data['assessorEmployeeName'] = this.assessorEmployeeName;
data['secondLineManagerId'] = this.secondLineManagerId;
data['secondLineManagerName'] = this.secondLineManagerName;
data['secondLineManagerApprovalId'] = this.secondLineManagerApprovalId;
data['secondLineManagerApprovalName'] = this.secondLineManagerApprovalName;
data['hospitalManagementId'] = this.hospitalManagementId;
data['hospitalManagementName'] = this.hospitalManagementName;
data['hospitalManagementApprovalId'] = this.hospitalManagementApprovalId;
data['hospitalManagementApprovalName'] = this.hospitalManagementApprovalName;
if (this.oracleCodes != null) {
data['oracleCodes'] = this.oracleCodes!.map((v) => v.toJson()).toList();
}
data['assessorTeamLeaderId'] = this.assessorTeamLeaderId;
data['assessorTeamLeaderName'] = this.assessorTeamLeaderName;
data['assessorTeamLeaderApprovalId'] = this.assessorTeamLeaderApprovalId;
data['assessorTeamLeaderApprovalName'] = this.assessorTeamLeaderApprovalName;
data['statusOfDRId'] = this.statusOfDRId;
data['statusOfDRName'] = this.statusOfDRName;
data['statusOfRequesterId'] = this.statusOfRequesterId;
data['statusOfRequesterName'] = this.statusOfRequesterName;
data['approved'] = this.approved;
data['approvalUserId'] = this.approvalUserId;
data['approvalUserName'] = this.approvalUserName;
data['approvalDate'] = this.approvalDate;
data['docDate'] = this.docDate;
data['approvalDocDate'] = this.approvalDocDate;
data['isAssigned'] = this.isAssigned;
data['poNumber'] = this.poNumber;
data['apiDirectorId'] = this.apiDirectorId;
data['apiDirectorName'] = this.apiDirectorName;
data['apiDirectorApprovalId'] = this.apiDirectorApprovalId;
data['apiDirectorApprovalName'] = this.apiDirectorApprovalName;
data['trafAssessIds'] = this.trafAssessIds;
data['trafPRIds'] = this.trafPRIds;
data['trafOfferIds'] = this.trafOfferIds;
data['id'] = this.id;
data['createdBy'] = this.createdBy;
data['createdDate'] = this.createdDate;
data['modifiedBy'] = this.modifiedBy;
data['modifiedDate'] = this.modifiedDate;
return data;
}
}
class TrafContacts {
int? id;
int? trafId;
String? name;
String? telephone;
String? notes;
TrafContacts({this.id, this.trafId, this.name, this.telephone, this.notes});
TrafContacts.fromJson(Map<String, dynamic> json) {
id = json['id'];
trafId = json['trafId'];
name = json['name'];
telephone = json['telephone'];
notes = json['notes'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['trafId'] = this.trafId;
data['name'] = this.name;
data['telephone'] = this.telephone;
data['notes'] = this.notes;
return data;
}
}
class OracleCodes {
int? id;
int? assetNDId;
int? codeTypeId;
String? codeTypeName;
int? codeTypeValue;
String? codeValue;
OracleCodes({this.id, this.assetNDId, this.codeTypeId, this.codeTypeName, this.codeTypeValue, this.codeValue});
OracleCodes.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;
}
}
class Departments {
int? id;
int? trafId;
int? departmentId;
Departments({this.id, this.trafId, this.departmentId});
Departments.fromJson(Map<String, dynamic> json) {
id = json['id'];
trafId = json['trafId'];
departmentId = json['departmentId'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['trafId'] = this.trafId;
data['departmentId'] = this.departmentId;
return data;
}
}
class Attachments {
int? id;
int? trafId;
String? attachmentName;
Attachments({this.id, this.trafId, this.attachmentName});
Attachments.fromJson(Map<String, dynamic> json) {
id = json['id'];
trafId = json['trafId'];
attachmentName = json['attachmentName'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['trafId'] = this.trafId;
data['attachmentName'] = this.attachmentName;
return data;
}
}

@ -0,0 +1,32 @@
import 'dart:convert';
import 'package:flutter/foundation.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 'traf_request_model.dart';
class TrafRequestProvider extends ChangeNotifier {
bool isLoading = false;
TrafRequestDataModel? trafRequestDataModel;
Future<int> getTRAFById(int trafId) async {
try {
isLoading = true;
notifyListeners();
Response response = await ApiManager.instance.get("${URLs.getTRAFById}?tRAFId=$trafId");
if (response.statusCode >= 200 && response.statusCode < 300) {
trafRequestDataModel = TrafRequestDataModel.fromJson(json.decode(response.body)["data"]);
}
isLoading = false;
notifyListeners();
return 0;
} catch (error) {
isLoading = false;
notifyListeners();
return -1;
}
}
}

@ -0,0 +1,224 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/models/generic_attachment_model.dart';
import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart';
import 'package:test_sa/modules/traf_module/traf_request_model.dart';
import 'package:test_sa/modules/traf_module/traf_request_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/multiple_item_drop_down_menu.dart';
import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart';
import 'package:test_sa/providers/lookups/classification_lookup_provider.dart';
import 'package:test_sa/providers/lookups/recommendation_lookup_provider.dart';
import 'package:test_sa/providers/lookups/request_type_lookup_provider.dart';
import 'package:test_sa/views/widgets/equipment/asset_picker.dart';
import 'package:test_sa/models/device/asset.dart';
import 'package:test_sa/views/widgets/images/multi_image_picker.dart';
class UpdateTrafRequestPage extends StatefulWidget {
static const String id = "/update-TRAF";
UpdateTrafRequestPage({Key? key}) : super(key: key);
@override
_UpdateTrafRequestPageState createState() {
return _UpdateTrafRequestPageState();
}
}
class _UpdateTrafRequestPageState extends State<UpdateTrafRequestPage> {
Lookup? requestType;
Lookup? assessorRecommendation;
Lookup? classificationType;
late TrafRequestProvider trafRequestProvider;
late TrafRequestDataModel trafRequest;
List<Lookup> abc = [];
List<Asset> _deviceList = [];
Asset? asset;
List<GenericAttachmentModel> attachments = [];
@override
void initState() {
super.initState();
Provider.of<ClassificationLookupProvider>(context, listen: false).reset();
Provider.of<RecommendationLookupProvider>(context, listen: false).reset();
trafRequestProvider = Provider.of<TrafRequestProvider>(context, listen: false);
trafRequest = trafRequestProvider.trafRequestDataModel!;
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const DefaultAppBar(title: "Update TRAF Request"),
body: Column(
children: [
SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SingleItemDropDownMenu<Lookup, ClassificationLookupProvider>(
context: context,
showAsBottomSheet: true,
backgroundColor: AppColor.neutral100,
showShadow: false,
title: "Classification",
initialValue: classificationType,
onSelect: (value) {
classificationType = value;
// trafRequest?.requestTypeId = value?.value;
setState(() {});
},
),
12.height,
SingleItemDropDownMenu<Lookup, RequestTypeLookupProvider>(
context: context,
showAsBottomSheet: true,
backgroundColor: AppColor.neutral100,
showShadow: false,
title: context.translation.requestType,
initialValue: requestType,
onSelect: (value) {
requestType = value;
setState(() {});
},
),
if (requestType?.id == 732) ...[
12.height,
AppTextFormField(
initialValue: "",
makeMultiLinesNull: true,
labelText: "Recommended Quantity",
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
showShadow: false,
textInputType: TextInputType.number,
onChange: (value) {
trafRequest.qty = int.tryParse(value);
},
),
],
if (requestType?.id == 733) ...[
12.height,
AssetPicker(
device: asset,
editable: false,
showLoading: false,
// borderColor: AppColor.black20,
// backgroundColor: AppColor.white936,
onPick: (asset) async {
this.asset = asset;
setState(() {});
// pendingAssetServiceRequest = null;
// _serviceRequest.device = asset;
// await checkAssetForPendingServiceRequest(asset.id!.toInt());
// if (pendingAssetServiceRequest != null && pendingAssetServiceRequest!.details!.isNotEmpty) {
// showPendingRequestBottomSheet();
// }
},
),
],
// 12.height,
// MultipleItemDropDownMenu<Lookup, RequestTypeLookupProvider>(
// context: context,
// showAsBottomSheet: true,
// backgroundColor: AppColor.neutral100,
// showShadow: false,
// showCancel: true,
// title: "Please specify departments and relations",
// initialValue: abc,
// onSelect: (value) {
// abc = value ?? [];
// // setState(() {
// //
// // });
// },
// ),
12.height,
Text(
"Quantity to be Transfer",
style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50, fontWeight: FontWeight.w500),
),
8.height,
AssetPicker(
deviceList: _deviceList,
showLoading: false,
borderColor: AppColor.black20,
buttonColor: AppColor.white936,
multiSelection: true,
onAssetRemove: (itemId) {
_deviceList.removeWhere((asset) => asset.id?.toInt() == itemId);
setState(() {});
},
onMultiAssetPick: (assetList) {
_deviceList.addAll(assetList);
final seenIds = <num?>{};
_deviceList = _deviceList.where((item) => seenIds.add(item.id)).toList();
setState(() {});
},
),
12.height,
SingleItemDropDownMenu<Lookup, RecommendationLookupProvider>(
context: context,
showAsBottomSheet: true,
backgroundColor: AppColor.neutral100,
showShadow: false,
title: "Assessor Recommendation",
initialValue: assessorRecommendation,
onSelect: (value) {
assessorRecommendation = value;
// trafRequest?.requestTypeId = value?.value;
setState(() {});
},
),
12.height,
AppTextFormField(
initialValue: "",
textInputType: TextInputType.multiline,
alignLabelWithHint: true,
labelText: "Assessor comment",
backgroundColor: AppColor.fieldBgColor(context),
labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)),
showShadow: false,
onChange: (value) {
trafRequest?.comment = value; // todo confirm parameter shaheer
},
),
12.height,
AttachmentPicker(
label: context.translation.attachFiles,
attachment: attachments,
buttonColor: AppColor.black10,
onlyImages: false,
buttonIcon: 'image-plus'.toSvgAsset(color: AppColor.neutral120),
),
],
).toShadowContainer(context, borderRadius: 20, padding: 12),
).expanded,
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(buttonColor: AppColor.primary10, label: "Update", onPressed: () {}
// buttonColor: AppColor.primary10,
),
),
],
));
}
}

@ -25,6 +25,7 @@ class AppTextFormField extends StatefulWidget {
final TextStyle? style;
final TextStyle? labelStyle;
final TextStyle? hintStyle;
final TextStyle? floatingLabelStyle;
final bool enable;
final TextAlign? textAlign;
final FocusNode? node;
@ -58,6 +59,7 @@ class AppTextFormField extends StatefulWidget {
this.hintText,
this.labelText,
this.hintStyle,
this.floatingLabelStyle,
this.textInputType = TextInputType.text,
this.initialValue, // Provide default value
this.enable = true,
@ -183,12 +185,16 @@ class _AppTextFormFieldState extends State<AppTextFormField> {
onChanged: widget.onChange,
obscureText: widget.obscureText ?? false,
keyboardType: widget.textInputType,
maxLines: widget.makeMultiLinesNull ? null:widget.textInputType == TextInputType.multiline ? 4 : 1,
maxLines: widget.makeMultiLinesNull
? null
: widget.textInputType == TextInputType.multiline
? 4
: 1,
obscuringCharacter: "*",
controller: widget.controller,
textInputAction: widget.textInputType == TextInputType.multiline ? null : widget.textInputAction ?? TextInputAction.next,
onEditingComplete: widget.onAction ?? () => FocusScope.of(context).nextFocus(),
style: widget.style ?? AppTextStyle.body1.copyWith(fontWeight: FontWeight.w500,color:context.isDark?AppColor.white10: AppColor.black10),
style: widget.style ?? AppTextStyle.body1.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.white10 : AppColor.black10),
onTap: widget.onTap,
decoration: InputDecoration(
alignLabelWithHint: widget.alignLabelWithHint,
@ -208,9 +214,12 @@ class _AppTextFormFieldState extends State<AppTextFormField> {
? (widget.enableColor ?? AppColor.neutral40)
: AppColor.background(context)),
errorStyle: AppTextStyle.tiny.copyWith(color: context.isDark ? AppColor.red50 : AppColor.red60),
floatingLabelStyle: AppTextStyle.body1.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.white10 : AppColor.neutral20),
floatingLabelStyle: widget.floatingLabelStyle ?? AppTextStyle.body1.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.white10 : AppColor.neutral20),
hintText: widget.hintText ?? "",
labelText: (widget.showSpeechToText && _speechToText.isListening) ? "Listening..." : widget.labelText ?? "",
// labelText: (widget.showSpeechToText && _speechToText.isListening) ? "Listening..." : widget.labelText ?? "",
label: Text(
(widget.showSpeechToText && _speechToText.isListening) ? "Listening..." : widget.labelText ?? "",
),
labelStyle: widget.labelStyle,
hintStyle: widget.labelStyle,
prefixIcon: widget.prefixIcon ??

@ -0,0 +1,251 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/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/new_views/common_widgets/app_loading_manager.dart';
import 'package:test_sa/providers/loading_list_notifier.dart';
import 'package:test_sa/views/widgets/bottom_sheets/selection_bottom_sheet.dart';
import 'package:test_sa/views/widgets/fullscreen_dialogs/multiple_selection_fullscreen_dialog.dart';
import 'package:test_sa/views/widgets/fullscreen_dialogs/selection_fullscreen_dialog.dart';
import '../../models/base.dart';
import '../app_style/app_color.dart';
class MultipleItemDropDownMenu<T extends Base, X extends LoadingListNotifier> extends StatefulWidget {
final BuildContext context;
final Function(List<T>?)? onSelect; // Now accepts nullable values
final List<T> initialValue;
final T? disableValue;
final bool enabled;
final bool showAsBottomSheet;
final bool showAsFullScreenDialog;
final List<T>? staticData;
final String title;
final double? height;
final bool showShadow;
final bool showCancel;
final Color? backgroundColor; // Now nullable
final bool? loading; // Now nullable
/// To use a static data (without calling API)
/// just send [NullableLoadingProvider] as generic data type and fill the [staticData]
const MultipleItemDropDownMenu({
Key? key,
this.title = "",
required this.context,
this.onSelect,
this.initialValue = const [],
this.disableValue,
this.enabled = true,
this.height,
this.showAsBottomSheet = false,
this.showAsFullScreenDialog = true,
this.staticData, // Provide a default empty list
this.showShadow = true,
this.showCancel = false,
this.backgroundColor,
this.loading,
}) : super(key: key);
@override
State<MultipleItemDropDownMenu<T, X>> createState() => _MultipleItemDropDownMenuState<T, X>();
}
class _MultipleItemDropDownMenuState<T extends Base, X extends LoadingListNotifier> extends State<MultipleItemDropDownMenu<T, X>> {
List<T> _selectedItem = [];
X? provider;
@override
void initState() {
if (X != NullableLoadingProvider) {
provider = Provider.of<X>(widget.context);
}
if (widget.initialValue.isNotEmpty) {
// Get the source list
final sourceList = (X == NullableLoadingProvider ? widget.staticData : provider?.items);
if (sourceList != null && sourceList.isNotEmpty) {
// Collect matches based on identifier
final results = sourceList.where((element) => widget.initialValue!.any((init) => init.identifier == element.identifier)).toList();
if (results.isNotEmpty) {
_selectedItem
..clear()
..addAll(results.cast<T>());
// Call onSelect if items changed
if (widget.onSelect != null) {
widget.onSelect!(_selectedItem);
}
}
}
}
// if (widget.initialValue != null) {
// final result = (X == NullableLoadingProvider ? widget.staticData : provider?.items)?.where((element) => element.identifier == widget.initialValue?.identifier);
// if (result?.isNotEmpty ?? false) _selectedItem.add(result!.first as T);
// if (widget.onSelect != null && (widget.initialValue?.identifier ?? "") != (_selectedItem?.identifier ?? "")) {
// widget.onSelect!(_selectedItem); // Non-null assertion after null check
// }
// }
super.initState();
}
@override
void setState(VoidCallback fn) {
if (mounted) super.setState(fn);
}
@override
void didUpdateWidget(covariant MultipleItemDropDownMenu<T, X> oldWidget) {
if (widget.initialValue.isNotEmpty) {
// Get the source list
final sourceList = (X == NullableLoadingProvider ? widget.staticData : provider?.items);
if (sourceList != null && sourceList.isNotEmpty) {
// Collect matches based on identifier
final results = sourceList.where((element) => widget.initialValue!.any((init) => init.identifier == element.identifier)).toList();
if (results.isNotEmpty) {
_selectedItem
..clear()
..addAll(results.cast<T>());
// Call onSelect if items changed
if (widget.onSelect != null) {
widget.onSelect!(_selectedItem);
}
}
}
}
// if (widget.initialValue != null) {
// final result = (X == NullableLoadingProvider ? widget.staticData : provider?.items)?.where((element) => element.identifier == widget.initialValue?.identifier);
//
// if (result?.isNotEmpty ?? false) {
// _selectedItem.add(result!.first as T);
// } else {
// _selectedItem = [];
// }
// // if (widget.onSelect != null && (widget.initialValue?.identifier ?? "") != (_selectedItem?.identifier ?? "")) {
// // widget.onSelect!(_selectedItem); // Non-null assertion after null check
// // }
// } else {
// _selectedItem = [];
// }
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
final isEmpty = (X == NullableLoadingProvider ? widget.staticData : provider?.items)?.isEmpty ?? true;
return AppLoadingManager(
isLoading: widget.loading ?? ((X == NullableLoadingProvider) ? false : provider?.loading ?? false),
// Provide default value if null
isFailedLoading: (X == NullableLoadingProvider) ? false : provider?.items == null,
stateCode: (X == NullableLoadingProvider) ? 200 : provider?.stateCode,
onRefresh: () async {
if (X != NullableLoadingProvider) {
provider?.reset();
await provider?.getData();
}
},
child: Container(
// height: widget.height ?? 60.toScreenHeight,
padding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth, vertical: 8.toScreenHeight),
decoration: BoxDecoration(
color: context.isDark && (widget.enabled == false || isEmpty)
? AppColor.neutral20
: (widget.enabled == false || isEmpty)
? AppColor.neutral40
: widget.backgroundColor ?? AppColor.background(context),
borderRadius: BorderRadius.circular(10),
boxShadow: widget.showShadow ? [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)] : null,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Text(
widget.title,
style: Theme.of(context).textTheme.bodySmall?.copyWith(color: context.isDark ? null : AppColor.neutral20, fontWeight: FontWeight.w500),
),
if (_selectedItem.isEmpty)
Text(
context.translation.select,
style: Theme.of(context).textTheme.bodyLarge,
)
else
ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: const EdgeInsets.only(top: 8),
separatorBuilder: (cxt, index) => const Divider(
thickness: 1,
color: Colors.white,
),
itemBuilder: (cxt, index) => Text(
_selectedItem[index].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),
),
itemCount: _selectedItem.length,
),
],
)
.onPress(
widget.enabled && widget.showAsFullScreenDialog
? () {
openDialog();
}
: null,
)
.expanded,
if (widget.enabled) const PositionedDirectional(end: 0, child: Icon(Icons.keyboard_arrow_down_rounded)),
],
),
),
);
}
void openDialog() async {
Widget child = MultipleSelectionFullScreenDialog<T>(
// Specify generic type
items: ((X == NullableLoadingProvider) ? widget.staticData : provider?.items as List<T>) ?? [],
// Provide default empty list if null
selectedItem: _selectedItem,
disableItem: widget.disableValue,
title: widget.title,
showCancel: widget.showCancel,
onSelect: (selectedT) {
setState(() {
_selectedItem = selectedT;
});
widget.onSelect!(selectedT);
},
builderString: (emp) => emp?.name ?? "", // Null-aware operator for emp.name
);
pushRouter(child);
// final selectedT = await pushRouter(child);
// if (selectedT != null) {
// setState(() {
// _selectedItem = selectedT;
// });
// widget.onSelect!(selectedT); // Non-null assertion after null check
// }
}
Future<T?> pushRouter(Widget child) async {
// PageRoute<T> pRoute = !Platform.isIOS ? CupertinoPageRoute(fullscreenDialog: true, builder: (context) => child) : MaterialPageRoute(fullscreenDialog: true, builder: (context) => child);
return await Navigator.of(context).push(CupertinoPageRoute(fullscreenDialog: true, builder: (context) => child));
}
}

@ -168,50 +168,50 @@ class _SingleItemDropDownMenuState<T extends Base, X extends LoadingListNotifier
),
);
}).toList(),
).onPress(widget.enabled && widget.showAsFullScreenDialog
? () {
openDialog();
}
: 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;
});
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();
}
: 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;
});
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),
],
),
),

@ -3,9 +3,11 @@ import 'package:provider/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/string_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/modules/cm_module/views/nurse/create_new_request_view.dart';
import 'package:test_sa/modules/tm_module/tasks_wo/create_task_view.dart';
import 'package:test_sa/modules/traf_module/create_traf_request_page.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/pages/gas_refill_request_form.dart';
import 'package:test_sa/views/pages/device_transfer/create__device_transfer_request.dart';
@ -101,6 +103,7 @@ class CreateRequestModel {
list.add(CreateRequestModel(context.translation.transferAsset, "add_icon", CreateDeviceTransferRequest.id));
//TODO uncommit this to enable task.
list.add(CreateRequestModel(context.translation.task, "add_icon", CreateTaskView.id));
list.add(CreateRequestModel("Traf".addTranslation, "add_icon", CreateTRAFRequestPage.id));
}
return list;
}

@ -21,9 +21,7 @@ class GasRefillItemView extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (requestData != null) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -144,6 +142,6 @@ class GasRefillItemView extends StatelessWidget {
});
}
return const SizedBox();
return const SizedBox();
}
}

@ -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 ClassificationLookupProvider extends LoadingListNotifier<Lookup> {
@override
Future getData() async {
if (loading == true) return -2;
loading = true;
notifyListeners();
try {
Response response = await ApiManager.instance.get(URLs.getClassificationTypeLookup);
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,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 RecommendationLookupProvider extends LoadingListNotifier<Lookup> {
@override
Future getData() async {
if (loading == true) return -2;
loading = true;
notifyListeners();
try {
Response response = await ApiManager.instance.get(URLs.getRecommendationTypeLookup);
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,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 RequestTypeLookupProvider extends LoadingListNotifier<Lookup> {
@override
Future getData() async {
if (loading == true) return -2;
loading = true;
notifyListeners();
try {
Response response = await ApiManager.instance.get(URLs.getTrafRequestTypeLookup);
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,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 YesNoLookupProvider extends LoadingListNotifier<Lookup> {
@override
Future getData() async {
if (loading == true) return -2;
loading = true;
notifyListeners();
try {
Response response = await ApiManager.instance.get(URLs.getYesNoRequestTypeLookup);
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,172 @@
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/modules/cm_module/views/components/action_button/footer_action_button.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
typedef SelectionBuilderString = String Function(dynamic);
class MultipleSelectionBottomSheet<T> extends StatefulWidget {
final List<T>? items;
final List<T> selectedItem; // Now nullable
final String title;
final SelectionBuilderString builderString;
final bool showCancel;
final Function(List<T>) onSelect;
const MultipleSelectionBottomSheet({Key? key, this.items = const [], this.selectedItem = const [], this.title = "", required this.builderString, this.showCancel = false, required this.onSelect})
: super(key: key);
@override
_MultipleSelectionBottomSheetState createState() => _MultipleSelectionBottomSheetState<T>();
}
class _MultipleSelectionBottomSheetState<T> extends State<MultipleSelectionBottomSheet<T>> {
late List<T> _selectedValue; // Now nullable
String query = "";
List<T>? get filteredList => widget.items?.where((element) => widget.builderString(element).toLowerCase().contains(query.toLowerCase())).toList();
@override
void initState() {
_selectedValue = widget.selectedItem;
super.initState();
}
FocusNode searchFocusNode = FocusNode();
@override
void dispose() {
searchFocusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height * .7,
color: Theme.of(context).scaffoldBackgroundColor,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
widget.title.heading5(context).expanded,
if (widget.showCancel)
AnimatedOpacity(
opacity: _selectedValue != null ? 1 : 0,
duration: const Duration(milliseconds: 250),
child: Container(
height: 30,
decoration: BoxDecoration(color: const Color(0xffF63939).withOpacity(.75), borderRadius: BorderRadius.circular(30)),
padding: const EdgeInsets.only(left: 8, right: 12, top: 4, bottom: 4),
alignment: Alignment.center,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.clear, color: Colors.white, size: 16),
4.width,
const Text(
"Clear",
style: TextStyle(fontSize: 14, color: Colors.white),
)
],
),
).onPress(_selectedValue.isNotEmpty
? () {
Navigator.pop(context);
widget.onSelect([]);
}
: null),
),
],
).paddingOnly(top: 16, start: 16, end: 16, bottom: 0),
TextField(
onChanged: (queryString) {
query = queryString;
setState(() {});
},
style: const TextStyle(fontSize: 14),
focusNode: searchFocusNode,
decoration: InputDecoration(
hintText: 'Search by name',
labelText: 'Search',
labelStyle: TextStyle(color: AppColor.textColor(context)),
filled: true,
fillColor: AppColor.fieldBgColor(context),
hintStyle: const TextStyle(fontSize: 14),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: AppColor.blueStatus(context), width: 2.0),
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: AppColor.blueStatus(context), width: 1.0),
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
),
contentPadding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
),
).paddingOnly(top: 16, start: 16, end: 16, bottom: 16),
Expanded(
// Wrap ListView with Expanded
child: ListView.builder(
itemCount: filteredList?.length,
padding: EdgeInsets.zero,
itemBuilder: (cxt, index) => Theme(
data: Theme.of(context).copyWith(
radioTheme: RadioThemeData(
fillColor: MaterialStateColor.resolveWith((states) {
if (states.contains(MaterialState.selected)) {
return AppColor.textColor(context); // Active color
}
return Colors.grey; // Inactive color
}),
),
),
child: CheckboxListTile(
value: checkItContains(filteredList![index]),
dense: true,
// groupValue: _selectedValue,
activeColor: AppColor.textColor(context),
contentPadding: const EdgeInsets.only(left: 16, right: 16),
onChanged: (value) {
if (value == true) {
_selectedValue.add(filteredList![index]);
} else if (value == false) {
_selectedValue.remove(filteredList![index]);
}
searchFocusNode.unfocus();
setState(() {});
},
title: Text(
widget.builderString(filteredList![index]).cleanupWhitespace.capitalizeFirstOfEach ?? "",
style: Theme.of(context).textTheme.bodyLarge,
),
),
),
),
),
8.height,
if (_selectedValue.isNotEmpty)
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
label: context.translation.select,
maxWidth: true,
onPressed: () {
Navigator.pop(context);
widget.onSelect(_selectedValue);
},
)),
],
),
);
}
bool? checkItContains(T) {
return widget.items?.contains(T);
}
}

@ -0,0 +1,196 @@
import 'package:flutter/material.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/models/base.dart';
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
typedef SelectionBuilderString = String Function(dynamic);
class MultipleSelectionFullScreenDialog<T extends Base> extends StatefulWidget {
final List<T>? items;
final List<T> selectedItem; // Now nullable
final T? disableItem; // Now nullable
final String title;
final bool showCancel;
final SelectionBuilderString builderString;
final Function(List<T>) onSelect;
const MultipleSelectionFullScreenDialog(
{Key? key, this.items, this.selectedItem = const [], this.disableItem, this.title = "", required this.builderString, this.showCancel = false, required this.onSelect})
: super(key: key);
@override
_SelectionBottomSheetState createState() => _SelectionBottomSheetState<T>();
}
class _SelectionBottomSheetState<T extends Base> extends State<MultipleSelectionFullScreenDialog<T>> {
late List<T> _selectedValue; // Now nullable
String query = "";
List<T>? get filteredList => widget.items?.where((element) => widget.builderString(element).toLowerCase().contains(query.toLowerCase())).toList();
@override
void initState() {
_selectedValue = widget.selectedItem;
super.initState();
}
FocusNode searchFocusNode = FocusNode();
@override
void dispose() {
searchFocusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
for (var abc in filteredList ?? []) {
print(abc.toJson());
}
return Scaffold(
appBar: AppBar(
title: widget.title.heading5(context),
actions: [
if (widget.showCancel)
AnimatedContainer(
height: 24,
duration: const Duration(milliseconds: 250),
margin: const EdgeInsets.only(right: 16),
decoration: BoxDecoration(color: _selectedValue.isNotEmpty ? const Color(0xffF63939).withOpacity(.75) : Colors.grey.withOpacity(.75), borderRadius: BorderRadius.circular(30)),
padding: const EdgeInsets.only(left: 4, right: 8, top: 4, bottom: 4),
alignment: Alignment.center,
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Icon(Icons.clear, color: Colors.white, size: 16),
4.width,
const Text(
"Clear",
style: TextStyle(fontSize: 14, color: Colors.white, height: 1),
)
],
).onPress(_selectedValue.isNotEmpty
? () {
Navigator.pop(context);
widget.onSelect([]);
}
: null),
),
],
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SearchBar(
focusNode: searchFocusNode,
elevation: WidgetStateProperty.all<double>(0),
backgroundColor: WidgetStateProperty.all<Color?>(context.isDark ? AppColor.neutral120 : null),
leading: Icon(Icons.search, color: AppColor.iconColor(context)),
textStyle: WidgetStateProperty.all<TextStyle>(
TextStyle(color: AppColor.textColor(context), fontSize: 16.0),
),
hintStyle: WidgetStateProperty.all<TextStyle>(
TextStyle(color: AppColor.textColor(context), fontSize: 14.0),
),
hintText: 'Search by name',
onChanged: (queryString) {
query = queryString;
setState(() {});
},
).paddingOnly(top: 16, start: 16, end: 16, bottom: 8),
// TextField(
// onChanged: (queryString) {
// query = queryString;
// setState(() {});
// },
// style: const TextStyle(fontSize: 14),
// focusNode: searchFocusNode,
// decoration: InputDecoration(
// hintText: 'Search by name',
// labelText: 'Search',
// hintStyle: const TextStyle(fontSize: 14),
// focusedBorder: OutlineInputBorder(
// borderSide: BorderSide(color: AppColor.blueStatus(context), width: 2.0),
// borderRadius: const BorderRadius.all(Radius.circular(12.0)),
// ),
// enabledBorder: OutlineInputBorder(
// borderSide: BorderSide(color: AppColor.blueStatus(context), width: 1.0),
// borderRadius: const BorderRadius.all(Radius.circular(12.0)),
// ),
// contentPadding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
// ),
// ),
8.height,
Expanded(
child: ListView.builder(
itemCount: filteredList?.length,
padding: EdgeInsets.zero,
itemBuilder: (cxt, index) {
bool isDisabledItem = widget.disableItem != null && widget.disableItem?.identifier == filteredList![index].identifier;
return Theme(
data: Theme.of(context).copyWith(
radioTheme: RadioThemeData(
fillColor: MaterialStateColor.resolveWith((states) {
if (states.contains(MaterialState.selected)) {
return AppColor.iconColor(context); // Active color
}
return Colors.grey; // Inactive color
}),
),
),
child: CheckboxListTile(
value: checkItContains(filteredList![index]),
dense: true,
controlAffinity: ListTileControlAffinity.leading,
activeColor: AppColor.textColor(context),
contentPadding: const EdgeInsets.only(left: 16, right: 16),
onChanged: (value) {
// if (checkItContains(filteredList![index]) ?? false) {
// _selectedValue.remove(filteredList![index]);
// } else {
// _selectedValue.add(filteredList![index]);
// }
if (value == true) {
_selectedValue.add(filteredList![index]);
} else if (value == false) {
_selectedValue.remove(filteredList![index]);
}
searchFocusNode.unfocus();
setState(() {});
},
title: Text(
widget.builderString(filteredList![index]).cleanupWhitespace.capitalizeFirstOfEach ?? "",
style: Theme.of(context).textTheme.bodyLarge,
),
),
);
}),
),
8.height,
if (_selectedValue.isNotEmpty)
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
label: context.translation.select,
maxWidth: true,
onPressed: () {
Navigator.pop(context);
widget.onSelect(_selectedValue);
},
)),
],
),
);
}
bool? checkItContains(T) {
return _selectedValue.contains(T);
}
}

@ -87,9 +87,7 @@ class _SelectionBottomSheetState<T extends Base> extends State<SelectionFullScre
SearchBar(
focusNode: searchFocusNode,
elevation: WidgetStateProperty.all<double>(0),
backgroundColor: WidgetStateProperty.all<Color>(
AppColor.fieldBgColor(context), // Your custom background color
),
backgroundColor: WidgetStateProperty.all<Color?>(context.isDark ? AppColor.neutral120 : null),
leading: Icon(Icons.search, color: AppColor.iconColor(context)),
textStyle: WidgetStateProperty.all<TextStyle>(
TextStyle(color: AppColor.textColor(context), fontSize: 16.0),

Loading…
Cancel
Save