From 56328b81b463fb3e7d4bbb886b8e3c3631470f78 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Thu, 9 Oct 2025 17:47:24 +0300 Subject: [PATCH] traf module added, create, view details, update. cont --- lib/controllers/api_routes/urls.dart | 11 +- .../providers/api/oracle_code_provider.dart | 119 ++++ lib/main.dart | 33 +- .../asset_auto_complete_field.dart | 131 +++++ .../traf_module/create_traf_request_page.dart | 381 ++++++++++++ .../traf_module/traf_request_detail_page.dart | 161 +++++ .../traf_module/traf_request_item_view.dart | 0 .../traf_module/traf_request_model.dart | 551 ++++++++++++++++++ .../traf_module/traf_request_provider.dart | 32 + .../traf_module/update_traf_request_page.dart | 224 +++++++ .../common_widgets/app_text_form_field.dart | 17 +- .../multiple_item_drop_down_menu.dart | 251 ++++++++ .../single_item_drop_down_menu.dart | 86 +-- .../create_request-type_bottomsheet.dart | 3 + .../requests/gas_refill_item_view.dart | 4 +- .../classification_lookup_provider.dart | 33 ++ .../recommendation_lookup_provider.dart | 33 ++ .../lookups/request_type_lookup_provider.dart | 33 ++ .../lookups/yes_no_lookup_provider.dart | 33 ++ .../muliple_selection_bottom_sheet.dart | 172 ++++++ .../multiple_selection_fullscreen_dialog.dart | 196 +++++++ .../selection_fullscreen_dialog.dart | 4 +- 22 files changed, 2440 insertions(+), 68 deletions(-) create mode 100644 lib/controllers/providers/api/oracle_code_provider.dart create mode 100644 lib/modules/traf_module/asset_auto_complete_field.dart create mode 100644 lib/modules/traf_module/create_traf_request_page.dart create mode 100644 lib/modules/traf_module/traf_request_detail_page.dart create mode 100644 lib/modules/traf_module/traf_request_item_view.dart create mode 100644 lib/modules/traf_module/traf_request_model.dart create mode 100644 lib/modules/traf_module/traf_request_provider.dart create mode 100644 lib/modules/traf_module/update_traf_request_page.dart create mode 100644 lib/new_views/common_widgets/multiple_item_drop_down_menu.dart create mode 100644 lib/providers/lookups/classification_lookup_provider.dart create mode 100644 lib/providers/lookups/recommendation_lookup_provider.dart create mode 100644 lib/providers/lookups/request_type_lookup_provider.dart create mode 100644 lib/providers/lookups/yes_no_lookup_provider.dart create mode 100644 lib/views/widgets/bottom_sheets/muliple_selection_bottom_sheet.dart create mode 100644 lib/views/widgets/fullscreen_dialogs/multiple_selection_fullscreen_dialog.dart diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index 2cac62b1..9d4ef5db 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -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 diff --git a/lib/controllers/providers/api/oracle_code_provider.dart b/lib/controllers/providers/api/oracle_code_provider.dart new file mode 100644 index 00000000..df0acb98 --- /dev/null +++ b/lib/controllers/providers/api/oracle_code_provider.dart @@ -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? _parts; + + List? 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 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 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> getAssetByOracleCode(String oracleCode) async { + late Response response; + try { + response = await ApiManager.instance.post(URLs.getPartNumber, body: {"oracleCode": oracleCode}); + List 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> getPartsListByDisplayName({num? assetId, String? displayName}) async { +// late Response response; +// try { +// response = await ApiManager.instance.post(URLs.getPartNumber, body: {"displayName": displayName, }); +// List 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 []; +// } +// } +} diff --git a/lib/main.dart b/lib/main.dart index e60fb1e8..30daa9c1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -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(), diff --git a/lib/modules/traf_module/asset_auto_complete_field.dart b/lib/modules/traf_module/asset_auto_complete_field.dart new file mode 100644 index 00000000..df4a042e --- /dev/null +++ b/lib/modules/traf_module/asset_auto_complete_field.dart @@ -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 { + late OracleCodeProvider _oracleCodeProvider; + + late TextEditingController _controller; + + bool loading = false; + + @override + void initState() { + _controller = TextEditingController(text: widget.initialValue); + super.initState(); + _oracleCodeProvider = Provider.of(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( + optionsBuilder: (TextEditingValue textEditingValue) async { + if (textEditingValue.text.isEmpty) { + if (loading) { + setState(() { + loading = false; + }); + } + return const Iterable.empty(); + } + if (!loading) { + setState(() { + loading = true; + }); + } + List 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); + }, + ), + ); + } +} diff --git a/lib/modules/traf_module/create_traf_request_page.dart b/lib/modules/traf_module/create_traf_request_page.dart new file mode 100644 index 00000000..f1b62e0e --- /dev/null +++ b/lib/modules/traf_module/create_traf_request_page.dart @@ -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 { + final GlobalKey _formKey = GlobalKey(); + + bool _acknowledgement = false; + + Asset? asset; + + Lookup? requestType; + Lookup? isUsedSolelyOrShared; + Lookup? otherServicesEffects; + Lookup? useInCombination; + + TrafRequestDataModel? trafRequest; + + List abc = []; + + @override + void initState() { + super.initState(); + trafRequest = TrafRequestDataModel(); + resetProviders(); + } + + void resetProviders() { + Provider.of(context, listen: false).reset(); + // Provider.of(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( + // context: context, + // height: 56.toScreenHeight, + // title: context.translation.taskType, + // showShadow: false, + // backgroundColor: AppColor.fieldBgColor(context), + // showAsBottomSheet: true, + // initialValue: selectedType, + // onSelect: (type) {}, + // ), + SingleItemDropDownMenu( + 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( + 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( + 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( + 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( + 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( + 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))); + } +} diff --git a/lib/modules/traf_module/traf_request_detail_page.dart b/lib/modules/traf_module/traf_request_detail_page.dart new file mode 100644 index 00000000..4b763736 --- /dev/null +++ b/lib/modules/traf_module/traf_request_detail_page.dart @@ -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 { + @override + void initState() { + super.initState(); + Provider.of(context, listen: false).getTRAFById(widget.trafId); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + bool isEngineer = (Provider.of(context, listen: false).user?.type) == UsersTypes.engineer; + return Scaffold( + appBar: const DefaultAppBar(title: "TRAF Request"), + body: Selector( + selector: (_, myModel) => myModel.isLoading, // Selects only the userName + builder: (_, isLoading, __) { + if (isLoading) return const ALoading(); + TrafRequestProvider trafProvider = Provider.of(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); + } +} diff --git a/lib/modules/traf_module/traf_request_item_view.dart b/lib/modules/traf_module/traf_request_item_view.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/modules/traf_module/traf_request_model.dart b/lib/modules/traf_module/traf_request_model.dart new file mode 100644 index 00000000..5705b6d0 --- /dev/null +++ b/lib/modules/traf_module/traf_request_model.dart @@ -0,0 +1,551 @@ +class TrafRequestModel { + List? 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 json) { + if (json['TrafRequestDataModel'] != null) { + 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 toJson() { + final Map data = new Map(); + 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; + 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; + int? usingSolelyOrSharedId; + String? usingSolelyOrSharedName; + int? isEffectedId; + String? isEffectedName; + String? effectedServices; + int? isCombinationId; + String? isCombinationName; + String? usedWithCombination; + String? comment; + List? 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; + 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? trafAssessIds; + List? trafPRIds; + List? 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 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 = []; + 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 = []; + 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 = []; + 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 = []; + 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(); + trafPRIds = json['trafPRIds'].cast(); + trafOfferIds = json['trafOfferIds'].cast(); + id = json['id']; + createdBy = json['createdBy']; + createdDate = json['createdDate']; + modifiedBy = json['modifiedBy']; + modifiedDate = json['modifiedDate']; + } + + Map toJson() { + final Map data = new Map(); + 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 json) { + id = json['id']; + trafId = json['trafId']; + name = json['name']; + telephone = json['telephone']; + notes = json['notes']; + } + + Map toJson() { + final Map data = new Map(); + 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 json) { + id = json['id']; + assetNDId = json['assetNDId']; + codeTypeId = json['codeTypeId']; + codeTypeName = json['codeTypeName']; + codeTypeValue = json['codeTypeValue']; + codeValue = json['codeValue']; + } + + Map toJson() { + final Map data = new Map(); + 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 json) { + id = json['id']; + trafId = json['trafId']; + departmentId = json['departmentId']; + } + + Map toJson() { + final Map data = new Map(); + 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 json) { + id = json['id']; + trafId = json['trafId']; + attachmentName = json['attachmentName']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['trafId'] = this.trafId; + data['attachmentName'] = this.attachmentName; + return data; + } +} diff --git a/lib/modules/traf_module/traf_request_provider.dart b/lib/modules/traf_module/traf_request_provider.dart new file mode 100644 index 00000000..85459f5c --- /dev/null +++ b/lib/modules/traf_module/traf_request_provider.dart @@ -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 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; + } + } +} diff --git a/lib/modules/traf_module/update_traf_request_page.dart b/lib/modules/traf_module/update_traf_request_page.dart new file mode 100644 index 00000000..4fcea820 --- /dev/null +++ b/lib/modules/traf_module/update_traf_request_page.dart @@ -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 { + Lookup? requestType; + Lookup? assessorRecommendation; + Lookup? classificationType; + late TrafRequestProvider trafRequestProvider; + + late TrafRequestDataModel trafRequest; + + List abc = []; + + List _deviceList = []; + Asset? asset; + List attachments = []; + + @override + void initState() { + super.initState(); + Provider.of(context, listen: false).reset(); + Provider.of(context, listen: false).reset(); + trafRequestProvider = Provider.of(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( + 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( + 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( + // 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 = {}; + _deviceList = _deviceList.where((item) => seenIds.add(item.id)).toList(); + setState(() {}); + }, + ), + 12.height, + SingleItemDropDownMenu( + 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, + ), + ), + ], + )); + } +} diff --git a/lib/new_views/common_widgets/app_text_form_field.dart b/lib/new_views/common_widgets/app_text_form_field.dart index 2434230c..02cb825b 100644 --- a/lib/new_views/common_widgets/app_text_form_field.dart +++ b/lib/new_views/common_widgets/app_text_form_field.dart @@ -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 { 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 { ? (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 ?? diff --git a/lib/new_views/common_widgets/multiple_item_drop_down_menu.dart b/lib/new_views/common_widgets/multiple_item_drop_down_menu.dart new file mode 100644 index 00000000..723b3faf --- /dev/null +++ b/lib/new_views/common_widgets/multiple_item_drop_down_menu.dart @@ -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 extends StatefulWidget { + final BuildContext context; + final Function(List?)? onSelect; // Now accepts nullable values + final List initialValue; + final T? disableValue; + final bool enabled; + final bool showAsBottomSheet; + final bool showAsFullScreenDialog; + final List? 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> createState() => _MultipleItemDropDownMenuState(); +} + +class _MultipleItemDropDownMenuState extends State> { + List _selectedItem = []; + X? provider; + + @override + void initState() { + if (X != NullableLoadingProvider) { + provider = Provider.of(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()); + + // 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 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()); + + // 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( + // Specify generic type + items: ((X == NullableLoadingProvider) ? widget.staticData : provider?.items as List) ?? [], + // 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 pushRouter(Widget child) async { + // PageRoute 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)); + } +} diff --git a/lib/new_views/common_widgets/single_item_drop_down_menu.dart b/lib/new_views/common_widgets/single_item_drop_down_menu.dart index c36af1a3..02e0e16a 100644 --- a/lib/new_views/common_widgets/single_item_drop_down_menu.dart +++ b/lib/new_views/common_widgets/single_item_drop_down_menu.dart @@ -168,50 +168,50 @@ class _SingleItemDropDownMenuState( - // Specify return type - context: context, - isScrollControlled: true, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical( - top: Radius.circular(20), - ), - ), - clipBehavior: Clip.antiAliasWithSaveLayer, - builder: (BuildContext context) => SelectionBottomSheet( - // Specify generic type - items: ((X == NullableLoadingProvider) ? widget.staticData : provider?.items as List) ?? [], - // 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( + // Specify return type + context: context, + isScrollControlled: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(20), + ), + ), + clipBehavior: Clip.antiAliasWithSaveLayer, + builder: (BuildContext context) => SelectionBottomSheet( + // Specify generic type + items: ((X == NullableLoadingProvider) ? widget.staticData : provider?.items as List) ?? [], + // 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), ], ), ), diff --git a/lib/new_views/pages/land_page/create_request-type_bottomsheet.dart b/lib/new_views/pages/land_page/create_request-type_bottomsheet.dart index edd2ba89..ff090f7b 100644 --- a/lib/new_views/pages/land_page/create_request-type_bottomsheet.dart +++ b/lib/new_views/pages/land_page/create_request-type_bottomsheet.dart @@ -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; } diff --git a/lib/new_views/pages/land_page/requests/gas_refill_item_view.dart b/lib/new_views/pages/land_page/requests/gas_refill_item_view.dart index bad36d00..397ac41c 100644 --- a/lib/new_views/pages/land_page/requests/gas_refill_item_view.dart +++ b/lib/new_views/pages/land_page/requests/gas_refill_item_view.dart @@ -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(); } } diff --git a/lib/providers/lookups/classification_lookup_provider.dart b/lib/providers/lookups/classification_lookup_provider.dart new file mode 100644 index 00000000..87b57efb --- /dev/null +++ b/lib/providers/lookups/classification_lookup_provider.dart @@ -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 { + @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; + } + } +} diff --git a/lib/providers/lookups/recommendation_lookup_provider.dart b/lib/providers/lookups/recommendation_lookup_provider.dart new file mode 100644 index 00000000..a0bbe48a --- /dev/null +++ b/lib/providers/lookups/recommendation_lookup_provider.dart @@ -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 { + @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; + } + } +} diff --git a/lib/providers/lookups/request_type_lookup_provider.dart b/lib/providers/lookups/request_type_lookup_provider.dart new file mode 100644 index 00000000..cc94de62 --- /dev/null +++ b/lib/providers/lookups/request_type_lookup_provider.dart @@ -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 { + @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; + } + } +} diff --git a/lib/providers/lookups/yes_no_lookup_provider.dart b/lib/providers/lookups/yes_no_lookup_provider.dart new file mode 100644 index 00000000..a089069b --- /dev/null +++ b/lib/providers/lookups/yes_no_lookup_provider.dart @@ -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 { + @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; + } + } +} diff --git a/lib/views/widgets/bottom_sheets/muliple_selection_bottom_sheet.dart b/lib/views/widgets/bottom_sheets/muliple_selection_bottom_sheet.dart new file mode 100644 index 00000000..a9abe36e --- /dev/null +++ b/lib/views/widgets/bottom_sheets/muliple_selection_bottom_sheet.dart @@ -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 extends StatefulWidget { + final List? items; + final List selectedItem; // Now nullable + final String title; + final SelectionBuilderString builderString; + final bool showCancel; + final Function(List) 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(); +} + +class _MultipleSelectionBottomSheetState extends State> { + late List _selectedValue; // Now nullable + + String query = ""; + + List? 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); + } +} diff --git a/lib/views/widgets/fullscreen_dialogs/multiple_selection_fullscreen_dialog.dart b/lib/views/widgets/fullscreen_dialogs/multiple_selection_fullscreen_dialog.dart new file mode 100644 index 00000000..806b9de3 --- /dev/null +++ b/lib/views/widgets/fullscreen_dialogs/multiple_selection_fullscreen_dialog.dart @@ -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 extends StatefulWidget { + final List? items; + final List selectedItem; // Now nullable + final T? disableItem; // Now nullable + final String title; + final bool showCancel; + final SelectionBuilderString builderString; + final Function(List) 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(); +} + +class _SelectionBottomSheetState extends State> { + late List _selectedValue; // Now nullable + + String query = ""; + + List? 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(0), + backgroundColor: WidgetStateProperty.all(context.isDark ? AppColor.neutral120 : null), + leading: Icon(Icons.search, color: AppColor.iconColor(context)), + textStyle: WidgetStateProperty.all( + TextStyle(color: AppColor.textColor(context), fontSize: 16.0), + ), + hintStyle: WidgetStateProperty.all( + 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); + } +} diff --git a/lib/views/widgets/fullscreen_dialogs/selection_fullscreen_dialog.dart b/lib/views/widgets/fullscreen_dialogs/selection_fullscreen_dialog.dart index ce08754f..b7227dda 100644 --- a/lib/views/widgets/fullscreen_dialogs/selection_fullscreen_dialog.dart +++ b/lib/views/widgets/fullscreen_dialogs/selection_fullscreen_dialog.dart @@ -87,9 +87,7 @@ class _SelectionBottomSheetState extends State(0), - backgroundColor: WidgetStateProperty.all( - AppColor.fieldBgColor(context), // Your custom background color - ), + backgroundColor: WidgetStateProperty.all(context.isDark ? AppColor.neutral120 : null), leading: Icon(Icons.search, color: AppColor.iconColor(context)), textStyle: WidgetStateProperty.all( TextStyle(color: AppColor.textColor(context), fontSize: 16.0),