diff --git a/assets/images/help-circle.svg b/assets/images/help-circle.svg new file mode 100644 index 00000000..3fa1ecbc --- /dev/null +++ b/assets/images/help-circle.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index 2e7e5f56..b9d53650 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 = "28"; // 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 const host1 = "http://10.201.111.125:9495"; // temporary Server UAT url // static String _baseUrl = "$_host/mobile"; @@ -369,4 +369,10 @@ class URLs { //survey static get getQuestionnaire => '$_baseUrl/SurveyQuestionnaire/GetQuestionnaire'; static get submitSurvey => '$_baseUrl/SurveyQuestionnaire/SubmitSurvey'; + + // load module + static get getLoanPeriod => "$_baseUrl/Lookups/GetLookup?lookupEnum=3051"; + static get getMedDepartmentBasedOnSite => "$_baseUrl/TRAFDataSource/GetDepartmentBasedOnSite"; + static get getLoanById => '$_baseUrl/Loan/GetLoanById'; + static get addLoan => '$_baseUrl/Loan/AddLoan'; } diff --git a/lib/controllers/providers/api/all_requests_provider.dart b/lib/controllers/providers/api/all_requests_provider.dart index 99f77a02..6b714ec6 100644 --- a/lib/controllers/providers/api/all_requests_provider.dart +++ b/lib/controllers/providers/api/all_requests_provider.dart @@ -140,8 +140,7 @@ class AllRequestsProvider extends ChangeNotifier { return list; } if (context.userProvider.isQualityUser) { - - list = [10,11]; + list = [10, 11]; return list; } @@ -163,6 +162,7 @@ class AllRequestsProvider extends ChangeNotifier { if (context.userProvider.isNurse) { list.add(9); // } + list.add(12); // loan module return list; } diff --git a/lib/extensions/text_extensions.dart b/lib/extensions/text_extensions.dart index ea5496e3..e608131f 100644 --- a/lib/extensions/text_extensions.dart +++ b/lib/extensions/text_extensions.dart @@ -134,7 +134,7 @@ abstract class AppTextStyles { fontStyle: FontStyle.normal, decoration: TextDecoration.none, ); - static final TextStyle textFieldLabelStyle= TextStyle( + static final TextStyle textFieldLabelStyle = TextStyle( fontSize: 11.toScreenWidth, fontWeight: FontWeight.w500, color: AppColor.neutral20, @@ -164,14 +164,14 @@ extension CapExtension on String { String get allInCaps => toUpperCase(); - String get cleanupWhitespace => this;//replaceAll(RegExp(r"\s+"), " "); + String get cleanupWhitespace => this; //replaceAll(RegExp(r"\s+"), " "); String get capitalizeFirstOfEach => this; - //trim().isNotEmpty - // ? trim().toLowerCase().split(" ").map((str) { - // return str.trim()?.inCaps ?? ""; - // }).join(" ") - // : ""; +//trim().isNotEmpty +// ? trim().toLowerCase().split(" ").map((str) { +// return str.trim()?.inCaps ?? ""; +// }).join(" ") +// : ""; } extension FilesExtension on String { diff --git a/lib/main.dart b/lib/main.dart index 29c6f8af..0148b9f3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -382,7 +382,7 @@ class MyApp extends StatelessWidget { CreateSystemInternalAuditForm.id: (_) => const CreateSystemInternalAuditForm(), UpdateEquipmentInternalAuditPage.id: (_) => UpdateEquipmentInternalAuditPage(), UpdateSystemInternalAuditPage.id: (_) => UpdateSystemInternalAuditPage(), - CreateLoanRequestView.id: (_) => const CreateLoanRequestView(), + CreateLoanRequestView.id: (_) => CreateLoanRequestView(), // SwipeSuccessView.routeName: (_) => const SwipeSuccessView(), // SwipeHistoryView.routeName: (_) => const SwipeHistoryView(), }, diff --git a/lib/modules/loan_module/models/loan_form_model.dart b/lib/modules/loan_module/models/loan_form_model.dart index 40e55232..bdfdf3af 100644 --- a/lib/modules/loan_module/models/loan_form_model.dart +++ b/lib/modules/loan_module/models/loan_form_model.dart @@ -75,7 +75,6 @@ class LoanFormModel { // "cMWOItemId": null // } - Map toJson() { return { "doctorName": docName, @@ -90,12 +89,13 @@ class LoanFormModel { "vendorRepresentativeName": vendorRepresentativeName, "vendorNumber": vendorNumber, "vendorEmail": vendorEmail, - 'siteId' : site?.id, - 'departmentId' : department?.id, + 'siteId': site?.id, + 'departmentId': department?.id, "loanAttachments": loanAttachment != null ? loanAttachment!.map((v) => v.toJson()).toList() : [], }; } } + class LoanAttachments { LoanAttachments({ this.id, diff --git a/lib/modules/loan_module/pages/create_loan_request_view.dart b/lib/modules/loan_module/pages/create_loan_request_view.dart index 5f836178..c9c7848d 100644 --- a/lib/modules/loan_module/pages/create_loan_request_view.dart +++ b/lib/modules/loan_module/pages/create_loan_request_view.dart @@ -4,12 +4,14 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/controllers/providers/api/device_transfer_provider.dart'; +import 'package:test_sa/controllers/validator/validator.dart'; import 'package:test_sa/dashboard_latest/dashboard_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/helper/utils.dart'; import 'package:test_sa/models/device/asset.dart'; import 'package:test_sa/models/enums/user_types.dart'; import 'package:test_sa/models/generic_attachment_model.dart'; @@ -28,6 +30,7 @@ import 'package:test_sa/modules/loan_module/models/loan_form_model.dart'; import 'package:test_sa/modules/loan_module/models/medical_department_model.dart'; import 'package:test_sa/modules/loan_module/provider/loan_period_provider.dart'; import 'package:test_sa/modules/loan_module/provider/loan_provider.dart'; +import 'package:test_sa/modules/loan_module/provider/loan_provider.dart'; import 'package:test_sa/modules/loan_module/provider/medical_department_provider.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; @@ -47,8 +50,9 @@ import '../../../models/new_models/department.dart'; class CreateLoanRequestView extends StatefulWidget { static const String id = "/create-loan"; + int? workOrderId; - const CreateLoanRequestView({Key? key}) : super(key: key); + CreateLoanRequestView({Key? key, this.workOrderId}) : super(key: key); @override _CreateLoanRequestViewState createState() => _CreateLoanRequestViewState(); @@ -61,14 +65,16 @@ class _CreateLoanRequestViewState extends State with Tick Asset? device; TaskType? selectedType; final LoanFormModel _loanFormModel = LoanFormModel(); - bool isEng = true; + TextEditingController commentController = TextEditingController(); + bool get isBackUpRequest => widget.workOrderId != null; + @override void initState() { - // TODO: implement initState super.initState(); Provider.of(context, listen: false).reset(); + Provider.of(context, listen: false).reset(); } @override @@ -81,33 +87,36 @@ class _CreateLoanRequestViewState extends State with Tick child: Column( children: [ SingleChildScrollView( + padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, children: [ - // if(isEng)...[ - // scanAssetButton(), - // 16.height, - // ], - contactInfoSection(), + if (isBackUpRequest) ...[ + scanAssetButton(), + 16.height, + ], + // ...contactInfoSection(), + // 16.height, + ...itemDetailsSection(), 16.height, - itemDetailsSection(), + ...doctorInfoSection(), + 16.height, + ...vendorDetailsSection(), 16.height, - vendorDetailsSection(), - 23.height, AttachmentPicker( label: context.translation.attachments, 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).paddingAll(16), + ).toShadowContainer(context, borderRadius: 20), ).expanded, FooterActionButton.footerContainer( context: context, @@ -124,236 +133,333 @@ class _CreateLoanRequestViewState extends State with Tick ); } - Widget contactInfoSection() { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - context.translation.contactInfo.heading6(context).custom(color: AppColor.black10), - 8.height, - AppTextFormField( - labelText: "Name", - backgroundColor: AppColor.fieldBgColor(context), - textAlign: TextAlign.center, - hintText: "Doc Name", - labelStyle: AppTextStyles.textFieldLabelStyle, - hintStyle: AppTextStyles.textFieldLabelStyle, - textInputType: TextInputType.text, - showShadow: false, - onSaved: (value) { - _loanFormModel.docName = value; - }, - style: Theme.of(context).textTheme.titleMedium, - ), - 8.height, - AppTextFormField( - labelText: "Number", - backgroundColor: AppColor.fieldBgColor(context), - textAlign: TextAlign.center, - hintText: "Doc Number", - labelStyle: AppTextStyles.textFieldLabelStyle, - hintStyle: AppTextStyles.textFieldLabelStyle, - textInputType: TextInputType.phone, - showShadow: false, - onSaved: (value) { - _loanFormModel.docNumber = value; - }, - style: Theme.of(context).textTheme.titleMedium, - ), - 8.height, - AppTextFormField( - labelText: "Email", - backgroundColor: AppColor.fieldBgColor(context), - textAlign: TextAlign.center, - hintText: "Doc Email", - labelStyle: AppTextStyles.textFieldLabelStyle, - hintStyle: AppTextStyles.textFieldLabelStyle, - textInputType: TextInputType.emailAddress, - showShadow: false, - onSaved: (value) { - _loanFormModel.docEmail = value; - }, - style: Theme.of(context).textTheme.titleMedium, - ), - ], - ); + List contactInfoSection() { + return [ + context.translation.contactInfo.heading6(context).custom(color: AppColor.black10), + 8.height, + AppTextFormField( + labelText: "Name", + backgroundColor: AppColor.fieldBgColor(context), + textAlign: TextAlign.center, + hintText: "Doc Name", + labelStyle: AppTextStyles.textFieldLabelStyle, + hintStyle: AppTextStyles.textFieldLabelStyle, + textInputType: TextInputType.text, + showShadow: false, + onSaved: (value) { + _loanFormModel.docName = value; + }, + style: Theme.of(context).textTheme.titleMedium, + ), + 8.height, + AppTextFormField( + labelText: "Number", + backgroundColor: AppColor.fieldBgColor(context), + textAlign: TextAlign.center, + hintText: "Doc Number", + labelStyle: AppTextStyles.textFieldLabelStyle, + hintStyle: AppTextStyles.textFieldLabelStyle, + textInputType: TextInputType.phone, + showShadow: false, + onSaved: (value) { + _loanFormModel.docNumber = value; + }, + style: Theme.of(context).textTheme.titleMedium, + ), + 8.height, + AppTextFormField( + labelText: "Email", + backgroundColor: AppColor.fieldBgColor(context), + textAlign: TextAlign.center, + hintText: "Doc Email", + labelStyle: AppTextStyles.textFieldLabelStyle, + hintStyle: AppTextStyles.textFieldLabelStyle, + textInputType: TextInputType.emailAddress, + showShadow: false, + onSaved: (value) { + _loanFormModel.docEmail = value; + }, + style: Theme.of(context).textTheme.titleMedium, + ), + ]; } - Widget itemDetailsSection() { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - 'Item Details'.addTranslation.heading6(context).custom(color: AppColor.black10), - 8.height, - SingleItemDropDownMenu( - context: context, - title: context.translation.site, - initialValue: _loanFormModel.site, - showShadow: false, - backgroundColor: AppColor.fieldBgColor(context), - showAsBottomSheet: true, - onSelect: (value) { - _loanFormModel.site = value; - setState(() {}); - }, - ), - 8.height, - //TODO need to check where to get all departments.. - // MultipleItemDropDownMenu( - // context: context, - // showAsBottomSheet: true, - // backgroundColor: AppColor.neutral100, - // showShadow: false, - // showCancel: true, - // requestById: context.userProvider.user?.clientId, - // title: "Please specify departments and relations", - // initialValue: departments, - // onSelect: (value) { - // departments = value ?? []; - // trafRequest?.trafDepartments = departments.map((element) => Departments(id: 0, trafId: 0, departmentId: element.id!.toInt())).toList(); - // }, - // ), - SingleItemDropDownMenu( - context: context, - title: context.translation.department, - showShadow: false, - showAsBottomSheet: true, - initialValue: _loanFormModel.department, - requestById: context.userProvider.user?.clientId, - backgroundColor: AppColor.fieldBgColor(context), - onSelect: (value) { - _loanFormModel.department = value; + List doctorInfoSection() { + return [ + context.translation.contactInfo.bodyText(context).custom(color: AppColor.black10), + 8.height, + AppTextFormField( + initialValue: "", + labelText: "Doctor Name", + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, + style: Theme.of(context).textTheme.titleMedium, + backgroundColor: AppColor.fieldBgColor(context), + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + showShadow: false, + textInputType: TextInputType.text, + onChange: (value) { + _loanFormModel.docName = value; + }, + ), + 8.height, + AppTextFormField( + initialValue: "", + labelText: "Doctor Number", + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, + style: Theme.of(context).textTheme.titleMedium, + backgroundColor: AppColor.fieldBgColor(context), + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + showShadow: false, + textInputType: TextInputType.phone, + onChange: (value) { + _loanFormModel.docNumber = value; + }, + ), + 8.height, + AppTextFormField( + initialValue: "", + labelText: "Doctor Email", + validator: (value) { + if ((value ?? "").isEmpty) { + return "Mandatory"; + } else { + return Validator.isEmail(value!) ? null : context.translation.emailValidateMessage; + } + }, + style: Theme.of(context).textTheme.titleMedium, + backgroundColor: AppColor.fieldBgColor(context), + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + showShadow: false, + textInputType: TextInputType.emailAddress, + onChange: (value) { + _loanFormModel.docEmail = value; + }, + ), + ]; + } + + List itemDetailsSection() { + return [ + 'Item Details'.addTranslation.bodyText(context).custom(color: AppColor.black10), + 8.height, + SingleItemDropDownMenu( + context: context, + title: context.translation.site, + initialValue: _loanFormModel.site, + showShadow: false, + validator: (value) { + if (value == null) return "Please select a site"; + return null; + }, + backgroundColor: AppColor.fieldBgColor(context), + showAsBottomSheet: true, + onSelect: (value) { + _loanFormModel.site = value; + setState(() {}); + }, + ), + 8.height, + SingleItemDropDownMenu( + context: context, + title: context.translation.department, + showShadow: false, + validator: (value) { + if (value == null) return "Please select a department"; + return null; + }, + showAsBottomSheet: true, + initialValue: _loanFormModel.department, + requestById: context.userProvider.user?.clientId, + backgroundColor: AppColor.fieldBgColor(context), + onSelect: (value) { + _loanFormModel.department = value; + setState(() {}); + }, + ), + 8.height, + AppTextFormField( + labelText: "Item Description", + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, + style: Theme.of(context).textTheme.titleMedium, + backgroundColor: AppColor.fieldBgColor(context), + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + floatingLabelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + showShadow: false, + textInputType: TextInputType.text, + onChange: (value) { + _loanFormModel.itemDescription = value; + }, + ), + 8.height, + Row( + children: [ + "help-circle".toSvgAsset(height: 16, width: 16), + 8.width, + Text( + "Specify the purpose of the loan (e.g., equipment needed for a scheduled procedure or patient case)", + style: AppTextStyles.tinyFont2.copyWith(fontWeight: FontWeight.w400, color: context.isDark ? Colors.white : const Color(0xff3B3D4A)), + ).expanded, + ], + ), + 8.height, + AppTextFormField( + labelText: "Request Description", + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, + style: Theme.of(context).textTheme.titleMedium, + backgroundColor: AppColor.fieldBgColor(context), + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + floatingLabelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + showShadow: false, + textInputType: TextInputType.text, + onChange: (value) { + _loanFormModel.requestDescription = value; + }, + ), + 8.height, + AppTextFormField( + labelText: "Model", + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, + style: Theme.of(context).textTheme.titleMedium, + backgroundColor: AppColor.fieldBgColor(context), + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + floatingLabelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + showShadow: false, + textInputType: TextInputType.text, + onChange: (value) { + _loanFormModel.model = value; + }, + ), + 8.height, + AppTextFormField( + labelText: "Manufacturer", + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, + style: Theme.of(context).textTheme.titleMedium, + backgroundColor: AppColor.fieldBgColor(context), + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + floatingLabelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + showShadow: false, + textInputType: TextInputType.text, + onChange: (value) { + _loanFormModel.manufacturer = value; + }, + ), + 8.height, + SingleItemDropDownMenu( + context: context, + height: 56.toScreenHeight, + title: 'Loan Period'.addTranslation, + showShadow: false, + validator: (value) { + if (value == null) return "Please select loan period"; + return null; + }, + backgroundColor: AppColor.fieldBgColor(context), + showAsBottomSheet: true, + initialValue: _loanFormModel.loanProvided, + onSelect: (status) { + if (status != null) { + _loanFormModel.loanProvided = status; setState(() {}); - }, - ), - 8.height, - AppTextFormField( - labelText: "Item Description", - backgroundColor: AppColor.fieldBgColor(context), - textAlign: TextAlign.center, - labelStyle: AppTextStyles.textFieldLabelStyle, - hintStyle: AppTextStyles.textFieldLabelStyle, - textInputType: TextInputType.text, - showShadow: false, - onSaved: (value) { - _loanFormModel.itemDescription = value; - }, - style: Theme.of(context).textTheme.titleMedium, - ), - 8.height, - AppTextFormField( - labelText: "Request Description", - backgroundColor: AppColor.fieldBgColor(context), - textAlign: TextAlign.center, - labelStyle: AppTextStyles.textFieldLabelStyle, - hintStyle: AppTextStyles.textFieldLabelStyle, - textInputType: TextInputType.text, - showShadow: false, - onSaved: (value) { - _loanFormModel.requestDescription = value; - }, - style: Theme.of(context).textTheme.titleMedium, - ), - 8.height, - AppTextFormField( - labelText: "Model", - backgroundColor: AppColor.fieldBgColor(context), - textAlign: TextAlign.center, - labelStyle: AppTextStyles.textFieldLabelStyle, - hintStyle: AppTextStyles.textFieldLabelStyle, - textInputType: TextInputType.text, - showShadow: false, - onSaved: (value) { - _loanFormModel.model = value; - }, - style: Theme.of(context).textTheme.titleMedium, - ), - 8.height, - AppTextFormField( - labelText: "Manufacturer", - backgroundColor: AppColor.fieldBgColor(context), - textAlign: TextAlign.center, - labelStyle: AppTextStyles.textFieldLabelStyle, - hintStyle: AppTextStyles.textFieldLabelStyle, - textInputType: TextInputType.text, - showShadow: false, - onSaved: (value) { - _loanFormModel.manufacturer = value; - }, - style: Theme.of(context).textTheme.titleMedium, - ), - 8.height, - SingleItemDropDownMenu( - context: context, - height: 56.toScreenHeight, - title: 'Loan Period'.addTranslation, - showShadow: false, - backgroundColor: AppColor.fieldBgColor(context), - showAsBottomSheet: true, - initialValue: _loanFormModel.loanProvided, - onSelect: (status) { - if (status != null) { - _loanFormModel.loanProvided = status; - setState(() {}); - } - }, - ), - 8.height, - ], - ); + } + }, + ), + 8.height, + ]; } - Widget vendorDetailsSection() { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - 'Vendor Details'.addTranslation.heading6(context).custom(color: AppColor.black10), - 8.height, - AppTextFormField( - labelText: "Name", - backgroundColor: AppColor.fieldBgColor(context), - textAlign: TextAlign.center, - hintText: "Vendor Name", - labelStyle: AppTextStyles.textFieldLabelStyle, - hintStyle: AppTextStyles.textFieldLabelStyle, - textInputType: TextInputType.text, - showShadow: false, - onSaved: (value) { - _loanFormModel.vendorName = value; - }, - style: Theme.of(context).textTheme.titleMedium, - ), - 8.height, - AppTextFormField( - labelText: "Number", - backgroundColor: AppColor.fieldBgColor(context), - textAlign: TextAlign.center, - hintText: "Vendor Number", - labelStyle: AppTextStyles.textFieldLabelStyle, - hintStyle: AppTextStyles.textFieldLabelStyle, - textInputType: TextInputType.phone, - showShadow: false, - onSaved: (value) { - _loanFormModel.vendorNumber = value; - }, - style: Theme.of(context).textTheme.titleMedium, - ), - 8.height, - AppTextFormField( - labelText: "Email", - backgroundColor: AppColor.fieldBgColor(context), - textAlign: TextAlign.center, - hintText: "Vendor Email", - labelStyle: AppTextStyles.textFieldLabelStyle, - hintStyle: AppTextStyles.textFieldLabelStyle, - textInputType: TextInputType.emailAddress, - showShadow: false, - onSaved: (value) { - _loanFormModel.vendorEmail = value; - }, - style: Theme.of(context).textTheme.titleMedium, - ), - ], - ); + List vendorDetailsSection() { + return [ + 'Vendor Details'.addTranslation.bodyText(context).custom(color: AppColor.black10), + 8.height, + AppTextFormField( + initialValue: "", + labelText: "Vendor Name", + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, + style: Theme.of(context).textTheme.titleMedium, + backgroundColor: AppColor.fieldBgColor(context), + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + showShadow: false, + textInputType: TextInputType.name, + onChange: (value) { + _loanFormModel.vendorName = value; + }, + ), + 8.height, + AppTextFormField( + initialValue: "", + labelText: "Vendor Representative Name", + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, + style: Theme.of(context).textTheme.titleMedium, + backgroundColor: AppColor.fieldBgColor(context), + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + showShadow: false, + textInputType: TextInputType.name, + onChange: (value) { + _loanFormModel.vendorRepresentativeName = value; + }, + ), + 8.height, + AppTextFormField( + initialValue: "", + labelText: "Vendor Number", + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, + style: Theme.of(context).textTheme.titleMedium, + backgroundColor: AppColor.fieldBgColor(context), + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + showShadow: false, + textInputType: TextInputType.phone, + onChange: (value) { + _loanFormModel.vendorNumber = value; + }, + ), + 8.height, + AppTextFormField( + initialValue: "", + labelText: "Vendor Email", + validator: (value) { + if ((value ?? "").isEmpty) { + return "Mandatory"; + } else { + return Validator.isEmail(value!) ? null : context.translation.emailValidateMessage; + } + }, + style: Theme.of(context).textTheme.titleMedium, + backgroundColor: AppColor.fieldBgColor(context), + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + showShadow: false, + textInputType: TextInputType.emailAddress, + onChange: (value) { + _loanFormModel.vendorEmail = value; + }, + ), + ]; } Widget scanAssetButton() { @@ -369,16 +475,33 @@ class _CreateLoanRequestViewState extends State with Tick } Future _submit() async { + if (isBackUpRequest && device == null) { + "Please scan or pick asset".showToast; + return; + } if (_formKey.currentState!.validate()) { _formKey.currentState!.save(); - _loanFormModel?.loanAttachment = []; + _loanFormModel.loanAttachment = []; for (var item in attachments) { String fileName = ServiceRequestUtils.isLocalUrl(item.name ?? '') ? ("${item.name ?? ''.split("/").last}|${base64Encode(File(item.name ?? '').readAsBytesSync())}") : item.name ?? ''; - _loanFormModel?.loanAttachment?.add(LoanAttachments(id: item.id, name: fileName)); + _loanFormModel.loanAttachment?.add(LoanAttachments(id: item.id, name: fileName)); } + Utils.showLoading(context); LoanProvider loanProvider = Provider.of(context, listen: false); - // await loanProvider.addLoanRequest(context: context, task: _loanFormModel!); + Map body = _loanFormModel.toJson(); + body["id"] = 0; + body["requestorUserID"] = context.userProvider.user!.userID; + + if (isBackUpRequest) { + body["assetId"] = device!.id; + body["cMWOItemId"] = widget.workOrderId; + } + bool isSuccess = await loanProvider.addLoanRequest(body); + Utils.hideLoading(context); + if (isSuccess) { + Navigator.pop(context); + } } } } diff --git a/lib/modules/loan_module/provider/loan_period_provider.dart b/lib/modules/loan_module/provider/loan_period_provider.dart index 6d863a20..4f2c9bbf 100644 --- a/lib/modules/loan_module/provider/loan_period_provider.dart +++ b/lib/modules/loan_module/provider/loan_period_provider.dart @@ -8,7 +8,7 @@ import 'package:test_sa/providers/loading_list_notifier.dart'; class LoanPeriodProvider extends LoadingListNotifier { @override - Future getData({int?id}) async { + Future getData({int? id}) async { if (loading == true) return -2; loading = true; notifyListeners(); @@ -29,4 +29,4 @@ class LoanPeriodProvider extends LoadingListNotifier { return -1; } } -} \ No newline at end of file +} diff --git a/lib/modules/loan_module/provider/loan_provider.dart b/lib/modules/loan_module/provider/loan_provider.dart index 5397aa3b..44658f6c 100644 --- a/lib/modules/loan_module/provider/loan_provider.dart +++ b/lib/modules/loan_module/provider/loan_provider.dart @@ -1,8 +1,32 @@ import 'package:flutter/cupertino.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'; class LoanProvider extends ChangeNotifier { + Future addLoanRequest(Map body) async { + try { + Response response = await ApiManager.instance.post(URLs.addLoan, body: body); + if (response.statusCode >= 200 && response.statusCode < 300) { + // trafRequestDataModel = TrafRequestDataModel.fromJson(json.decode(response.body)["data"]); + return true; + } + return false; + } catch (error) { + return false; + } + } - - - -} \ No newline at end of file + Future getLoanById(int id) async { + try { + Response response = await ApiManager.instance.get(URLs.getLoanById + "?loanId=$id"); + if (response.statusCode >= 200 && response.statusCode < 300) { + // trafRequestDataModel = TrafRequestDataModel.fromJson(json.decode(response.body)["data"]); + return true; + } + return false; + } catch (error) { + return false; + } + } +} 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 02cb825b..7600bc72 100644 --- a/lib/new_views/common_widgets/app_text_form_field.dart +++ b/lib/new_views/common_widgets/app_text_form_field.dart @@ -213,7 +213,7 @@ class _AppTextFormFieldState extends State { : !widget.enable ? (widget.enableColor ?? AppColor.neutral40) : AppColor.background(context)), - errorStyle: AppTextStyle.tiny.copyWith(color: context.isDark ? AppColor.red50 : AppColor.red60), + errorStyle: AppTextStyle.tiny.copyWith(color: context.isDark ? AppColor.red50 : AppColor.red30), 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 ?? "", 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 5c77205a..d78e5f69 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 @@ -4,6 +4,7 @@ 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/app_style/app_text_style.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'; @@ -28,6 +29,8 @@ class SingleItemDropDownMenu exte final Color? backgroundColor; // Now nullable final bool? loading; // Now nullable final int? requestById; + final String? Function(T?)? validator; + final AutovalidateMode? autovalidateMode; /// To use a static data (without calling API) /// just send [NullableLoadingProvider] as generic data type and fill the [staticData] @@ -48,6 +51,8 @@ class SingleItemDropDownMenu exte this.backgroundColor, this.loading, this.requestById, + this.validator, + this.autovalidateMode, }) : super(key: key); @override @@ -100,127 +105,148 @@ class _SingleItemDropDownMenuState( - value: _selectedItem, - iconSize: 24, - isDense: true, - icon: const SizedBox.shrink(), - dropdownColor: AppColor.neutral100, - elevation: 0, - isExpanded: true, - hint: Text( - isEmpty || widget.enabled == false ? context.translation.noDataFound : context.translation.select, - style: Theme.of(context).textTheme.bodyLarge, - ), - style: TextStyle(color: Theme.of(context).primaryColor, fontSize: 12), - underline: const SizedBox.shrink(), - onChanged: (widget.enabled == false || widget.showAsBottomSheet || widget.showAsFullScreenDialog) - ? null - : (T? newValue) { - // Now accepts nullable values - final isNull = newValue?.identifier == "-1"; - setState(() { - _selectedItem = isNull ? null : newValue; - }); - widget.onSelect!(isNull ? null : newValue); // Non-null assertion after null check - }, - items: ((X == NullableLoadingProvider) ? widget.staticData : provider?.items as List)?.map>((value) { - return DropdownMenuItem( - value: value, - child: Text( - value.name ?? "", // Null-aware operator for value.name - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.white10 : AppColor.black10), - ), - ); - }).toList(), - ), - ], - ).onPress(widget.enabled && widget.showAsFullScreenDialog - ? () { - openDialog(); + return FormField( + validator: widget.validator, + autovalidateMode: widget.autovalidateMode ?? AutovalidateMode.disabled, + initialValue: widget.initialValue, + builder: (FormFieldState fieldState) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 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(id: widget.requestById); } - : 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), + }, + child: Container( + height: widget.height ?? 60.toScreenHeight, + padding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth), + 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: Stack( + alignment: Alignment.center, + children: [ + if (widget.enabled) const PositionedDirectional(end: 0, child: Icon(Icons.keyboard_arrow_down_rounded)), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + widget.title, + style: Theme.of(context).textTheme.bodySmall?.copyWith(color: context.isDark ? null : AppColor.neutral20, fontWeight: FontWeight.w500), + ), + DropdownButton( + value: _selectedItem, + iconSize: 24, + isDense: true, + icon: const SizedBox.shrink(), + dropdownColor: AppColor.neutral100, + elevation: 0, + isExpanded: true, + hint: Text( + isEmpty || widget.enabled == false ? context.translation.noDataFound : context.translation.select, + style: Theme.of(context).textTheme.bodyLarge, + ), + style: TextStyle(color: Theme.of(context).primaryColor, fontSize: 12), + underline: const SizedBox.shrink(), + onChanged: (widget.enabled == false || widget.showAsBottomSheet || widget.showAsFullScreenDialog) + ? null + : (T? newValue) { + // Now accepts nullable values + final isNull = newValue?.identifier == "-1"; + setState(() { + _selectedItem = isNull ? null : newValue; + }); + fieldState.didChange(isNull ? null : newValue); + widget.onSelect!(isNull ? null : newValue); + }, + items: ((X == NullableLoadingProvider) ? widget.staticData : provider?.items as List)?.map>((value) { + return DropdownMenuItem( + value: value, + child: Text( + value.name ?? "", // Null-aware operator for value.name + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.white10 : AppColor.black10), ), - ), - 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), - ], - ), - ), - ); + ); + }).toList(), + ), + ], + ).onPress(widget.enabled && widget.showAsFullScreenDialog + ? () { + openDialog(fieldState); + } + : 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; + }); + fieldState.didChange(selectedT); + widget.onSelect!(selectedT); + }, + builderString: (emp) => emp?.name ?? "", // Null-aware operator for emp.name + ), + ); + // if (selectedT != null) { + // setState(() { + // _selectedItem = selectedT; + // }); + // widget.onSelect!(selectedT); // Non-null assertion after null check + // } + } + : null) + : null), + ], + ), + ), + ), + if (fieldState.hasError) + Padding( + padding: const EdgeInsets.only(left: 12, top: 4), + child: Text( + fieldState.errorText!, + style: AppTextStyle.tiny.copyWith(color: context.isDark ? AppColor.red50 : AppColor.red30), + ), + ) + ], + ); + }); } - void openDialog() async { + void openDialog(FormFieldState fieldState) async { Widget child = SelectionFullScreenDialog( // Specify generic type items: ((X == NullableLoadingProvider) ? widget.staticData : provider?.items as List) ?? [], @@ -233,6 +259,7 @@ class _SingleItemDropDownMenuState emp?.name ?? "", // Null-aware operator for emp.name diff --git a/lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart b/lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart index 7305a81a..792ba3e8 100644 --- a/lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart +++ b/lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart @@ -67,6 +67,7 @@ class _AllRequestsFilterPageState extends State { context.translation.transferAsset: 3, context.translation.preventiveMaintenance: 4, context.translation.task: 6, + "Loan Equipment": 12, }; if (isEngineer) { diff --git a/lib/new_views/pages/land_page/my_request/my_requests_page.dart b/lib/new_views/pages/land_page/my_request/my_requests_page.dart index 641c0243..25ab7cee 100644 --- a/lib/new_views/pages/land_page/my_request/my_requests_page.dart +++ b/lib/new_views/pages/land_page/my_request/my_requests_page.dart @@ -44,11 +44,9 @@ class _MyRequestsPageState extends State { Request(2, context.translation.gasRefill), Request(3, context.translation.transferAsset), Request(4, context.translation.preventiveMaintenance), - ]; if (Provider.of(context, listen: false).user!.type != UsersTypes.normal_user) { requestsList.add(Request(5, context.translation.recurrentWo)); - } //TODO unCommit this to enable task requestsList.add(Request(6, context.translation.taskRequest)); @@ -68,6 +66,8 @@ class _MyRequestsPageState extends State { requestsList.add(Request(11, 'System Internal Audit')); } + requestsList.add(Request(12, "Loan Equipment")); + if (context.userProvider.isAssessor) { requestsList = [ Request(null, context.translation.allWorkOrder), diff --git a/lib/views/widgets/equipment/asset_picker.dart b/lib/views/widgets/equipment/asset_picker.dart index b5f029d5..a57a08f0 100644 --- a/lib/views/widgets/equipment/asset_picker.dart +++ b/lib/views/widgets/equipment/asset_picker.dart @@ -46,7 +46,8 @@ class AssetPicker extends StatelessWidget { this.showAssetInfo = true, this.showBorder = false, this.multiSelection = false, - this.forPPM = false, this.enablePickManually = true, + this.forPPM = false, + this.enablePickManually = true, this.showLoading = false}) : assert( multiSelection == false || onMultiAssetPick != null, @@ -66,16 +67,16 @@ class AssetPicker extends StatelessWidget { decoration: BoxDecoration( color: buttonColor ?? backgroundColor ?? AppColor.blueStatus(context), borderRadius: BorderRadius.circular(10), - border: showBorder?Border.all(color: borderColor ?? AppColor.blueStatus(context), width: 2):null, + border: showBorder ? Border.all(color: borderColor ?? AppColor.blueStatus(context), width: 2) : null, // boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.03), blurRadius: 14)], ), padding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth, vertical: 8.toScreenHeight), child: Row( mainAxisSize: MainAxisSize.min, children: [ - "scan_asset".toSvgAsset(height: 22, fit: BoxFit.fitHeight, color: context.isDark ? AppColor.neutral20 :iconColor?? Colors.white), + "scan_asset".toSvgAsset(height: 22, fit: BoxFit.fitHeight, color: context.isDark ? AppColor.neutral20 : iconColor ?? Colors.white), 8.width, - ' ${label??"Scan or Pick Asset"}'.bodyText(context).custom(color: context.isDark ? AppColor.neutral20 : labelColor?? Colors.white), + ' ${label ?? "Scan or Pick Asset"}'.bodyText(context).custom(color: context.isDark ? AppColor.neutral20 : labelColor ?? Colors.white), ], ), ).onPress(() async { @@ -84,7 +85,7 @@ class AssetPicker extends StatelessWidget { builder: (context) => AssetScanQr( title: context.translation.assetScan, multiSelection: multiSelection, - enablePickManually: enablePickManually, + enablePickManually: enablePickManually, ))) as List?; if (device?.isNotEmpty ?? false) { onMultiAssetPick!(device!); @@ -94,7 +95,7 @@ class AssetPicker extends StatelessWidget { builder: (context) => AssetScanQr( title: context.translation.assetScan, multiSelection: multiSelection, - enablePickManually: enablePickManually, + enablePickManually: enablePickManually, ))) as Asset?; if (device != null) { onPick!(device); @@ -124,7 +125,7 @@ class AssetPicker extends StatelessWidget { builder: (context) => AssetScanQr( title: context.translation.assetScan, multiSelection: multiSelection, - enablePickManually: enablePickManually, + enablePickManually: enablePickManually, ))) as List?; if (device?.isNotEmpty ?? false) { onMultiAssetPick!(device!); @@ -134,7 +135,7 @@ class AssetPicker extends StatelessWidget { builder: (context) => AssetScanQr( title: context.translation.assetScan, multiSelection: multiSelection, - enablePickManually: enablePickManually, + enablePickManually: enablePickManually, ))) as Asset?; if (device != null) { onPick!(device); @@ -178,7 +179,7 @@ class AssetPicker extends StatelessWidget { color: Colors.black87, decoration: TextDecoration.none, ), - ).toShimmer(isShow: showLoading,context: context).expanded, + ).toShimmer(isShow: showLoading, context: context).expanded, const Icon( Icons.info, color: Color(0xff7D859A), @@ -209,10 +210,10 @@ class AssetPicker extends StatelessWidget { ], ), 8.height, - "${context.translation.assetNo}: ${device.assetNumber}".bodyText2(context).toShimmer(isShow: showLoading,context: context), + "${context.translation.assetNo}: ${device.assetNumber}".bodyText2(context).toShimmer(isShow: showLoading, context: context), 2.height, // "${context.translation.manufacture}: ${device.modelDefinition?.manufacturerName}".bodyText(context), - "${context.translation.model}: ${device.modelDefinition?.modelName}".bodyText2(context).toShimmer(isShow: showLoading,context: context), + "${context.translation.model}: ${device.modelDefinition?.modelName}".bodyText2(context).toShimmer(isShow: showLoading, context: context), // "${context.translation.serialNumber}: ${device.assetNumber}".bodyText(context), // const Divider().defaultStyle(context), // "${context.translation.department}: ${device.department?.departmentName}".bodyText(context),