diff --git a/android/app/build.gradle b/android/app/build.gradle index b92d6c6d..b315f561 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -38,12 +38,12 @@ android { compileOptions { coreLibraryDesugaringEnabled true - sourceCompatibility JavaVersion.VERSION_21 - targetCompatibility JavaVersion.VERSION_21 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = "21" // Must match the compileOptions target + jvmTarget = "17" // Must match the compileOptions target } defaultConfig { diff --git a/lib/app_strings/app_asset.dart b/lib/app_strings/app_asset.dart index 242f0170..b5d64cea 100644 --- a/lib/app_strings/app_asset.dart +++ b/lib/app_strings/app_asset.dart @@ -10,7 +10,7 @@ class AppAsset { static String maintenanceIcon = 'maintenance_icon'; static String retiredAssetIcon = 'assets/images/retired_asset_icon.svg'; static String sparePartIcon = 'spare_part_icon'; - static String editIcon = 'assets/images/edit_icon.svg'; - static String deleteIcon = 'assets/images/delete_icon.svg'; + static String editIcon = 'assets/images/edit_icon'; + static String deleteIcon = 'assets/images/delete_icon'; static String overDueIcon = 'assets/images/overdue.svg'; } diff --git a/lib/common_widgets/lookup_autocomplete_field.dart b/lib/common_widgets/lookup_autocomplete_field.dart new file mode 100644 index 00000000..8b296a7d --- /dev/null +++ b/lib/common_widgets/lookup_autocomplete_field.dart @@ -0,0 +1,124 @@ +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/models/lookup.dart'; +import 'package:test_sa/modules/asset_inventory_module/provider/asset_inventory_provider.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/views/app_style/sizing.dart'; +import '../../../extensions/text_extensions.dart'; +import '../../../new_views/app_style/app_text_style.dart'; + +class LookUpAutoCompleteField extends StatefulWidget { + final String initialValue; + final String label; + final num? assetId; + final bool forAssetName; + final bool forSupplier; + final bool clearAfterPick, isManufacturer; + final Function(Lookup) onPick; + final Function(String) onChanged; + //need to pass directly url + const LookUpAutoCompleteField( + {Key? key, + this.isManufacturer = false, + required this.initialValue, + required this.label, + this.forAssetName = false, + this.forSupplier = false, + this.assetId, + required this.onPick, + this.clearAfterPick = true, + required this.onChanged}) + : super(key: key); + + @override + _AutoCompletePartsFieldState createState() => _AutoCompletePartsFieldState(); +} + +class _AutoCompletePartsFieldState extends State { + AssetInventoryProvider? assetInventoryProvider; + late TextEditingController _controller; + + @override + void initState() { + _controller = TextEditingController(text: widget.initialValue); + super.initState(); + } + + @override + void didUpdateWidget(covariant LookUpAutoCompleteField 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) { + assetInventoryProvider ??= Provider.of(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) { + return const Iterable.empty(); + } + return (await assetInventoryProvider!.getAutoCompleteDetails(query: textEditingValue.text, isManufacturer: widget.isManufacturer, type: widget.forAssetName?1:widget.forSupplier?2:0)); + }, + displayStringForOption: (Lookup option) => option.name ?? "", + 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(minWidth: 0), + 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: widget.label, + labelStyle: AppTextStyles.tinyFont.copyWith(color: AppColor.textColor(context)), + ), + textInputAction: TextInputAction.search, + onChanged: (text) { + widget.onChanged(text); + fieldTextEditingController.text = text; + }, + onSubmitted: (String value) { + onFieldSubmitted(); + }, + ); + }, + onSelected: (Lookup selection) { + if (widget.clearAfterPick) { + _controller.clear(); + } else { + _controller.text = selection.name ?? ''; + } + widget.onPick(selection); + }, + ), + ); + } +} diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index e7cf01b2..47183ee1 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -1,16 +1,19 @@ class URLs { URLs._(); - static const String appReleaseBuildNumber = "26"; + 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 = "http://10.201.111.125:9495"; // temporary Server UAT url // static String _baseUrl = "$_host/mobile"; static final String _baseUrl = "$_host/v2/mobile"; // new V2 apis + // static final String _baseUrl = "$_host/v4/mobile"; // for asset inventory on UAT // static final String _baseUrl = "$_host/mobile"; // host local UAT // static final String _baseUrl = "$_host/v3/mobile"; // v3 for production CM,PM,TM + // static final String _baseUrl = "$_host/v5/mobile"; // v5 for data segregation static String _host = host1; @@ -23,6 +26,8 @@ class URLs { // API Routes static get login => "$_baseUrl/MobileAuth/Login"; + static get userChangeAssetGroup => "$_baseUrl/MobileAuth/UserChangeAssetGroup"; + static get getUserInfo => "$_baseUrl/MobileAuth/GetUserInfo"; static get logout => "$_baseUrl/MobileAuth/Logout"; @@ -258,7 +263,9 @@ class URLs { //Traf static get addTRAF => "$_baseUrl/TRAF/AddTRAF"; // get static get getTRAFById => "$_baseUrl/TRAF/GetTRAFById"; // get + static get getAssetAnalytics => "$_baseUrl/Asset/GetAssetAnalytics"; // get static get getAssetNDAutoCompleteByDynamicCodes => "$_baseUrl/AssetNameDefinition/GetAssetNDAutoCompleteByDynamicCodes"; // get// get + static get getAssetNameAutoComplete => "$_baseUrl/AssetNameDefinition/GetAssetNameAutoComplete"; // get// get //gas refill static get getGasTypes => "$_baseUrl/Lookups/GetLookup?lookupEnum=606"; // get @@ -295,6 +302,7 @@ class URLs { static get getNurses => "$_baseUrl/Account/GetUserByRoleValue?value=R-7"; // get static get getNursesBySiteId => "$_baseUrl/Account/GetUserByRoleValueSiteAndAssetGroupBySiteId?value=R-7"; // get static get getUsersBasedOnSearch => "$_baseUrl/Account/GetUsersBasedOnSearch"; // get + static get getUserByRoleValueSiteMobileAutocomplete => "$_baseUrl/Account/GetUserByRoleValueSiteMobileAutocomplete"; // get // pentry static get getPentry => "$_baseUrl/return/pentry/details"; // get @@ -311,8 +319,32 @@ class URLs { static get getSiteContactInfo => "$_baseUrl/AssetGroupSiteContactInfo"; // add static get getDepartmentBasedOnSite => "$_baseUrl/TRAFDataSource/GetDepartmentBasedOnSite"; // add +// asset inventory Urls. + static get getAssetInventoryById => '$_baseUrl/AssetInventory/GetAssetInventoryById'; + + static get getAssetsInSession => '$_baseUrl/AssetInventory/GetAssetsInSeassion'; + + static get getInventoryDetailsByFilter => '$_baseUrl/AssetInventory/GetInventoryDetailsByFilter'; + + static get searchAsset => '$_baseUrl/AssetInventory/SearchAsset'; + + static get saveAssetInSession => '$_baseUrl/AssetInventory/SaveAssetInSession'; + + static get deleteAssetInSession => '$_baseUrl/AssetInventory/DeleteAssetInSession'; + + static get getManufacturerOrModelAutoComplete => '$_baseUrl/AssetInventory/GetManufacturerOrModelAutoComplete'; -//internal Audit + static get searchAssetName => '$_baseUrl/AssetInventory/SearchAssetName'; + + static get getSuppliersAutoCompleteInventory => '$_baseUrl/Supplier/GetSuppliersMobile'; + + static get getAssetsTemp => '$_baseUrl/AssetInventory/GetAssetsTemp'; + + static get convertDetailToComplete => '$_baseUrl/AssetInventory/ConvertDetailToComplete'; + + static get getClassification => '$_baseUrl/AssetInventory/Classification'; + + //internal Audit static get getInternalAuditEquipmentById => "$_baseUrl/InternalAuditEquipments/GetInternalAuditEquipmentById"; static get getInternalAuditSystemById => "$_baseUrl/InternalAuditSystems/GetInternalAuditSystemByIdV2"; static get getInternalAuditChecklist => "$_baseUrl/Lookups/GetLookup?lookupEnum=43"; @@ -324,5 +356,4 @@ class URLs { static get updateAuditEquipmentsEngineer => "$_baseUrl/InternalAuditEquipments/UpdateAuditEquipmentsEngineer"; static get updateAuditSystemEngineer => "$_baseUrl/InternalAuditSystems/UpdateAuditSystemEngineer"; static get loadAllWorkOrderDetailsByID => "$_baseUrl/InternalAuditSystems/LoadAllWorkOrderDetailsByID"; - } diff --git a/lib/controllers/providers/api/gas_refill_comments.dart b/lib/controllers/providers/api/gas_refill_comments.dart index 71e85f01..b40d44e4 100644 --- a/lib/controllers/providers/api/gas_refill_comments.dart +++ b/lib/controllers/providers/api/gas_refill_comments.dart @@ -48,7 +48,6 @@ class GasRefillCommentsProvider extends ChangeNotifier { late Response response; try { response = await ApiManager.instance.get(URLs.getGazRefillComments + "?gasRefillId=$callId"); - stateCode = response.statusCode; if (response.statusCode >= 200 && response.statusCode < 300) { List requestsListJson = json.decode(response.body)["data"]; diff --git a/lib/controllers/providers/api/oracle_code_provider.dart b/lib/controllers/providers/api/oracle_code_provider.dart index ea8b0978..71375675 100644 --- a/lib/controllers/providers/api/oracle_code_provider.dart +++ b/lib/controllers/providers/api/oracle_code_provider.dart @@ -88,7 +88,7 @@ class OracleCodeProvider extends ChangeNotifier { Future> getAssetByOracleCode(String oracleCode) async { late Response response; try { - response = await ApiManager.instance.post(URLs.getAssetNDAutoCompleteByDynamicCodes, body: {"codeValue": oracleCode}); + response = await ApiManager.instance.post(URLs.getAssetNameAutoComplete, body: {"pageSize": 1, "pageNumber": 20, "searchText": oracleCode}); List page = []; if (response.statusCode >= 200 && response.statusCode < 300) { // client's request was successfully received diff --git a/lib/controllers/providers/api/search_user_provider.dart b/lib/controllers/providers/api/search_user_provider.dart index 2804e8a6..f13a30ca 100644 --- a/lib/controllers/providers/api/search_user_provider.dart +++ b/lib/controllers/providers/api/search_user_provider.dart @@ -46,10 +46,10 @@ class UserSearchProvider extends ChangeNotifier { notifyListeners(); } - Future> getUsersBasedOnSearch(String query) async { + Future> getUsersBasedOnSearch(int siteId, String query) async { late Response response; try { - response = await ApiManager.instance.get(URLs.getUsersBasedOnSearch + "?searchText=$query"); + response = await ApiManager.instance.get(URLs.getUserByRoleValueSiteMobileAutocomplete + "?siteId=$siteId&roleValue=R-7&searchText=$query"); List page = []; if (response.statusCode >= 200 && response.statusCode < 300) { // client's request was successfully received diff --git a/lib/controllers/providers/api/user_provider.dart b/lib/controllers/providers/api/user_provider.dart index 5d1495d5..471be087 100644 --- a/lib/controllers/providers/api/user_provider.dart +++ b/lib/controllers/providers/api/user_provider.dart @@ -155,6 +155,23 @@ class UserProvider extends ChangeNotifier { } } + Future userChangeAssetGroup() async { + Response response; + try { + response = await ApiManager.instance.post(URLs.userChangeAssetGroup, body: {}); + if (response.statusCode >= 200 && response.statusCode < 300) { + User tempUser = await User.fromJson(jsonDecode(response.body)); + tempUser.profilePhotoName = URLs.getFileUrl(tempUser!.profilePhotoName); + ApiManager.instance.user = tempUser; + _user = user; + return true; + } + return false; + } catch (error) { + return false; + } + } + Future logout(BuildContext context) async { Response response; try { diff --git a/lib/controllers/providers/settings/setting_provider.dart b/lib/controllers/providers/settings/setting_provider.dart index 2482c5b9..63cd4a28 100644 --- a/lib/controllers/providers/settings/setting_provider.dart +++ b/lib/controllers/providers/settings/setting_provider.dart @@ -37,7 +37,9 @@ class SettingProvider extends ChangeNotifier { bool get isUserFlowMedical => _assetGroup?.id == 1; bool get isUserFMS => _assetGroup?.id == 2; + bool get engineerCanCreateCM => isUserFMS; + bool get showPriority => !(isUserFMS && user?.type == UsersTypes.normal_user); // Local authentication instance @@ -77,8 +79,10 @@ class SettingProvider extends ChangeNotifier { } void selectAssetGroup(User user) { - if(user.assetGroups ==null) return; - if (user.assetGroups!.length == 1) { + if (user.assetGroups == null) return; + if (user.selectedAssetGroup != null) { + _assetGroup = user.selectedAssetGroup; + } else if (user.assetGroups!.length == 1) { _assetGroup = user.assetGroups!.first; } else { int fmIndex = user.assetGroups!.indexWhere((element) => element.id == 1); @@ -187,14 +191,13 @@ class SettingProvider extends ChangeNotifier { } } - Future checkIsEnabledFaceId({required bool status}) async { try { final response = await ApiManager.instance.post( URLs.enableFaceId, showToast: false, body: { - 'isEnabled':status, + 'isEnabled': status, }, ); return response.statusCode >= 200 && response.statusCode < 300; @@ -203,7 +206,6 @@ class SettingProvider extends ChangeNotifier { } } - Future checkAppUpdate(String buildNumber, String osType) async { Response response; bool isValid = false; diff --git a/lib/dashboard_latest/widgets/app_bar_widget.dart b/lib/dashboard_latest/widgets/app_bar_widget.dart index f65ed034..a716fd58 100644 --- a/lib/dashboard_latest/widgets/app_bar_widget.dart +++ b/lib/dashboard_latest/widgets/app_bar_widget.dart @@ -1,13 +1,16 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:test_sa/controllers/api_routes/api_manager.dart'; import 'package:test_sa/controllers/providers/settings/setting_provider.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/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/helper/utils.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/views/pages/user/notifications/notifications_page.dart'; +import 'package:test_sa/views/widgets/dialogs/confirm_dialog.dart'; import '../../controllers/providers/api/user_provider.dart'; import '../../models/user.dart'; @@ -48,11 +51,11 @@ class AppBarWidget extends StatelessWidget { children: [ Text( snapshot.user?.username ?? "", // Simplified null check - style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50, fontWeight: FontWeight.w600), + style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50, fontWeight: FontWeight.w600, fontSize: context.isTablet() ? 14 : null), ), Text( snapshot.user?.type?.name.toCamelCase ?? "", // Simplified null check - style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral20), + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral20, fontSize: context.isTablet() ? 12 : null), ), ], ), @@ -83,16 +86,29 @@ class AppBarWidget extends StatelessWidget { borderRadius: BorderRadius.circular(8), style: TextStyle(color: Theme.of(context).primaryColor), underline: const SizedBox.shrink(), - onChanged: (newValue) { + onChanged: (newValue) async { if (settingProvider.assetGroup != newValue) { - settingProvider.setAssetGroup(newValue); - WidgetsBinding.instance.addPostFrameCallback((_) { - DashBoardProvider dashBoardProvider = Provider.of(context, listen: false); - dashBoardProvider.setTabs(userType: settingProvider.user!.type!, context: context); - dashBoardProvider.getDashBoardCount(usersType: settingProvider.user!.type!); - dashBoardProvider.resetRequestDataList(); - dashBoardProvider.getRequestDetail(usersType: settingProvider.user!.type!, status: dashBoardProvider.tabs[dashBoardProvider.currentListIndex].tag); - }); + AssetGroup? tempGroup = ApiManager.instance.assetGroup; + ApiManager.instance.assetGroup = newValue; + + Utils.showLoading(context); + bool isSuccess = await context.userProvider.userChangeAssetGroup(); + Utils.hideLoading(context); + if (isSuccess) { + settingProvider.setAssetGroup(newValue); + WidgetsBinding.instance.addPostFrameCallback((_) { + context.settingProvider.setUser(ApiManager.instance.user!); + context.userProvider.setUser(ApiManager.instance.user!); + DashBoardProvider dashBoardProvider = Provider.of(context, listen: false); + dashBoardProvider.setTabs(userType: settingProvider.user!.type!, context: context); + dashBoardProvider.getDashBoardCount(usersType: settingProvider.user!.type!); + dashBoardProvider.resetRequestDataList(); + dashBoardProvider.getRequestDetail(usersType: settingProvider.user!.type!, status: dashBoardProvider.tabs[dashBoardProvider.currentListIndex].tag); + }); + } else { + ApiManager.instance.assetGroup = tempGroup; + context.showConfirmDialog("Failed, Please Try again"); + } } }, items: Provider.of(context, listen: false).user?.assetGroups?.map>((value) { diff --git a/lib/dashboard_latest/widgets/request_category_fragment.dart b/lib/dashboard_latest/widgets/request_category_fragment.dart index 95af0330..0736fe80 100644 --- a/lib/dashboard_latest/widgets/request_category_fragment.dart +++ b/lib/dashboard_latest/widgets/request_category_fragment.dart @@ -73,7 +73,7 @@ class RequestCategoryFragment extends StatelessWidget { Widget getTabs({required BuildContext context, required DashBoardProvider requestsProvider, required UsersTypes? userType}) { List tabs = CategoryTabs.getTabs(userType: userType ?? UsersTypes.normal_user, context: context); return SizedBox( - height: 44 + 16, + height: 44 + (context.isTablet() ? 36 : 16), child: ListView.separated( scrollDirection: Axis.horizontal, separatorBuilder: (cxt, index) => 12.width, diff --git a/lib/dashboard_latest/widgets/request_category_list.dart b/lib/dashboard_latest/widgets/request_category_list.dart index 639b3e6a..9ca9daff 100644 --- a/lib/dashboard_latest/widgets/request_category_list.dart +++ b/lib/dashboard_latest/widgets/request_category_list.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/models/new_models/dashboard_detail.dart'; +import 'package:test_sa/modules/asset_inventory_module/pages/inventory_session_item_view.dart'; import 'package:test_sa/modules/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_item_view.dart'; import 'package:test_sa/modules/tm_module/tasks_wo/task_request_item_view.dart'; import 'package:test_sa/modules/traf_module/traf_request_item_view.dart'; @@ -50,6 +51,10 @@ class RequestCategoryList extends StatelessWidget { return RecurrentWoItemView(requestData: request); case 6: return TaskRequestItemView(requestData: request); + case 7: + return TaskRequestItemView(requestData: request); + case 8: + return InventorySessionItemView(requestData: request); case 9: return TrafRequestItemView(requestData: request); //TODO need to verify this ... diff --git a/lib/extensions/context_extension.dart b/lib/extensions/context_extension.dart index 1f36d4ba..85299f73 100644 --- a/lib/extensions/context_extension.dart +++ b/lib/extensions/context_extension.dart @@ -19,7 +19,14 @@ extension BuildContextExtension on BuildContext { SettingProvider get settingProvider => Provider.of(this, listen: false); UserProvider get userProvider => Provider.of(this, listen: false); - + bool isTablet() { + final mediaQueryData = MediaQuery.of(this); + final double screenWidth = mediaQueryData.size.width; + final double screenHeight = mediaQueryData.size.height; + final double devicePixelRatio = mediaQueryData.devicePixelRatio; + bool isTablet = (devicePixelRatio < 2 && (screenWidth >= 700 || screenHeight >= 700)) || (devicePixelRatio >= 2 && (screenWidth >= 1000 || screenHeight >= 1000)); + return isTablet; + } void showConfirmDialog(String message, {String? title, VoidCallback? onTap}) => showDialog( context: this, builder: (BuildContext cxt) => ConfirmDialog(message: message, onTap: onTap, title: title), diff --git a/lib/main.dart b/lib/main.dart index 1ad94683..f3e3bc3a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -27,6 +27,7 @@ import 'package:test_sa/controllers/providers/api/status_drop_down/report/servic import 'package:test_sa/controllers/providers/api/status_drop_down/report/service_report_last_calls_provider.dart'; import 'package:test_sa/controllers/providers/api/status_drop_down/report/service_report_repair_location_provider.dart'; import 'package:test_sa/controllers/providers/api/status_drop_down/report/service_types_provider.dart'; +import 'package:test_sa/modules/asset_inventory_module/provider/asset_inventory_provider.dart'; 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/internal_audit_module/pages/equipment_internal_audit/create_equipment_internal_audit_form.dart'; @@ -302,6 +303,7 @@ class MyApp extends StatelessWidget { //ChangeNotifierProvider(create: (_) => RequestStatusProvider()), ChangeNotifierProvider(create: (_) => VendorProvider()), ChangeNotifierProvider(create: (_) => PpmChecklistStatusProvider()), + ChangeNotifierProvider(create: (_) => AssetInventoryProvider()), ], child: GestureDetector( onTap: () { diff --git a/lib/models/all_requests_and_count_model.dart b/lib/models/all_requests_and_count_model.dart index 511ece33..e980e164 100644 --- a/lib/models/all_requests_and_count_model.dart +++ b/lib/models/all_requests_and_count_model.dart @@ -123,11 +123,14 @@ class RequestsDetails { String? statusReceiver; String? assetTransferFrom; String? assetTransferTo; + String? sessionType; String? code; String? date; String? siteTransferFrom; String? siteTransferTo; int? transactionType; + int? numberOfAssets; + int? numberOfSites; RequestsDetails( {this.id, @@ -142,6 +145,9 @@ class RequestsDetails { this.manufacturer, this.requestType, this.requestNo, + this.numberOfAssets, + this.numberOfSites, + this.sessionType, this.gasType, this.site, this.statusReceiver, @@ -165,6 +171,9 @@ class RequestsDetails { supplier = json['supplier']; manufacturer = json['manufacturer']; requestType = json['requestType']; + sessionType = json['sessionType']; + numberOfAssets = json['numberOfAssets']; + numberOfSites = json['numberOfSites']; requestNo = json['requestNo']; gasType = json['gasType']; site = json['site']; @@ -197,6 +206,9 @@ class RequestsDetails { data['statusReceiver'] = statusReceiver; data['assetTransferFrom'] = assetTransferFrom; data['assetTransferTo'] = assetTransferTo; + data['sessionType'] = sessionType; + data['numberOfSites'] = numberOfSites; + data['numberOfAssets'] = numberOfAssets; data['code'] = code; data['date'] = date; data['siteTransferFrom'] = siteTransferFrom; diff --git a/lib/models/device/asset.dart b/lib/models/device/asset.dart index 9473cfcc..1e41dc31 100644 --- a/lib/models/device/asset.dart +++ b/lib/models/device/asset.dart @@ -142,11 +142,11 @@ class Asset { Building? building; // Now nullable Floor? floor; // Now nullable Department? department; // Now nullable + Rooms? room; MappedSite? mappedSite; // Now nullable MappedBuilding? mappedBuilding; // Now nullable MappedFloor? mappedFloor; // Now nullable - MappedDepartment? mappedDepartment; // No - Rooms? room; // Now nullable + MappedDepartment? mappedDepartment; // num? testsDay; // Now nullable num? purchasingPrice; // Now nullable String? nbv; // Now nullable diff --git a/lib/models/new_models/building.dart b/lib/models/new_models/building.dart index 131d8d40..e112af11 100644 --- a/lib/models/new_models/building.dart +++ b/lib/models/new_models/building.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:test_sa/models/base.dart'; import 'package:test_sa/models/new_models/floor.dart'; @@ -10,15 +12,20 @@ class Building extends Base { }) : super(identifier: id?.toString() ?? '', name: name); // Handle potential null id Building.fromJson(dynamic json) { - id = json['id']; - identifier = id?.toString() ?? ''; // Handle potential null id - name = json['name']; + id = json['id'] ?? json['buildingId']; + identifier = id?.toString() ?? ''; + name = json['name'] ?? json['buildingName']; value = json['value']; if (json['floors'] != null) { floors = []; json['floors'].forEach((v) { floors!.add(Floor.fromJson(v)); }); + } else if (json['assetInventoryFloors'] != null) { + floors = []; + json['assetInventoryFloors'].forEach((v) { + floors!.add(Floor.fromJson(v)); + }); } } diff --git a/lib/models/new_models/dashboard_detail.dart b/lib/models/new_models/dashboard_detail.dart index de0617be..093019b2 100644 --- a/lib/models/new_models/dashboard_detail.dart +++ b/lib/models/new_models/dashboard_detail.dart @@ -50,11 +50,14 @@ class Data { String? priorityName; bool? isHighPriority; String? assetName; + String? sessionType; String? rejectReason; String? assetNumber; String? requestTypeName; String? requestNo; int? transactionNo; + int? numberOfSites; + int? numberOfAssets; String? nameOfType; Data( @@ -63,6 +66,9 @@ class Data { this.nameOfType, this.transactionDate, this.statusName, + this.numberOfAssets, + this.numberOfSites, + this.sessionType, this.priorityName, this.isHighPriority, this.assetName, @@ -79,6 +85,9 @@ class Data { priorityName = json['priorityName']; isHighPriority = json['isHighPriority']; assetName = json['assetName']; + sessionType = json['sessionType']; + numberOfSites = json['numberOfSites']; + numberOfAssets = json['numberOfAssets']; assetNumber = json['assetNumber']; requestTypeName = json['requestTypeName']; requestNo = json['requestNo']; @@ -101,6 +110,9 @@ class Data { data['requestNo'] = requestNo; data['rejectReason'] = rejectReason; data['transactionNo'] = transactionNo; + data['sessionType'] = sessionType; + data['numberOfAssets'] = numberOfAssets; + data['numberOfSites'] = numberOfSites; data['nameOfType'] = nameOfType; return data; } diff --git a/lib/models/new_models/department.dart b/lib/models/new_models/department.dart index fb93cc40..e48585da 100644 --- a/lib/models/new_models/department.dart +++ b/lib/models/new_models/department.dart @@ -13,7 +13,7 @@ class Department extends Base { }) : super(identifier: id?.toString() ?? '', name: departmentName); // Handle potential null id Department.fromJson(dynamic json) { - id = json['id']; + id = json['id']??json['departmentId']; identifier = id?.toString() ?? ''; // Handle potential null id departmentName = json['departmentName'] ?? json['name']; name = departmentName; @@ -25,6 +25,11 @@ class Department extends Base { json['rooms'].forEach((v) { rooms!.add(Rooms.fromJson(v)); // Use '!' since rooms is non-nullable after initialization }); + } else if (json['assetInventoryRooms'] != null) { + rooms = []; + json['assetInventoryRooms'].forEach((v) { + rooms!.add(Rooms.fromJson(v)); // Use '!' since rooms is non-nullable after initialization + }); } } @@ -33,7 +38,7 @@ class Department extends Base { String? departmentCode; // Now nullable dynamic departmentId; // Now nullable String? ntCode; // Now nullable - List? rooms; // Now nullable + List? rooms=[]; // Now nullable Department copyWith({ num? id, // Parameters are now nullable diff --git a/lib/models/new_models/floor.dart b/lib/models/new_models/floor.dart index 2f62da25..33e74d8b 100644 --- a/lib/models/new_models/floor.dart +++ b/lib/models/new_models/floor.dart @@ -10,9 +10,9 @@ class Floor extends Base { }) : super(identifier: id?.toString() ?? '', name: name); // Handle potentialnull id Floor.fromJson(dynamic json) { - id = json['id']; + id = json['id']??json['floorId']; identifier = id?.toString() ?? ''; // Handle potential null id - name = json['name']; + name = json['name']??json['floorName']; value = json['value']; if (json['departments'] != null) { departments = []; @@ -20,6 +20,12 @@ class Floor extends Base { departments!.add(Department.fromJson(v)); // Use '!' since departments is non-nullable after initialization }); } + else if (json['assetInventoryDepartments'] != null) { + departments = []; + json['assetInventoryDepartments'].forEach((v) { + departments!.add(Department.fromJson(v)); // Use '!' since departments is non-nullable after initialization + }); + } } num? id; // Now nullable diff --git a/lib/models/new_models/room_model.dart b/lib/models/new_models/room_model.dart index 1fa7ccbe..62012565 100644 --- a/lib/models/new_models/room_model.dart +++ b/lib/models/new_models/room_model.dart @@ -10,8 +10,8 @@ class Rooms extends Base { Rooms.fromJson(Map? json) { // Handle potential null json input - id = json?['id']; // Use null-aware operator - name = json?['name']; + id = json?['id']?? json?['roomId']; + name = json?['name']??json?['roomName']; value = json?['value']; } diff --git a/lib/models/new_models/site.dart b/lib/models/new_models/site.dart index e76839fd..83922549 100644 --- a/lib/models/new_models/site.dart +++ b/lib/models/new_models/site.dart @@ -9,9 +9,9 @@ class Site extends Base { }) : super(identifier: id?.toString() ?? '', name: custName); // Handle potential null id Site.fromJson(dynamic json) { - id = json['id']; + id = json['siteId'] ?? json['id']; identifier = id?.toString() ?? ''; // Handle potential null id - custName = json['custName']?? json['siteName']; + custName = json['custName']?? json['siteName']; name = custName; if (json['buildings'] != null) { buildings = []; @@ -19,6 +19,12 @@ class Site extends Base { buildings!.add(Building.fromJson(v)); // Use '!' since buildings is initialized here }); } + else if (json['assetInventoryBuildings'] != null) { + buildings = []; + json['assetInventoryBuildings'].forEach((v) { + buildings!.add(Building.fromJson(v)); + }); + } } num? id; // Now nullable diff --git a/lib/models/new_models/task_request/task_request_model.dart b/lib/models/new_models/task_request/task_request_model.dart index 3dc5e315..d1b6dc1a 100644 --- a/lib/models/new_models/task_request/task_request_model.dart +++ b/lib/models/new_models/task_request/task_request_model.dart @@ -928,6 +928,8 @@ class TaskAsset { String? roomName; String? supplierName; String? assetTypeName; + String? poNumber; + String? installationDate; TaskAsset(); @@ -946,24 +948,26 @@ class TaskAsset { roomName = json['roomName']; supplierName = json['supplierName']; assetTypeName = json['assetTypeName']; + poNumber = json['poNumber']; + installationDate = json['installationDate']; } - Map toJson() { - return { - 'id': id, - 'assetNumber': assetNumber, - 'serialNo': serialNo, - 'assetName': assetName, - 'model': model, - 'manufacturer': manufacturer, - 'siteName': siteName, - 'siteId': siteId, - 'buildingName': buildingName, - 'floorName': floorName, - 'departmentName': departmentName, - 'roomName': roomName, - 'supplierName': supplierName, - 'assetTypeName': assetTypeName, - }; - } + Map toJson() => { + 'id': id, + 'assetNumber': assetNumber, + 'serialNo': serialNo, + 'assetName': assetName, + 'model': model, + 'manufacturer': manufacturer, + 'siteName': siteName, + 'siteId': siteId, + 'buildingName': buildingName, + 'floorName': floorName, + 'departmentName': departmentName, + 'roomName': roomName, + 'supplierName': supplierName, + 'assetTypeName': assetTypeName, + 'poNumber': poNumber, + 'installationDate': installationDate, + }; } diff --git a/lib/models/new_models/users_based_on_search_model.dart b/lib/models/new_models/users_based_on_search_model.dart index 54480f91..4dea3117 100644 --- a/lib/models/new_models/users_based_on_search_model.dart +++ b/lib/models/new_models/users_based_on_search_model.dart @@ -6,17 +6,11 @@ class UsersBasedOnSearchModel { int? languageId; String? extensionNo; String? phoneNumber; + String? positionName; + String? position; bool? isActive; - UsersBasedOnSearchModel( - {this.userId, - this.userName, - this.email, - this.employeeId, - this.languageId, - this.extensionNo, - this.phoneNumber, - this.isActive}); + UsersBasedOnSearchModel({this.userId, this.userName, this.email, this.employeeId, this.languageId, this.extensionNo, this.phoneNumber, this.positionName, this.position, this.isActive}); UsersBasedOnSearchModel.fromJson(Map json) { userId = json['userId']; @@ -26,6 +20,8 @@ class UsersBasedOnSearchModel { languageId = json['languageId']; extensionNo = json['extensionNo']; phoneNumber = json['phoneNumber']; + positionName = json['positionName']; + position = json['position']; isActive = json['isActive']; } @@ -38,6 +34,8 @@ class UsersBasedOnSearchModel { data['languageId'] = this.languageId; data['extensionNo'] = this.extensionNo; data['phoneNumber'] = this.phoneNumber; + data['positionName'] = this.positionName; + data['position'] = this.position; data['isActive'] = this.isActive; return data; } diff --git a/lib/models/user.dart b/lib/models/user.dart index de6f88b8..76ac1a2c 100644 --- a/lib/models/user.dart +++ b/lib/models/user.dart @@ -11,7 +11,6 @@ class User { List? departmentId; List? departmentName; String? message; - String? username; String? userID; String? email; @@ -39,6 +38,7 @@ class User { bool? lockoutEnabled; int? accessFailedCount; List? assetGroups; + AssetGroup? selectedAssetGroup; bool? employeeIsHMG; bool? enableWifi; bool? enableNFC; @@ -158,6 +158,7 @@ class User { map['assetGroups'] = assetGroups!.map((v) => v.toJson()).toList(); } map['tokenlife'] = tokenlife; + map['selectedAssetGroup'] = selectedAssetGroup?.toJson(); map['isAuthenticated'] = isAuthenticated; map['hasError'] = hasError; map['profilePhotoName'] = profilePhotoName; @@ -217,7 +218,7 @@ class User { userRoles!.add(UserRoles.fromJson(v)); }); } - + selectedAssetGroup = json['selectedAssetGroup'] != null ? AssetGroup.fromJson(json['selectedAssetGroup']) : null; if (json['assetGroups'] != null) { assetGroups = []; json['assetGroups'].forEach((v) { diff --git a/lib/modules/asset_inventory_module/models/asset_inventory_model.dart b/lib/modules/asset_inventory_module/models/asset_inventory_model.dart new file mode 100644 index 00000000..0b6796cb --- /dev/null +++ b/lib/modules/asset_inventory_module/models/asset_inventory_model.dart @@ -0,0 +1,297 @@ +import 'dart:developer'; + +import 'package:test_sa/models/new_models/building.dart'; +import 'package:test_sa/models/new_models/floor.dart'; +import 'package:test_sa/models/new_models/room_model.dart'; +import 'package:test_sa/models/new_models/site.dart'; +import 'package:test_sa/models/service_request/supplier_details.dart'; +import '../../../models/new_models/department.dart'; + +class AssetInventoryResponse { + num? totalRows; + num? count; + String? message; + String? title; + String? innerMessage; + num? responseCode; + bool? isSuccess; + List? assetList; + + AssetInventoryResponse({ + this.totalRows, + this.count, + this.message, + this.title, + this.innerMessage, + this.responseCode, + this.isSuccess, + this.assetList, + }); + + AssetInventoryResponse.fromJson(Map json) { + totalRows = json['totalRows']; + count = json['count']; + message = json['message']; + title = json['title']; + innerMessage = json['innerMessage']; + responseCode = json['responseCode']; + isSuccess = json['isSuccess']; + if (json['data'] != null) { + assetList = (json['data'] as List).map((item) => AssetInventoryModel.fromJson(item)).toList(); + } + } + + Map toJson() { + return { + 'totalRows': totalRows, + 'count': count, + 'message': message, + 'title': title, + 'innerMessage': innerMessage, + 'responseCode': responseCode, + 'isSuccess': isSuccess, + }; + } +} + +class AssetInventoryModel { + num? id; + num? assetId; + String? transactionDate; + String? assetNumber; + String? serialNo; + String? assetName; + num? assetNameId; + String? model; + String? manufacturer; + String? supplierName; + String? siteName; + String? buildingName; + String? floorName; + String? departmentName; + String? roomName; + num? statusId; + String? status; + num? statusValue; + num? statusByAdminId; + String? statusByAdmin; + num? statusByAdminValue; + bool? isNotRegistered; + num? sessionId; + num? assetImportId; + num? siteId; + num? supplierId; + num? buildingId; + num? floorId; + num? departmentId; + num? roomId; + String? newAssetNumber; + String? newSerialNo; + num? newAssetNameId; + String? newAssetNameText; + num? newModelId; + String? newModelName; + num? manufacturerId; + num? modelId; + num? newManufacturerId; + String? newManufacturerName; + num? newSupplierId; + String? newSupplierName; + String? photo; + String? photoOriginName; + String? remarks; + + Site? site; + Building? building; + Floor? floor; + Department? department; + Rooms? room; + SupplierDetails? supplier; + + AssetInventoryModel({ + this.id, + this.sessionId, + this.assetId, + this.transactionDate, + this.assetNumber, + this.assetNameId, + this.serialNo, + this.assetName, + this.model, + this.manufacturer, + this.supplierName, + this.siteName, + this.buildingName, + this.floorName, + this.departmentName, + this.roomName, + this.statusId, + this.status, + this.statusValue, + this.statusByAdminId, + this.statusByAdmin, + this.statusByAdminValue, + this.isNotRegistered, + this.assetImportId, + this.siteId, + this.buildingId, + this.floorId, + this.departmentId, + this.supplierId, + this.roomId, + this.newAssetNumber, + this.manufacturerId, + this.newSerialNo, + this.newAssetNameId, + this.newAssetNameText, + this.newModelId, + this.modelId, + this.newModelName, + this.newManufacturerId, + this.newManufacturerName, + this.newSupplierId, + this.newSupplierName, + this.photo, + this.photoOriginName, + this.remarks, + this.site, + this.department, + this.building, + this.floor, + this.room, + this.supplier, + }); + + AssetInventoryModel.fromJson(Map json) { + id = json['id']; + assetId = json['assetId']; + transactionDate = json['transactionDate']; + assetNumber = json['assetNumber']; + assetNameId = json['assetNameId']; + serialNo = json['serialNo']; + assetName = json['assetName']; + model = json['model'] ?? json['modelName']; + manufacturer = json['manufacturer'] ?? json['manufacturerName']; + supplierName = json['supplierName']; + siteName = json['site'] ?? json['siteName']; + buildingName = json['building'] ?? json['buildingName']; + floorName = json['floor'] ?? json['floorName']; + departmentName = json['department'] ?? json['departmentName']; + roomName = json['room'] ?? json['roomName']; + statusId = json['statusId']; + status = json['status'] ?? json['classification']; + statusValue = json['statusValue']; + isNotRegistered = json['isNotRegistered']; + sessionId = json['sessionId']; + assetImportId = json['assetImportId']??json['id']; + siteId = json['siteId']; + buildingId = json['buildingId']; + floorId = json['floorId']; + supplierId = json['supplierId']; + departmentId = json['departmentId']; + roomId = json['roomId']; + modelId = json['modelId']; + photo = json['photo']; + photoOriginName = json['photoOriginName']; + remarks = json['remarks']; + //new data.. + // newAssetNumber = json['newAssetNumber'] ?? json['assetNumber']; + // manufacturerId = json['manufacturerId'] ?? json['manufacturerId']; + // newSerialNo = json['newSerialNo'] ?? json['serialNo']; + // newAssetNameId = json['newAssetNameId'] ?? json['assetNameId']; + // newAssetNameText = json['newAssetNameText'] ?? json['assetNameText']; + // newModelId = json['newModelId'] ?? json['modelId']; + // newModelName = json['newModelName'] ?? json['modelName']; + // newManufacturerId = json['newManufacturerId'] ?? json['manufacturerId']; + // newManufacturerName = json['newManufacturerName'] ?? json['manufacturerName']; + // newSupplierId = json['newSupplierId'] ?? json['supplierId']; + // newSupplierName = json['newSupplierName'] ?? json['supplierName']; + } + + Map toJson() { + return { + 'isNotRegistered': isNotRegistered, + 'sessionId': sessionId, + 'assetImportId': assetImportId, + 'transactionDate': transactionDate, + 'assetId': assetId, + 'siteId': siteId, + 'buildingId': buildingId, + 'floorId': floorId, + 'departmentId': departmentId, + 'roomId': roomId, + 'newSerialNo': newSerialNo, + 'newAssetNameId': newAssetNameId, + 'newAssetNameText': newAssetNameText, + 'newAssetNumber': newAssetNumber, + 'newModelId': newModelId, + 'newModelName': newModelName, + 'newManufacturerId': newManufacturerId, + 'newManufacturerName': newManufacturerName, + 'newSupplierId': newSupplierId, + 'newSupplierName': newSupplierName, + 'photo': photo, + 'remarks': remarks, + }; + } + + AssetInventoryModel mergeWith(AssetInventoryModel? other) { + if (other == null) return this; + + return AssetInventoryModel( + id: id ?? other.id, + assetId: assetId ?? other.assetId, + transactionDate: transactionDate ?? other.transactionDate, + assetNumber: assetNumber ?? other.assetNumber, + serialNo: serialNo ?? other.serialNo, + assetName: assetName ?? other.assetName, + assetNameId: assetNameId ?? other.assetNameId, + model: model ?? other.model, + manufacturer: manufacturer ?? other.manufacturer, + supplierName: supplierName ?? other.supplierName, + siteName: siteName ?? other.siteName, + buildingName: buildingName ?? other.buildingName, + floorName: floorName ?? other.floorName, + departmentName: departmentName ?? other.departmentName, + roomName: roomName ?? other.roomName, + statusId: statusId ?? other.statusId, + status: status ?? other.status, + statusValue: statusValue ?? other.statusValue, + statusByAdminId: statusByAdminId ?? other.statusByAdminId, + statusByAdmin: statusByAdmin ?? other.statusByAdmin, + statusByAdminValue: statusByAdminValue ?? other.statusByAdminValue, + isNotRegistered: isNotRegistered ?? other.isNotRegistered, + sessionId: sessionId ?? other.sessionId, + assetImportId: assetImportId ?? other.assetImportId, + siteId: siteId ?? other.siteId, + supplierId: supplierId ?? other.supplierId, + buildingId: buildingId ?? other.buildingId, + floorId: floorId ?? other.floorId, + departmentId: departmentId ?? other.departmentId, + roomId: roomId ?? other.roomId, + newAssetNumber: newAssetNumber ?? other.newAssetNumber, + newSerialNo: newSerialNo ?? other.newSerialNo, + newAssetNameId: newAssetNameId ?? other.newAssetNameId, + newAssetNameText: newAssetNameText ?? other.newAssetNameText, + newModelId: newModelId ?? other.newModelId, + newModelName: newModelName ?? other.newModelName, + manufacturerId: manufacturerId ?? other.manufacturerId, + modelId: modelId ?? other.modelId, + newManufacturerId: newManufacturerId ?? other.newManufacturerId, + newManufacturerName: newManufacturerName ?? other.newManufacturerName, + newSupplierId: newSupplierId ?? other.newSupplierId, + newSupplierName: newSupplierName ?? other.newSupplierName, + photo: photo ?? other.photo, + photoOriginName: photoOriginName ?? other.photoOriginName, + remarks: remarks ?? other.remarks, + site: site ?? other.site, + building: building ?? other.building, + floor: floor ?? other.floor, + department: department ?? other.department, + room: room ?? other.room, + supplier: supplier ?? other.supplier, + ); + } + + +} diff --git a/lib/modules/asset_inventory_module/models/session_model.dart b/lib/modules/asset_inventory_module/models/session_model.dart new file mode 100644 index 00000000..8de8503b --- /dev/null +++ b/lib/modules/asset_inventory_module/models/session_model.dart @@ -0,0 +1,234 @@ +import 'package:test_sa/models/new_models/site.dart'; + +class SessionModel { + int? id; + String? createdDate; + String? sessionName; + int? sessionTypeId; + int? sessionTypeValue; + String? sessionTypeName; + int? statusId; + String? statusName; + String? startDate; + String? endDate; + List assetInventorySites = []; + List assetInventoryAssignedEmployee = []; + + SessionModel({ + this.id, + this.sessionName, + this.createdDate, + this.sessionTypeId, + this.sessionTypeValue, + this.sessionTypeName, + this.statusId, + this.statusName, + this.startDate, + this.endDate, + List? assetInventorySites, + List? assetInventoryAssignedEmployee, + }) { + this.assetInventorySites = assetInventorySites ?? []; + this.assetInventoryAssignedEmployee = assetInventoryAssignedEmployee ?? []; + } + + SessionModel.fromJson(Map json) { + id = json['id']; + sessionName = json['sessionName']; + sessionTypeId = json['sessionTypeId']; + sessionTypeValue = json['sessionTypeValue']; + sessionTypeName = json['sessionTypeName']; + statusId = json['statusId']; + createdDate = json['createdDate']; + statusName = json['statusName']; + startDate = json['startDate']; + endDate = json['endDate']; + + if (json['assetInventorySites'] != null) { + assetInventorySites = (json['assetInventorySites'] as List).map((e) => Site.fromJson(e)).toList(); + } + + if (json['assetInventoryAssignedEmployee'] != null) { + assetInventoryAssignedEmployee = (json['assetInventoryAssignedEmployee'] as List).map((e) => AssetInventoryAssignedEmployee.fromJson(e)).toList(); + } + } + + Map toJson() { + final map = {}; + map['id'] = id; + map['sessionName'] = sessionName; + map['sessionTypeId'] = sessionTypeId; + map['sessionTypeValue'] = sessionTypeValue; + map['sessionTypeName'] = sessionTypeName; + map['statusId'] = statusId; + map['statusName'] = statusName; + map['createdDate'] = createdDate; + map['startDate'] = startDate; + map['endDate'] = endDate; + map['assetInventorySites'] = assetInventorySites.map((e) => e.toJson()).toList(); + map['assetInventoryAssignedEmployee'] = assetInventoryAssignedEmployee.map((e) => e.toJson()).toList(); + return map; + } +} + +// class AssetInventorySite { +// int? siteId; +// String? siteName; +// List assetInventoryBuildings = []; +// +// AssetInventorySite({ +// this.siteId, +// this.siteName, +// List? assetInventoryBuildings, +// }) { +// this.assetInventoryBuildings = assetInventoryBuildings ?? []; +// } +// +// AssetInventorySite.fromJson(Map json) { +// siteId = json['siteId']; +// siteName = json['siteName']; +// +// if (json['assetInventoryBuildings'] != null) { +// assetInventoryBuildings = (json['assetInventoryBuildings'] as List).map((e) => AssetInventoryBuilding.fromJson(e)).toList(); +// } +// } +// +// Map toJson() { +// final map = {}; +// map['siteId'] = siteId; +// map['siteName'] = siteName; +// map['assetInventoryBuildings'] = assetInventoryBuildings.map((e) => e.toJson()).toList(); +// return map; +// } +// } +// +// class AssetInventoryBuilding { +// int? buildingId; +// String? buildingName; +// List assetInventoryFloors = []; +// +// AssetInventoryBuilding({ +// this.buildingId, +// this.buildingName, +// List? assetInventoryFloors, +// }) { +// this.assetInventoryFloors = assetInventoryFloors ?? []; +// } +// +// AssetInventoryBuilding.fromJson(Map json) { +// buildingId = json['buildingId']; +// buildingName = json['buildingName']; +// +// if (json['assetInventoryFloors'] != null) { +// assetInventoryFloors = (json['assetInventoryFloors'] as List).map((e) => AssetInventoryFloor.fromJson(e)).toList(); +// } +// } +// +// Map toJson() { +// final map = {}; +// map['buildingId'] = buildingId; +// map['buildingName'] = buildingName; +// map['assetInventoryFloors'] = assetInventoryFloors.map((e) => e.toJson()).toList(); +// return map; +// } +// } +// +// class AssetInventoryFloor { +// int? floorId; +// String? floorName; +// List assetInventoryDepartments = []; +// +// AssetInventoryFloor({ +// this.floorId, +// this.floorName, +// List? assetInventoryDepartments, +// }) { +// this.assetInventoryDepartments = assetInventoryDepartments ?? []; +// } +// +// AssetInventoryFloor.fromJson(Map json) { +// floorId = json['floorId']; +// floorName = json['floorName']; +// +// if (json['assetInventoryDepartments'] != null) { +// assetInventoryDepartments = (json['assetInventoryDepartments'] as List).map((e) => AssetInventoryDepartment.fromJson(e)).toList(); +// } +// } +// +// Map toJson() { +// final map = {}; +// map['floorId'] = floorId; +// map['floorName'] = floorName; +// map['assetInventoryDepartments'] = assetInventoryDepartments.map((e) => e.toJson()).toList(); +// return map; +// } +// } +// +// class AssetInventoryDepartment { +// int? departmentId; +// String? departmentName; +// List assetInventoryRooms = []; +// +// AssetInventoryDepartment({ +// this.departmentId, +// this.departmentName, +// List? assetInventoryRooms, +// }) { +// this.assetInventoryRooms = assetInventoryRooms ?? []; +// } +// +// AssetInventoryDepartment.fromJson(Map json) { +// departmentId = json['departmentId']; +// departmentName = json['departmentName']; +// +// if (json['assetInventoryRooms'] != null) { +// assetInventoryRooms = (json['assetInventoryRooms'] as List).map((e) => AssetInventoryRoom.fromJson(e)).toList(); +// } +// } +// +// Map toJson() { +// final map = {}; +// map['departmentId'] = departmentId; +// map['departmentName'] = departmentName; +// map['assetInventoryRooms'] = assetInventoryRooms.map((e) => e.toJson()).toList(); +// return map; +// } +// } +// +// class AssetInventoryRoom { +// int? roomId; +// String? roomName; +// +// AssetInventoryRoom({this.roomId, this.roomName}); +// +// AssetInventoryRoom.fromJson(Map json) { +// roomId = json['roomId']; +// roomName = json['roomName']; +// } +// +// Map toJson() { +// final map = {}; +// map['roomId'] = roomId; +// map['roomName'] = roomName; +// return map; +// } +// } + +class AssetInventoryAssignedEmployee { + String? assignedEngineerId; + String? assignedEngineerName; + + AssetInventoryAssignedEmployee({this.assignedEngineerId, this.assignedEngineerName}); + + AssetInventoryAssignedEmployee.fromJson(Map json) { + assignedEngineerId = json['assignedEngineerId']; + assignedEngineerName = json['assignedEngineerName']; + } + + Map toJson() { + final map = {}; + map['assignedEngineerId'] = assignedEngineerId; + map['assignedEngineerName'] = assignedEngineerName; + return map; + } +} diff --git a/lib/modules/asset_inventory_module/pages/asset_detail_card_view.dart b/lib/modules/asset_inventory_module/pages/asset_detail_card_view.dart new file mode 100644 index 00000000..c49a836f --- /dev/null +++ b/lib/modules/asset_inventory_module/pages/asset_detail_card_view.dart @@ -0,0 +1,117 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:test_sa/controllers/api_routes/urls.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/string_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/modules/asset_inventory_module/models/asset_inventory_model.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/views/widgets/loaders/image_loader.dart'; + +class AssetDetailCardView extends StatelessWidget { + AssetInventoryModel assetInventoryModel; + VoidCallback onDeletePress; + + AssetDetailCardView({ + super.key, + required this.assetInventoryModel, + required this.onDeletePress, + }); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Row( + children: [ + Text( + context.translation.assetInformation, + style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + ).expanded, + 'delete_icon'.toSvgAsset().onPress(() { + onDeletePress(); + }), + ], + ), + 8.height, + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'transaction Date: ${assetInventoryModel.transactionDate?.toServiceRequestDetailsFormat ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + '${context.translation.assetNumber}: ${assetInventoryModel.assetNumber ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + '${context.translation.serialNo}: ${assetInventoryModel.serialNo ?? '-'}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + '${context.translation.manufacture}: ${assetInventoryModel.manufacturer ?? ''}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + '${context.translation.model}: ${assetInventoryModel.model ?? ''}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + '${context.translation.site}: ${assetInventoryModel.siteName ?? ''}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + '${context.translation.building}: ${assetInventoryModel.buildingName ?? ''}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + '${context.translation.floor}: ${assetInventoryModel.floorName ?? ''}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + '${context.translation.department}: ${assetInventoryModel.departmentName ?? ''}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + '${context.translation.supplier}: ${assetInventoryModel.supplierName ?? ''}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + '${'Classifications'.addTranslation}: ${assetInventoryModel.status ?? ''}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + '${'Remarks'.addTranslation}: ${assetInventoryModel.remarks ?? ''}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + ], + ).expanded, + Container( + decoration: BoxDecoration(color: AppColor.neutral100, borderRadius: BorderRadius.circular(15)), + height: 115.toScreenHeight, + width: 115.toScreenWidth, + margin: const EdgeInsets.only(top: 8), + child: assetInventoryModel.photo != null && assetInventoryModel.photo!.isNotEmpty + ? ClipRRect( + borderRadius: BorderRadius.circular(8), + child: ImageLoader( + url: URLs.getFileUrl(assetInventoryModel.photo), + boxFit: BoxFit.cover, + height: 48, + width: 48, + )) + : 'image_placeholder'.toSvgAsset().center, + ) + ], + ), + ], + ).toShadowContainer(context, borderRadius: 20, padding: 12); + } +} diff --git a/lib/modules/asset_inventory_module/pages/asset_inventory_detail_view.dart b/lib/modules/asset_inventory_module/pages/asset_inventory_detail_view.dart new file mode 100644 index 00000000..5c826f56 --- /dev/null +++ b/lib/modules/asset_inventory_module/pages/asset_inventory_detail_view.dart @@ -0,0 +1,163 @@ +import 'package:flutter/material.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/string_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/modules/asset_inventory_module/models/session_model.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; +import 'package:test_sa/views/widgets/requests/request_status.dart'; + +class AssetInventoryDetailView extends StatefulWidget { + SessionModel sessionModel; + + AssetInventoryDetailView({Key? key, required this.sessionModel}) : super(key: key); + + @override + State createState() => _AssetInventoryDetailViewState(); +} + +class _AssetInventoryDetailViewState extends State { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return widget.sessionModel.id == null + ? const NoDataFound() + : ListView( + padding: const EdgeInsets.all(16), + children: [ + requestDetailCard(context, widget.sessionModel), + 12.height, + siteListCard(context, widget.sessionModel), + ], + ); + } + + TextStyle infoTextStyle(context) => AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120); + + Widget requestDetailCard(BuildContext context, SessionModel sessionModel) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + StatusLabel( + label: sessionModel.statusName, + id: sessionModel.statusId, + radius: 4, + textColor: AppColor.green15, + backgroundColor: AppColor.greenStatus(context), + ), + Text( + sessionModel.createdDate!.toString().toServiceRequestCardFormat, + textAlign: TextAlign.end, + style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + ) + ], + ), + 8.height, + Text( + context.translation.requestDetails, + style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + ), + 8.height, + Text( + '${'Session Name'.addTranslation}: ${sessionModel.sessionName?.cleanupWhitespace.capitalizeFirstOfEach}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + '${'Session Type'.addTranslation}: ${sessionModel.sessionTypeName}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + '${'Start Date'.addTranslation}: ${sessionModel.startDate?.toServiceRequestDetailsFormat ?? ""}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + '${'End Date'.addTranslation}: ${sessionModel.endDate?.toServiceRequestDetailsFormat ?? ""}', + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ), + ], + ).toShadowContainer(context, padding: 12, borderRadius: 20); + } + + Widget siteListCard(BuildContext context, SessionModel sessionModel) { + final sites = sessionModel.assetInventorySites ?? []; + + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Sites Information", + style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + ), + 8.height, + ListView.separated( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + padding: EdgeInsets.zero, + itemCount: sites.length, + itemBuilder: (cxt, siteIndex) { + final site = sites[siteIndex]; + final buildingNames = (site.buildings ?? []).map((b) => b.name).where((name) => name != null && name!.trim().isNotEmpty).join(', '); + final floorNames = (site.buildings ?? []).expand((b) => b.floors ?? []).map((f) => f.name).where((name) => name != null && name!.trim().isNotEmpty).join(', '); + final departmentNames = + (site.buildings ?? []).expand((b) => b.floors ?? []).expand((f) => f.departments ?? []).map((d) => d.name).where((name) => name != null && name!.trim().isNotEmpty).join(', '); + final roomNames = (site.buildings ?? []) + .expand((b) => b.floors ?? []) + .expand((f) => f.departments ?? []) + .expand((d) => d.rooms ?? []) + .map((r) => r.name) + .where((name) => name != null && name!.trim().isNotEmpty) + .join(', '); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + site.name ?? '-', + style: AppTextStyles.heading6.copyWith( + color: context.isDark ? AppColor.neutral30 : AppColor.neutral50, + ), + ), + Text( + '${context.translation.building}: ${buildingNames.isNotEmpty ? buildingNames : '-'}', + style: AppTextStyles.bodyText.copyWith( + color: context.isDark ? AppColor.neutral10 : AppColor.neutral120, + ), + ), + Text( + '${context.translation.floor}: ${floorNames.isNotEmpty ? floorNames : '-'}', + style: AppTextStyles.bodyText.copyWith( + color: context.isDark ? AppColor.neutral10 : AppColor.neutral120, + ), + ), + Text( + '${context.translation.department}: ${departmentNames.isNotEmpty ? departmentNames : '-'}', + style: AppTextStyles.bodyText.copyWith( + color: context.isDark ? AppColor.neutral10 : AppColor.neutral120, + ), + ), + Text( + '${context.translation.room}: ${roomNames.isNotEmpty ? roomNames : '-'}', + style: AppTextStyles.bodyText.copyWith( + color: context.isDark ? AppColor.neutral10 : AppColor.neutral120, + ), + ), + ], + ); + }, + separatorBuilder: (cxt, index) => const Divider().defaultStyle(context), + ), + ], + ).toShadowContainer(context, padding: 12, borderRadius: 20); + } +} diff --git a/lib/modules/asset_inventory_module/pages/asset_inventory_form_view.dart b/lib/modules/asset_inventory_module/pages/asset_inventory_form_view.dart new file mode 100644 index 00000000..fd4bea7c --- /dev/null +++ b/lib/modules/asset_inventory_module/pages/asset_inventory_form_view.dart @@ -0,0 +1,485 @@ +import 'dart:convert'; +import 'dart:developer'; +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:flutter_advanced_switch/flutter_advanced_switch.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/common_widgets/lookup_autocomplete_field.dart'; +import 'package:test_sa/controllers/providers/api/all_requests_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/generic_attachment_model.dart'; +import 'package:test_sa/modules/asset_inventory_module/models/asset_inventory_model.dart'; +import 'package:test_sa/modules/asset_inventory_module/pages/search_asset_view.dart'; +import 'package:test_sa/modules/asset_inventory_module/provider/asset_inventory_provider.dart'; +import 'package:test_sa/modules/cm_module/utilities/service_request_utils.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_lazy_loading.dart'; +import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart'; +import 'package:test_sa/views/widgets/equipment/asset_picker.dart'; +import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; +import 'package:test_sa/views/widgets/images/multi_image_picker_item.dart'; +import 'package:test_sa/views/widgets/requests/request_status.dart'; +import '../../../new_views/common_widgets/app_filled_button.dart'; +import '../../../new_views/common_widgets/default_app_bar.dart'; + +class AssetInventoryFormView extends StatefulWidget { + static const String id = "/asset-inventory-form"; + AssetInventoryModel? assetInventoryModel; + int? sessionTypeValue; + + AssetInventoryFormView({Key? key, this.assetInventoryModel, this.sessionTypeValue}) : super(key: key); + + @override + State createState() => _AssetInventoryFormViewState(); +} + +class _AssetInventoryFormViewState extends State { + AssetInventoryModel? _scannedAssetModel = AssetInventoryModel(); + AssetInventoryModel? _pickedAssetModel = AssetInventoryModel(); + final GlobalKey _formKey = GlobalKey(); + final GlobalKey _scaffoldKey = GlobalKey(); + final TextEditingController _assetNoController = TextEditingController(); + final TextEditingController _serialNoController = TextEditingController(); + final TextEditingController _remarksController = TextEditingController(); + ValueNotifier? registeredController; + bool isRegistered = false; + + final List attachments = []; + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + resetData(); + } + + void populateFormValues() { + _serialNoController.text = _scannedAssetModel!.serialNo ?? ''; + _assetNoController.text = _scannedAssetModel!.assetNumber ?? ''; + // _scannedAssetModel?.supplier = SupplierDetails( + // suppliername: _scannedAssetModel?.supplierName, + // id: _scannedAssetModel?.supplierId, + // ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + key: _scaffoldKey, + appBar: DefaultAppBar( + title: context.translation.addAsset, + titleStyle: AppTextStyles.heading3.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + ), + body: Form( + key: _formKey, + child: Column( + children: [ + SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (widget.sessionTypeValue == 1 || widget.sessionTypeValue == 2) ...[ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + 'Not Registered'.addTranslation.heading5(context), + AdvancedSwitch( + controller: registeredController, + activeColor: AppColor.green50.withOpacity(0.5), + inactiveColor: AppColor.neutral10, + thumb: CircleAvatar(backgroundColor: isRegistered == true ? AppColor.green50 : AppColor.neutral20), + borderRadius: const BorderRadius.all(Radius.circular(30)), + width: 42.toScreenWidth, + height: 24.toScreenHeight, + onChanged: (value) { + isRegistered = value; + resetData(); + if (value) { + _scannedAssetModel?.status = 'Not Registered'; + } + setState(() {}); + }, + disabledOpacity: 1, + ), + ], + ), + 12.height, + ], + + if ((!isRegistered && widget.sessionTypeValue == 1) || (widget.sessionTypeValue == 3) || (!isRegistered && widget.sessionTypeValue == 2)) ...[ + AssetPicker( + showLoading: false, + labelColor: AppColor.white936, + iconColor: AppColor.neutral120, + label: 'Scan Asset'.addTranslation, + borderColor: AppColor.white936, + buttonColor: Colors.white, + enablePickManually: false, + showAssetInfo: false, + showBorder: true, + onPick: (asset) async { + resetData(); + setState(() {}); + if (asset.assetNumber != null) { + searchAsset(assetNo: asset.assetNumber!); + } + }), + 12.height, + AppTextFormField( + labelText: context.translation.assetNo, + backgroundColor: AppColor.fieldBgColor(context), + textAlign: TextAlign.center, + controller: _assetNoController, + showShadow: false, + enable: false, + labelStyle: AppTextStyles.textFieldLabelStyle, + style: Theme.of(context).textTheme.titleMedium, + ), + 12.height, + ], + + if (((!isRegistered && widget.sessionTypeValue == 2))) ...[ + AppFilledButton( + label: 'Search Asset'.addTranslation, + onPressed: () async { + resetData(isScanned: false); + setState(() {}); + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => SearchAssetView( + sessionId: widget.assetInventoryModel?.sessionId ?? 0, + ))).then((value) { + if (value != null) { + _pickedAssetModel = value; + _scannedAssetModel = _scannedAssetModel?.mergeWith(_pickedAssetModel) ?? _pickedAssetModel; + getClassificationData(assetImportId: _pickedAssetModel?.assetImportId); + populateFormValues(); + setState(() {}); + } + + ///Need to assign the values + }); + }, + ), + 12.height, + ], + if (((isRegistered && widget.sessionTypeValue != 3))) ...[ + AppTextFormField( + labelText: context.translation.assetNo, + backgroundColor: AppColor.fieldBgColor(context), + textAlign: TextAlign.center, + showShadow: false, + enable: true, + labelStyle: AppTextStyles.textFieldLabelStyle, + style: Theme.of(context).textTheme.titleMedium, + onChange: (value) { + _scannedAssetModel?.newAssetNumber = value; + }, + ), + 12.height, + ], + AppTextFormField( + labelText: context.translation.serialNo, + backgroundColor: AppColor.fieldBgColor(context), + controller: _serialNoController, + textAlign: TextAlign.center, + showShadow: false, + labelStyle: AppTextStyles.textFieldLabelStyle, + onChange: (value) { + if (value != _scannedAssetModel?.serialNo) { + _scannedAssetModel?.newSerialNo = value; + // setState(() {}); + } + }, + style: Theme.of(context).textTheme.titleMedium, + ), + 12.height, + LookUpAutoCompleteField( + clearAfterPick: false, + forAssetName: true, + onChanged: (value) { + _scannedAssetModel?.newAssetNameId = null; + _scannedAssetModel?.newAssetNameText = value; + }, + initialValue: _scannedAssetModel?.assetName ?? "", + label: 'Asset Name'.addTranslation, + onPick: (value) { + _scannedAssetModel?.newAssetNameText = null; + _scannedAssetModel?.assetName = value.name; + _scannedAssetModel?.newAssetNameId = value.id; + setState(() {}); + }, + ), + 12.height, + //Asset Name.. + LookUpAutoCompleteField( + clearAfterPick: false, + isManufacturer: true, + initialValue: _scannedAssetModel?.manufacturer ?? "", + label: 'Manufacturer'.addTranslation, + onChanged: (value) { + _scannedAssetModel?.newManufacturerId = null; + _scannedAssetModel?.newManufacturerName = value; + }, + onPick: (value) { + _scannedAssetModel?.newManufacturerName = null; + _scannedAssetModel?.manufacturer = value.name; + _scannedAssetModel?.newManufacturerId = value.id; + + setState(() {}); + }, + ), + 12.height, + LookUpAutoCompleteField( + clearAfterPick: false, + isManufacturer: false, + initialValue: _scannedAssetModel?.model ?? "", + label: 'Model'.addTranslation, + onChanged: (value) { + _scannedAssetModel?.newModelId = null; + _scannedAssetModel?.newModelName = value; + }, + onPick: (value) { + _scannedAssetModel?.newModelName = null; + _scannedAssetModel?.model = value.name; + _scannedAssetModel?.newModelId = value.id; + setState(() {}); + }, + ), + + 12.height, + + LookUpAutoCompleteField( + clearAfterPick: false, + forSupplier: true, + initialValue: _scannedAssetModel?.supplierName ?? "", + label: context.translation.supplier, + onChanged: (value) { + _scannedAssetModel?.newSupplierId = null; + _scannedAssetModel?.newSupplierName = value; + }, + onPick: (value) { + _scannedAssetModel?.newSupplierName = null; + _scannedAssetModel?.supplierName = value.name; + _scannedAssetModel?.newSupplierId = value.id; + setState(() {}); + }, + ), + if (!isRegistered) ...[ + 12.height, + siteInfoContainer(label: context.translation.site, value: _scannedAssetModel?.siteName ?? '-'), + 12.height, + siteInfoContainer(label: context.translation.building, value: _scannedAssetModel?.buildingName ?? '-'), + 12.height, + siteInfoContainer(label: context.translation.floor, value: _scannedAssetModel?.floorName ?? '-'), + 12.height, + siteInfoContainer(label: context.translation.department, value: _scannedAssetModel?.departmentName ?? '-'), + 12.height, + siteInfoContainer(label: context.translation.room, value: _scannedAssetModel?.roomName ?? '-'), + ], + if (_scannedAssetModel?.status != null && _scannedAssetModel!.status!.isNotEmpty) ...[ + 12.height, + classificationWidget(label: _scannedAssetModel?.status), + ], + 12.height, + Text( + 'Asset Photo'.addTranslation, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.white936), + ), + 12.height, + attachments.isEmpty + ? AttachmentPicker( + label: context.translation.attachImage, + attachment: attachments, + buttonColor: AppColor.black10, + onlyImages: true, + onChange: (value) { + setState(() {}); + }, + buttonIcon: 'image-plus'.toSvgAsset(color: AppColor.neutral120), + ) + : MultiFilesPickerItem( + file: File(attachments.first.name ?? ''), + enabled: true, + onRemoveTap: (file) { + setState(() { + attachments.clear(); + }); + }, + ), + 12.height, + AppTextFormField( + backgroundColor: AppColor.fieldBgColor(context), + labelText: 'Remarks'.addTranslation, + labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), + alignLabelWithHint: true, + textInputType: TextInputType.multiline, + showShadow: false, + controller: _remarksController, + onSaved: (text) { + _scannedAssetModel?.remarks = text; + setState(() {}); + }, + ), + // 100.height, + ], + ).toShadowContainer(context, borderRadius: 20, padding: 12)) + .expanded, + FooterActionButton.footerContainer( + context: context, + child: AppFilledButton(buttonColor: AppColor.primary10, label: context.translation.submitRequest, maxWidth: true, onPressed: _onSubmit), + ), + ], + ), + ), + ); + } + + Future searchAsset({required String assetNo}) async { + AssetInventoryProvider provider = Provider.of(context, listen: false); + + Map payload = { + "assetNumber": assetNo, + "sessionId": widget.assetInventoryModel?.sessionId, + "siteId": widget.assetInventoryModel?.site?.id, + "buildingId": widget.assetInventoryModel?.building?.id, + "floorId": widget.assetInventoryModel?.floor?.id, + "departmentId": widget.assetInventoryModel?.department?.id, + "roomId": widget.assetInventoryModel?.room?.id, + }; + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + _scannedAssetModel = await provider.searchAsset(payload: payload); + Navigator.pop(context); + if (_scannedAssetModel != null) { + populateFormValues(); + } + setState(() {}); + } + + Widget siteInfoContainer({required String label, required String value}) { + //TODO may be need to hide value for if empty or null . + return Container( + width: double.infinity, + padding: EdgeInsets.symmetric(horizontal: 12.toScreenWidth, vertical: 12.toScreenHeight), + decoration: BoxDecoration(color: AppColor.neutral80, borderRadius: BorderRadius.circular(8)), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(label, style: Theme.of(context).textTheme.bodySmall?.copyWith(color: context.isDark ? null : AppColor.neutral20, fontWeight: FontWeight.w500)), + Text( + value, + style: Theme.of(context).textTheme.bodyLarge, + ), + ], + ), + ); + } + + Widget classificationWidget({String? label}) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Classification'.addTranslation, + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.white936), + ), + //TODO Need to set background and text color according to data.. + StatusLabel( + label: label, + radius: 4, + textColor: AppColor.getPriorityStatusTextColor(context, 81), + backgroundColor: AppColor.getPriorityStatusColor(context, 370), + ), + ], + ); + } + + void _onSubmit() async { + AssetInventoryProvider assetInventoryProvider = Provider.of(context, listen: false); + _formKey.currentState!.save(); + _scannedAssetModel?.sessionId = widget.assetInventoryModel?.sessionId; + _scannedAssetModel?.siteId = widget.assetInventoryModel?.site?.id; + _scannedAssetModel?.buildingId = widget.assetInventoryModel?.building?.id; + _scannedAssetModel?.floorId = widget.assetInventoryModel?.floor?.id; + _scannedAssetModel?.departmentId = widget.assetInventoryModel?.department?.id; + _scannedAssetModel?.roomId = widget.assetInventoryModel?.room?.id; + _scannedAssetModel?.isNotRegistered = isRegistered; + if (attachments.isNotEmpty) { + String? photoName = attachments.first.name; + String fileName = ServiceRequestUtils.isLocalUrl(photoName ?? '') ? ("${photoName ?? ''.split("/").last}|${base64Encode(File(photoName ?? '').readAsBytesSync())}") : photoName ?? ''; + _scannedAssetModel?.photo = fileName; + } + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + assetInventoryProvider.saveAssetInSession(model: _scannedAssetModel).then((success) async { + ///Need to use push and remove until... + Navigator.pop(context); + if (success) { + /// need to confirm need to call this ... + AllRequestsProvider allRequestsProvider = Provider.of(context, listen: false); + allRequestsProvider.reset(); + allRequestsProvider.getAllRequests(context, typeTransaction: 8); + getAssetFilteredList(); + Navigator.pop(context); + } else { + log('api error...'); + } + }); + } + + void resetData({bool isScanned = true}) { + if (!isScanned) { + _scannedAssetModel = AssetInventoryModel(assetNumber: _scannedAssetModel?.assetNumber, assetId: _scannedAssetModel?.assetId); + } + _assetNoController.clear(); + _serialNoController.clear(); + _pickedAssetModel = AssetInventoryModel(); + attachments.clear(); + _remarksController.clear(); + registeredController?.dispose(); + } + + Future getAssetFilteredList({bool loadMore = false}) async { + AssetInventoryProvider provider = Provider.of(context, listen: false); + Map payload = { + "sessionId": widget.assetInventoryModel?.sessionId, + "siteId": widget.assetInventoryModel?.site?.id, + "buildingId": widget.assetInventoryModel?.building?.id, + "floorId": widget.assetInventoryModel?.floor?.id, + "departmentId": widget.assetInventoryModel?.department?.id, + "roomId": widget.assetInventoryModel?.room?.id, + }; + await provider.getInventoryDetailsByFilter(payload: payload, loadMore: loadMore); + provider.getAssetsInSession( + sessionId: (widget.assetInventoryModel?.sessionId ?? 0).toInt(), + ); + } + + Future getClassificationData({num? assetImportId}) async { + AssetInventoryProvider provider = Provider.of(context, listen: false); + Map payload = { + "assetImportId": assetImportId, + "sessionId": widget.assetInventoryModel?.sessionId, + "siteId": widget.assetInventoryModel?.site?.id, + "buildingId": widget.assetInventoryModel?.building?.id, + "floorId": widget.assetInventoryModel?.floor?.id, + "departmentId": widget.assetInventoryModel?.department?.id, + }; + await provider.getClassification(payload: payload).then((value) { + if (value != null) { + _scannedAssetModel?.status = value; + } + setState(() {}); + }); + } +} diff --git a/lib/modules/asset_inventory_module/pages/asset_inventory_page.dart b/lib/modules/asset_inventory_module/pages/asset_inventory_page.dart new file mode 100644 index 00000000..88e21b07 --- /dev/null +++ b/lib/modules/asset_inventory_module/pages/asset_inventory_page.dart @@ -0,0 +1,122 @@ +import 'dart:developer'; + +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/modules/asset_inventory_module/models/session_model.dart'; +import 'package:test_sa/modules/asset_inventory_module/pages/asset_inventory_detail_view.dart'; +import 'package:test_sa/modules/asset_inventory_module/pages/asset_inventory_scan_assets_view.dart'; +import 'package:test_sa/modules/asset_inventory_module/pages/asset_inventory_site_information_page.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'; +import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; +import '../provider/asset_inventory_provider.dart'; + +class AssetInventoryPage extends StatefulWidget { + final int sessionId; + + const AssetInventoryPage({Key? key, required this.sessionId}) : super(key: key); + + @override + _AssetInventoryPageState createState() { + return _AssetInventoryPageState(); + } +} + +class _AssetInventoryPageState extends State { + late AssetInventoryProvider _assetInventoryProvider; + + @override + void initState() { + super.initState(); + _assetInventoryProvider = Provider.of(context, listen: false); + WidgetsBinding.instance.addPostFrameCallback((_) { + getInitialData(); + }); + } + + Future getInitialData() async { + _assetInventoryProvider.reset(); + await _assetInventoryProvider.getSessionById(id: widget.sessionId); + await _assetInventoryProvider.getAssetsInSession( + sessionId: widget.sessionId, + ); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + appBar: DefaultAppBar( + title: 'Inventory Session Request'.addTranslation, + onBackPress: () { + Navigator.pop(context); + }, + ), + body: Consumer(builder: (context, provider, child) { + return DefaultTabController( + length: 2, + child: provider.isLoading + ? const CircularProgressIndicator(color: AppColor.primary10).center + : Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + margin: EdgeInsets.only(left: 16.toScreenWidth, right: 16.toScreenWidth, top: 12.toScreenHeight), + decoration: BoxDecoration(color: context.isDark ? AppColor.neutral50 : AppColor.white10, borderRadius: BorderRadius.circular(10)), + child: TabBar( + padding: EdgeInsets.symmetric(vertical: 4.toScreenHeight, horizontal: 4.toScreenWidth), + labelColor: context.isDark ? AppColor.neutral30 : AppColor.black20, + unselectedLabelColor: context.isDark ? AppColor.neutral30 : AppColor.black20, + unselectedLabelStyle: AppTextStyles.bodyText, + labelStyle: AppTextStyles.bodyText, + indicatorPadding: EdgeInsets.zero, + indicatorSize: TabBarIndicatorSize.tab, + dividerColor: Colors.transparent, + indicator: BoxDecoration(color: context.isDark ? AppColor.neutral60 : AppColor.neutral110, borderRadius: BorderRadius.circular(7)), + onTap: (index) {}, + tabs: [ + Tab(text: 'Request Details'.addTranslation, height: 57.toScreenHeight), + Tab( + text: + '${'Scan Assets'.addTranslation} ${provider.assetInventoryResponse?.totalRows != null && provider.assetInventoryResponse!.totalRows! > 0 ? '(${provider.assetInventoryResponse?.totalRows})' : ''}', + height: 57.toScreenHeight), + ], + ), + ), + TabBarView( + children: [ + AssetInventoryDetailView(sessionModel: provider.sessionModel ?? SessionModel()), + AssetInventoryScanAssetView(sessionId: provider.sessionModel?.id ?? 0), + ], + ).expanded, + FooterActionButton.footerContainer( + context: context, + child: AppFilledButton( + buttonColor: AppColor.primary10, + label: 'Scan Assets'.addTranslation, + onPressed: () => _scanAsset(provider: provider), + // buttonColor: AppColor.primary10, + ), + ) + ], + ), + ); + })); + } + + Future _scanAsset({required AssetInventoryProvider provider}) async { + provider.siteFilterAssetList.clear(); + Navigator.push(context, MaterialPageRoute(builder: (contxt) => SiteInformationPage(sessionModel: provider.sessionModel ?? SessionModel()))); + } +} diff --git a/lib/modules/asset_inventory_module/pages/asset_inventory_scan_assets_view.dart b/lib/modules/asset_inventory_module/pages/asset_inventory_scan_assets_view.dart new file mode 100644 index 00000000..4bf38915 --- /dev/null +++ b/lib/modules/asset_inventory_module/pages/asset_inventory_scan_assets_view.dart @@ -0,0 +1,129 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/modules/asset_inventory_module/provider/asset_inventory_provider.dart'; +import 'package:test_sa/views/widgets/loaders/lazy_loading.dart'; +import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; + +import 'asset_detail_card_view.dart'; + +class AssetInventoryScanAssetView extends StatefulWidget { + int sessionId; + + AssetInventoryScanAssetView({Key? key, required this.sessionId}) : super(key: key); + + @override + State createState() => _AssetInventoryScanAssetViewState(); +} + +class _AssetInventoryScanAssetViewState extends State { + late AssetInventoryProvider assetInventoryProvider; + + @override + void initState() { + assetInventoryProvider = Provider.of(context, listen: false); + super.initState(); + } + + Future getAssetList({bool loadMore = false}) async { + await assetInventoryProvider.getAssetsInSession( + sessionId: widget.sessionId, + loadMore: loadMore, + ); + } + + @override + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, provider, _) { + if (provider.isLoading && provider.assetInventoryResponse == null) { + return const Center(child: CircularProgressIndicator()); + } + final assets = provider.assetInventoryResponse?.assetList ?? []; + if (assets.isEmpty) { + return const Center(child: NoDataFound()); + } + return NotificationListener( + onNotification: (scrollInfo) { + if (!provider.isNextPageLoading && provider.nextPage && scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent) { + getAssetList(loadMore: true); + } + return false; + }, + child: ListView.separated( + padding: const EdgeInsets.all(16), + itemCount: assets.length + (provider.isNextPageLoading ? 1 : 0), + itemBuilder: (context, index) { + if (index == assets.length) { + return const Padding( + padding: EdgeInsets.symmetric(vertical: 16), + child: Center(child: CircularProgressIndicator()), + ); + } + return AssetDetailCardView( + assetInventoryModel: assets[index], + onDeletePress: () async { + await provider.deleteAssetISession(id: assets[index].id ?? 0).then((success) async { + if (success) { + await provider.getAssetsInSession(sessionId: widget.sessionId); + } + }); + }, + ); + }, + separatorBuilder: (context, index) => 12.height, + ), + ); + }, + ); + } + +// Widget build(BuildContext context) { +// return Consumer( +// builder: (context, provider, _) { +// if (provider.isLoading && provider.assetInventoryResponse == null) { +// //TODO need use existing loader if found.. +// return const Center(child: CircularProgressIndicator()); +// } +// final assets = provider.assetInventoryResponse?.assetList ?? []; +// if (assets.isEmpty) { +// return const Center(child: NoDataFound()); +// } +// return LazyLoading( +// nextPage: provider.nextPage, +// onLazyLoad: () async { +// log('Loading next page...'); +// await getAssetList(loadMore: true); +// }, +// child: ListView.separated( +// padding: const EdgeInsets.all(16), +// itemBuilder: (context, index) { +// if (index == assets.length) { +// // bottom loader +// return const Padding( +// padding: EdgeInsets.symmetric(vertical: 16), +// child: Center(child: CircularProgressIndicator()), +// ); +// } +// return AssetDetailCardView( +// assetInventoryModel: assets[index], +// onDeletePress: () async { +// await provider.deleteAssetISession(id: assets[index].id ?? 0).then((success) async { +// if (success) { +// await provider.getAssetsInSession(sessionId: widget.sessionId); +// } +// }); +// }, +// ); +// }, +// separatorBuilder: (context, index) => 12.height, +// itemCount: assets.length + (provider.nextPage ? 1 : 0), +// ), +// ); +// }, +// ); +// } +} diff --git a/lib/modules/asset_inventory_module/pages/asset_inventory_site_information_page.dart b/lib/modules/asset_inventory_module/pages/asset_inventory_site_information_page.dart new file mode 100644 index 00000000..11dcc31b --- /dev/null +++ b/lib/modules/asset_inventory_module/pages/asset_inventory_site_information_page.dart @@ -0,0 +1,308 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/controllers/providers/api/all_requests_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/new_models/building.dart'; +import 'package:test_sa/models/new_models/department.dart'; +import 'package:test_sa/models/new_models/floor.dart'; +import 'package:test_sa/models/new_models/room_model.dart'; +import 'package:test_sa/models/new_models/site.dart'; +import 'package:test_sa/modules/asset_inventory_module/models/asset_inventory_model.dart'; +import 'package:test_sa/modules/asset_inventory_module/models/session_model.dart'; +import 'package:test_sa/modules/asset_inventory_module/pages/asset_detail_card_view.dart'; +import 'package:test_sa/modules/asset_inventory_module/pages/asset_inventory_form_view.dart'; +import 'package:test_sa/modules/asset_inventory_module/provider/asset_inventory_provider.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'; +import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart'; +import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; +import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart'; +import 'package:test_sa/providers/loading_list_notifier.dart'; +import 'package:test_sa/views/widgets/loaders/lazy_loading.dart'; + +class SiteInformationPage extends StatefulWidget { + SessionModel sessionModel; + + SiteInformationPage({Key? key, required this.sessionModel}) : super(key: key); + + @override + State createState() => _SiteInformationPageState(); +} + +class _SiteInformationPageState extends State { + AssetInventoryModel assetInventoryModel = AssetInventoryModel(); + bool showMarkAsComplete = false; + final ScrollController _scrollController = ScrollController(); + + @override + void initState() { + super.initState(); + _scrollController.addListener(() { + final provider = Provider.of(context, listen: false); + if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent && !provider.isNextPageLoading && provider.nextPage) { + getAssetFilteredList(loadMore: true); + } + }); + } + + final GlobalKey _scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + return Scaffold( + key: _scaffoldKey, + appBar: DefaultAppBar( + title: 'Inventory Session Request'.addTranslation, + titleStyle: AppTextStyles.heading3.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + ), + body: Column( + children: [ + ListView( + controller: _scrollController, + padding: const EdgeInsets.only(left: 16, right: 16, top: 16), + children: [ + siteInfoCard(context, widget.sessionModel), + 12.height, + assetDetailList(), + 8.height, + ], + ).expanded, + FooterActionButton.footerContainer( + context: context, + child: Column( + children: [ + AppFilledButton( + buttonColor: AppColor.primary10, + label: 'Add Asset'.addTranslation, + onPressed: () => _addAsset(), + // buttonColor: AppColor.primary10, + ), + if (showMarkAsComplete) ...[ + 12.height, + AppFilledButton( + buttonColor: AppColor.green70, + label: 'Mark as completed'.addTranslation, + onPressed: () => _markAsCompleted(), + // buttonColor: AppColor.primary10, + ) + ] + ], + ), + ), + ], + ), + ); + } + + void _addAsset() async { + if (await validateRequest()) { + assetInventoryModel.sessionId = widget.sessionModel.id; + Navigator.push( + context, + MaterialPageRoute( + builder: (contxt) => AssetInventoryFormView( + assetInventoryModel: assetInventoryModel, + sessionTypeValue: widget.sessionModel.sessionTypeValue, + ))); + } + } + + void _markAsCompleted() async { + if (await validateRequest()) { + assetInventoryModel.sessionId = widget.sessionModel.id; + AssetInventoryProvider provider = Provider.of(context, listen: false); + Map payload = { + "sessionId": widget.sessionModel.id, + "siteId": assetInventoryModel.site?.id, + "buildingId": assetInventoryModel.building?.id, + "floorId": assetInventoryModel.floor?.id, + "departmentId": assetInventoryModel.department?.id, + "roomId": assetInventoryModel.room?.id, + }; + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + await provider.markAsComplete(payload: payload).then((success) { + Navigator.pop(context); + if (success) { + ///TODO need to confirm need to call this or not .. + // AllRequestsProvider allRequestsProvider = Provider.of(context, listen: false); + // allRequestsProvider.reset(); + // allRequestsProvider.getAllRequests(context, typeTransaction: 8); + ///Need to call push and remove until... + Navigator.pop(context); + Navigator.pop(context); + } + }); + } + } + + Widget assetDetailList() { + return Consumer( + builder: (context, provider, _) { + if (provider.isLoading) { + return SizedBox( + height: 300.toScreenHeight, + child: const CircularProgressIndicator(color: AppColor.primary10).center, + ); + } + + final assets = provider.siteFilterAssetList; + return ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + // Keep non-scrollable, parent handles scroll + itemCount: assets.length, + itemBuilder: (context, index) { + return AssetDetailCardView( + assetInventoryModel: assets[index], + onDeletePress: () async { + await provider.deleteAssetISession(id: assets[index].id ?? 0).then((success) async { + if (success) { + await getAssetFilteredList(); + await provider.getAssetsInSession(sessionId: widget.sessionModel.id ?? 0); + } + }); + }, + ); + }, + separatorBuilder: (context, index) => 12.height, + ); + }, + ); + } + + Widget siteInfoCard(BuildContext context, SessionModel sessionModel) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SingleItemDropDownMenu( + context: context, + title: context.translation.site, + initialValue: assetInventoryModel.site, + showShadow: false, + staticData: sessionModel.assetInventorySites, + backgroundColor: AppColor.fieldBgColor(context), + showAsBottomSheet: true, + onSelect: (value) { + assetInventoryModel.site = value; + assetInventoryModel.building = null; + assetInventoryModel.floor = null; + assetInventoryModel.department = null; + setState(() {}); + }, + ), + 12.height, + SingleItemDropDownMenu( + context: context, + title: context.translation.building, + initialValue: assetInventoryModel.building, + showShadow: false, + showAsBottomSheet: true, + backgroundColor: AppColor.fieldBgColor(context), + enabled: assetInventoryModel.site?.buildings?.isNotEmpty ?? false, + staticData: assetInventoryModel.site?.buildings ?? [], + onSelect: (value) { + assetInventoryModel.building = value; + assetInventoryModel.floor = null; + assetInventoryModel.department = null; + setState(() {}); + }, + ), + 12.height, + SingleItemDropDownMenu( + context: context, + title: context.translation.floor, + showShadow: false, + showAsBottomSheet: true, + initialValue: assetInventoryModel.floor, + backgroundColor: AppColor.fieldBgColor(context), + enabled: assetInventoryModel.building?.floors?.isNotEmpty ?? false, + staticData: assetInventoryModel.building?.floors ?? [], + onSelect: (value) { + assetInventoryModel.floor = value; + assetInventoryModel.department = null; + setState(() {}); + }, + ), + 12.height, + SingleItemDropDownMenu( + context: context, + title: context.translation.department, + showShadow: false, + showAsBottomSheet: true, + initialValue: assetInventoryModel.department, + backgroundColor: AppColor.fieldBgColor(context), + enabled: assetInventoryModel.floor?.departments?.isNotEmpty ?? false, + staticData: assetInventoryModel.floor?.departments ?? [], + onSelect: (value) { + assetInventoryModel.department = value; + assetInventoryModel.room = null; + showMarkAsComplete = true; + setState(() {}); + if (assetInventoryModel.department != null && assetInventoryModel.department!.rooms!.isEmpty) { + getAssetFilteredList(); + } + }, + ), + 12.height, + SingleItemDropDownMenu( + context: context, + title: context.translation.room, + showShadow: false, + showAsBottomSheet: true, + initialValue: assetInventoryModel.room, + backgroundColor: AppColor.fieldBgColor(context), + enabled: assetInventoryModel.department?.rooms?.isNotEmpty ?? false, + staticData: assetInventoryModel.department?.rooms ?? [], + onSelect: (value) { + assetInventoryModel.room = value; + setState(() {}); + if (assetInventoryModel.room != null) { + getAssetFilteredList(); + } + }, + ), + ], + ).toShadowContainer(context, borderRadius: 20, padding: 12); + } + + Future validateRequest() async { + if (assetInventoryModel.site == null) { + await Fluttertoast.showToast(msg: "Please Select Site"); + return false; + } + if (assetInventoryModel.building == null) { + await Fluttertoast.showToast(msg: "Please Select Building"); + return false; + } + if (assetInventoryModel.floor == null) { + await Fluttertoast.showToast(msg: "Please Select Floor"); + return false; + } + if (assetInventoryModel.department == null) { + await Fluttertoast.showToast(msg: "Please Select Department"); + return false; + } + return true; + } + + Future getAssetFilteredList({bool loadMore = false}) async { + AssetInventoryProvider provider = Provider.of(context, listen: false); + Map payload = { + "sessionId": widget.sessionModel.id, + "siteId": assetInventoryModel.site?.id, + "buildingId": assetInventoryModel.building?.id, + "floorId": assetInventoryModel.floor?.id, + "departmentId": assetInventoryModel.department?.id, + "roomId": assetInventoryModel.room?.id, + }; + await provider.getInventoryDetailsByFilter(payload: payload, loadMore: loadMore); + } +} diff --git a/lib/modules/asset_inventory_module/pages/inventory_session_item_view.dart b/lib/modules/asset_inventory_module/pages/inventory_session_item_view.dart new file mode 100644 index 00000000..8d9abfdd --- /dev/null +++ b/lib/modules/asset_inventory_module/pages/inventory_session_item_view.dart @@ -0,0 +1,124 @@ +import 'package:flutter/material.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/string_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/models/all_requests_and_count_model.dart'; +import 'package:test_sa/models/new_models/dashboard_detail.dart'; +import 'package:test_sa/modules/asset_inventory_module/pages/asset_inventory_page.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/views/widgets/requests/request_status.dart'; + +class InventorySessionItemView extends StatelessWidget { + final Data? requestData; + final RequestsDetails? requestDetails; + final bool showShadow; + + const InventorySessionItemView({Key? key, this.requestData, this.requestDetails, this.showShadow = true}) : super(key: key); + + @override + Widget build(BuildContext context) { + if (requestData != null) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + StatusLabel( + label: requestData!.priorityName!, + textColor: AppColor.getRequestStatusTextColorByName(context, requestData!.priorityName!), + backgroundColor: AppColor.getRequestStatusColorByName(context, requestData!.priorityName!), + ), + 8.width, + StatusLabel( + label: requestData!.statusName!, + textColor: AppColor.getRequestStatusTextColorByName(context, requestData!.statusName!), + backgroundColor: AppColor.getRequestStatusColorByName(context, requestData!.statusName!), + ), + 1.width.expanded, + Text( + requestData!.transactionDate?.toServiceRequestCardFormat ?? "", + textAlign: TextAlign.end, + style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50), + ), + ], + ), + 8.height, + (requestData?.typeTransaction ?? context.translation.taskRequest).heading5(context), + infoWidget(label: context.translation.requestType, value: requestData?.sessionType, context: context), + infoWidget(label: 'No of Assets'.addTranslation, value: requestData?.numberOfAssets != null ? requestData?.numberOfAssets.toString() : '-', context: context), + infoWidget(label: 'No of Sites'.addTranslation, value: requestData?.numberOfSites != null ? requestData?.numberOfSites.toString() : '-', context: context), + 8.height, + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + context.translation.viewDetails, + style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context)), + ), + 4.width, + Icon(Icons.arrow_forward, color: AppColor.blueStatus(context), size: 14) + ], + ), + ], + ).toShadowContainer(context, withShadow: showShadow).onPress(() async { + Navigator.of(context).push(MaterialPageRoute(builder: (_) => AssetInventoryPage(sessionId: requestData!.id!))); + }); + } + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + StatusLabel( + label: requestDetails!.priority!, + textColor: AppColor.getRequestStatusTextColorByName(context, requestDetails?.priority!), + backgroundColor: AppColor.getRequestStatusColorByName(context, requestDetails?.priority!), + ), + 8.width, + StatusLabel( + label: requestDetails!.status!, + textColor: AppColor.getRequestStatusTextColorByName(context, requestDetails?.status!), + backgroundColor: AppColor.getRequestStatusColorByName(context, requestDetails?.status!), + ), + 1.width.expanded, + Text( + requestDetails!.date?.toServiceRequestCardFormat ?? "", + textAlign: TextAlign.end, + style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50), + ), + ], + ), + 8.height, + (requestDetails?.nameOfType ?? context.translation.taskRequest).heading5(context), + infoWidget(label: context.translation.requestType, value: requestDetails?.sessionType, context: context), + infoWidget(label: 'No of Assets'.addTranslation, value: requestDetails?.numberOfAssets != null ? requestDetails?.numberOfAssets.toString() : '-', context: context), + infoWidget(label: 'No of Sites'.addTranslation, value: requestDetails?.numberOfSites != null ? requestDetails?.numberOfSites.toString() : '-', context: context), + 8.height, + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + context.translation.viewDetails, + style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context)), + ), + 4.width, + Icon(Icons.arrow_forward, color: AppColor.blueStatus(context), size: 14) + ], + ), + ], + ).toShadowContainer(context, withShadow: showShadow).onPress(() async { + Navigator.of(context).push(MaterialPageRoute(builder: (_) => AssetInventoryPage(sessionId: requestDetails!.id!))); + }); + } + + Widget infoWidget({required String label, String? value, required BuildContext context}) { + if (value != null && value.isNotEmpty) { + return '$label: $value'.bodyText(context); + } + return const SizedBox(); + } +} diff --git a/lib/modules/asset_inventory_module/pages/search_asset_view.dart b/lib/modules/asset_inventory_module/pages/search_asset_view.dart new file mode 100644 index 00000000..6557d3ed --- /dev/null +++ b/lib/modules/asset_inventory_module/pages/search_asset_view.dart @@ -0,0 +1,276 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/controllers/api_routes/urls.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/string_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/modules/asset_inventory_module/models/asset_inventory_model.dart'; +import 'package:test_sa/modules/asset_inventory_module/provider/asset_inventory_provider.dart'; +import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart'; +import 'package:test_sa/views/widgets/equipment/asset_item_listview.dart'; +import 'package:test_sa/views/widgets/horizontal_list_widget.dart'; +import 'package:test_sa/views/widgets/loaders/lazy_loading.dart'; +import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; +import '../../../models/device/asset.dart'; +import '../../../models/device/asset_search.dart'; +import '../../../new_views/app_style/app_color.dart'; +import '../../../new_views/common_widgets/app_lazy_loading.dart'; +import '../../../new_views/common_widgets/custom_app_bar.dart'; + +class SearchAssetView extends StatefulWidget { + static const String id = "asset_search_view"; + final num sessionId; + + const SearchAssetView({Key? key, required this.sessionId}) : super(key: key); + + @override + State createState() => _SearchAssetViewState(); +} + +class _SearchAssetViewState extends State { + int _selectedIndex = 0; + DeviceSearch? search; + late TextEditingController _searchController; + late AssetInventoryProvider _assetInventoryProvider; + final List _searchableList = []; + final GlobalKey _formKey = GlobalKey(); + bool _isFirst = true; + + @override + void initState() { + _searchController = TextEditingController(); + super.initState(); + } + + @override + void dispose() { + _searchController.dispose(); + _assetInventoryProvider.searchReset(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + List searchBy = [ + context.translation.assetName, + context.translation.model, + context.translation.manufacture, + context.translation.snNumber, + context.translation.siteName, + // 'Building Name'.addTranslation, + // 'Floor Name'.addTranslation, + 'Department Name'.addTranslation, + + ///need to check if want to add more filters + ]; + + _assetInventoryProvider = Provider.of(context, listen: false); + + return Scaffold( + resizeToAvoidBottomInset: false, + appBar: CustomAppBar( + title: context.translation.searchAsset, + ), + body: Column( + children: [ + HorizontalListWidget( + list: searchBy, + callBackFunction: (index) { + setState(() { + _selectedIndex = index; + }); + }, + ).paddingOnly(top: 16, bottom: 0), + Form( + key: _formKey, + child: AppTextFormField( + controller: _searchController, + textInputAction: TextInputAction.search, + labelText: "${context.translation.searchBy} ${searchBy[_selectedIndex]}", + onAction: _search, + onChange: (text) { + _searchController.text = text; + _searchController.selection = TextSelection.fromPosition(TextPosition(offset: _searchController.text.length)); + setState(() {}); + }, + onSaved: (value) { + setState(() { + search = DeviceSearch(); + }); + _setValue(value); + }, + suffixIcon: IconButton( + icon: const Icon(Icons.search), + splashColor: Colors.transparent, + onPressed: _searchController.text.isNotEmpty ? _search : null, + color: AppColor.neutral20, + ).paddingOnly(end: 0), + ).paddingOnly(top: 16, start: 16, end: 16, bottom: 8), + ), + Expanded( + child: _searchableList.isEmpty + ? _isFirst + ? const SizedBox() + : NoDataFound(message: context.translation.noDeviceFound) + : LazyLoading( + nextPage: _assetInventoryProvider.nextPage, + onLazyLoad: () async { + if (_searchController.text.isNotEmpty) { + await _assetInventoryProvider.getAssets(search: search, isSearchBy: true, sessionId: widget.sessionId); + setState(() { + _searchableList.clear(); + _searchableList.addAll(_assetInventoryProvider.searchDevices); + }); + } + }, + child: Column( + children: [ + ListView.separated( + itemCount: _searchableList.length, + separatorBuilder: (listContext, itemIndex) => 8.height, + padding: const EdgeInsets.all(16), + itemBuilder: (listContext, itemIndex) { + // bool isSelected = selectedAssets.contains(_searchableList[itemIndex].id!); + // String title = isSelected ? "UnSelect" : "Select"; + return InventoryAssetItemListView( + device: _searchableList[itemIndex], + // isSelected: isSelected, + onPressed: (device) { + // Navigator.of(context).pop(); + Navigator.of(context).pop(device); + }, + // selectButton: Text(title, style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context))), + ); + }, + ).expanded, + ], + ), + ), + ) + ], + )); + } + + void _search() async { + FocusScope.of(context).unfocus(); + _formKey.currentState!.save(); + _assetInventoryProvider.searchReset(); + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + await _assetInventoryProvider.getAssets(search: search, isSearchBy: true, sessionId: widget.sessionId); + setState(() { + _searchableList.clear(); + _searchableList.addAll(_assetInventoryProvider.searchDevices); + _isFirst = false; + }); + Navigator.pop(context); + } + + _setValue(value) { + /// todo : check oracle code (no matched parameter) + /// //ontext.translation.assetName, + // context.translation.model, + // context.translation.manufacture, + // context.translation.snNumber, + // context.translation.siteName, + // // 'Building Name'.addTranslation, + // // 'Floor Name'.addTranslation, + // 'Department Name'.addTranslation, + switch (_selectedIndex) { + case 0: + search!.assetName = value; + break; + case 1: + search!.model = value; + break; + case 2: + search!.manufacturer = value; + break; + case 3: + search!.assetSerialNumber = value; + break; + case 4: + search!.site = value; + break; + case 5: + search!.department = value; + break; + default: + break; + } + } + +// bool _showResetButton() { +// return (_searchController?.text?.isNotEmpty ?? false); +// } +} + +class InventoryAssetItemListView extends StatelessWidget { + final AssetInventoryModel device; + final Function(AssetInventoryModel) onPressed; + final Widget? selectButton; + final bool isSelected; + + const InventoryAssetItemListView({Key? key, required this.device, required this.onPressed, this.selectButton, this.isSelected = false}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 95, + height: 95, + decoration: ShapeDecoration( + color: context.isDark ? AppColor.neutral50 : AppColor.neutral30, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + image: DecorationImage( + fit: BoxFit.cover, + image: NetworkImage(device.photo != null ? URLs.getFileUrl(device.photo!)! : "https://www.lasteelcraft.com/images/no-image-available.png"), + )), + ), + 15.width, + Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + device.assetName!.cleanupWhitespace.capitalizeFirstOfEach.heading6(context), + 8.height, + "${context.translation.assetNumber} : ${device.assetNumber}".bodyText(context), + "${context.translation.modelName} : ${device.model}".cleanupWhitespace.capitalizeFirstOfEach.bodyText(context), + ], + ), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + "${context.translation.serialNo} : ${device.serialNo}".bodyText(context).expanded, + 4.width, + selectButton ?? + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + context.translation.viewDetails, + style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context)), + ), + 4.width, + Icon( + Icons.arrow_forward, + color: AppColor.blueStatus(context), + size: 14, + ) + ], + ), + ], + ) + ], + ).expanded + ], + ).toShadowContainer(context, padding: 12, borderColor: isSelected ? AppColor.blueStatus(context) : Colors.transparent).onPress(() => onPressed(device)); + } +} diff --git a/lib/modules/asset_inventory_module/provider/asset_inventory_provider.dart b/lib/modules/asset_inventory_module/provider/asset_inventory_provider.dart new file mode 100644 index 00000000..c712c26c --- /dev/null +++ b/lib/modules/asset_inventory_module/provider/asset_inventory_provider.dart @@ -0,0 +1,466 @@ +import 'dart:convert'; +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:http/http.dart'; +import 'package:test_sa/controllers/api_routes/api_manager.dart'; +import 'package:test_sa/controllers/api_routes/urls.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/string_extensions.dart'; +import 'package:test_sa/models/device/asset.dart'; +import 'package:test_sa/models/device/asset_search.dart'; +import 'package:test_sa/models/lookup.dart'; +import 'package:test_sa/models/service_request/pending_service_request_model.dart'; +import 'package:test_sa/models/service_request/service_report.dart'; +import 'package:test_sa/models/service_request/service_request.dart'; +import 'package:test_sa/models/service_request/service_request_search.dart'; +import 'package:test_sa/models/service_request/spare_parts.dart'; +import 'package:test_sa/models/service_request/supp_engineer_work_orders.dart'; +import 'package:test_sa/models/service_request/supplier_engineer_model.dart'; +import 'package:test_sa/models/timer_model.dart'; +import 'package:test_sa/modules/asset_inventory_module/models/asset_inventory_model.dart'; +import 'package:test_sa/modules/asset_inventory_module/models/session_model.dart'; + +import '../../../models/service_request/search_work_order.dart'; +import '../../../models/service_request/wo_call_request.dart'; +import '../../../models/user.dart'; +import '../../../new_views/common_widgets/app_lazy_loading.dart'; + +class AssetInventoryProvider extends ChangeNotifier { + final pageItemNumber = 10; + final searchPageItemNumber = 10; + int pageNo = 1; + List _devices = []; + List _searchDevices = []; + + List get devices => _devices; + + List get searchDevices => _searchDevices; + SessionModel? sessionModel; + + //TODO need to check i think don't need to create this obj + AssetInventoryResponse? assetInventoryResponse; + AssetInventoryResponse? siteFilterAssetResponse; + + List sessionAssetList = []; + List siteFilterAssetList = []; + + void reset() { + pageNo = 1; + assetInventoryResponse = null; + sessionModel = null; + sessionAssetList = []; + stateCode = null; + } + + int? stateCode; + bool isDetailLoading = false; + bool nextPage = false; + bool isNextPageLoading = false; + bool isLoading = false; + bool isAllAssetLoading = false; + + void searchReset() { + stateCode = null; + _searchDevices = []; + } + + Future getSessionById({required int id}) async { + try { + sessionModel = SessionModel(); + isLoading = true; + notifyListeners(); + final response = await ApiManager.instance.get(URLs.getAssetInventoryById + "?assetInventoryId=$id"); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + sessionModel = SessionModel.fromJson(json.decode(response.body)["data"]); + } else { + sessionModel = null; + } + + isLoading = false; + notifyListeners(); + } catch (e) { + log("getSessionError [error] : $e"); + isLoading = false; + sessionModel = null; + notifyListeners(); + return null; + } + } + + Future getAssetsInSession({ + required int sessionId, + bool loadMore = false, + }) async { + if (isLoading || isNextPageLoading) return -2; + if (loadMore) { + isNextPageLoading = true; + pageNo += 1; + } else { + isLoading = true; + pageNo = 1; // reset pagination + } + notifyListeners(); + try { + final payload = { + "pageSize": pageItemNumber, + "pageNumber": pageNo, + "sessionId": sessionId, + }; + final response = await ApiManager.instance.post(URLs.getAssetsInSession, body: payload); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + final Map jsonData = json.decode(response.body); + final newResponse = AssetInventoryResponse.fromJson(jsonData); + + if (loadMore) { + assetInventoryResponse?.assetList?.addAll(newResponse.assetList ?? []); + assetInventoryResponse?.totalRows = newResponse.totalRows; + } else { + assetInventoryResponse = newResponse; + } + nextPage = (assetInventoryResponse?.assetList?.length ?? 0) < (assetInventoryResponse?.totalRows ?? 0); + } else { + nextPage = false; + } + isLoading = false; + isNextPageLoading = false; + notifyListeners(); + return response.statusCode; + } catch (error) { + isLoading = false; + isNextPageLoading = false; + stateCode = -1; + nextPage = false; + notifyListeners(); + return -1; + } + } + + ///older code + // Future getAssetsInSession({ + // required int sessionId, + // bool loadMore = false, + // }) async { + // // if (isLoading || nextPage) return -2; + // + // if (loadMore) { + // nextPage = true; + // pageNo += 1; + // } else { + // isLoading = true; + // pageNo = 1; // reset pagination + // } + // + // notifyListeners(); + // log('Fetching page: $pageNo'); + // + // try { + // final payload = { + // "pageSize": pageItemNumber, + // "pageNumber": pageNo, + // "sessionId": sessionId, + // }; + // + // final response = await ApiManager.instance.post(URLs.getAssetsInSession, body: payload); + // stateCode = response.statusCode; + // + // if (response.statusCode >= 200 && response.statusCode < 300) { + // final Map jsonData = json.decode(response.body); + // final newResponse = AssetInventoryResponse.fromJson(jsonData); + // + // if (loadMore) { + // assetInventoryResponse?.assetList?.addAll(newResponse.assetList ?? []); + // assetInventoryResponse?.totalRows = newResponse.totalRows; + // } else { + // assetInventoryResponse = newResponse; + // } + // + // nextPage = (assetInventoryResponse?.assetList?.length ?? 0) < (assetInventoryResponse?.totalRows ?? 0); + // } + // + // isLoading = false; + // nextPage = false; + // notifyListeners(); + // return response.statusCode; + // } catch (error) { + // isLoading = false; + // nextPage = false; + // stateCode = -1; + // notifyListeners(); + // return -1; + // } + // } + + Future getInventoryDetailsByFilter({ + required Map payload, + bool loadMore = false, + }) async { + if (isLoading || isNextPageLoading) return -2; + + if (loadMore) { + isNextPageLoading = true; + pageNo += 1; + } else { + isLoading = true; + pageNo = 1; + } + + notifyListeners(); + log('Fetching inventory page: $pageNo'); + + try { + final paginatedPayload = { + ...payload, + "pageSize": pageItemNumber, + "pageNumber": pageNo, + }; + + final response = await ApiManager.instance.post(URLs.getInventoryDetailsByFilter, body: paginatedPayload); + stateCode = response.statusCode; + + if (response.statusCode >= 200 && response.statusCode < 300) { + final Map jsonData = json.decode(response.body); + final newResponse = AssetInventoryResponse.fromJson(jsonData); + + if (loadMore) { + siteFilterAssetList.addAll(newResponse.assetList ?? []); + siteFilterAssetResponse?.totalRows = newResponse.totalRows; + } else { + siteFilterAssetResponse = newResponse; + siteFilterAssetList = newResponse.assetList ?? []; + } + nextPage = (siteFilterAssetList.length) < (siteFilterAssetResponse?.totalRows ?? 0); + } else { + siteFilterAssetList = []; + nextPage = false; + } + + isLoading = false; + isNextPageLoading = false; + notifyListeners(); + return response.statusCode; + } catch (error) { + isLoading = false; + isNextPageLoading = false; + nextPage = false; + stateCode = -1; + notifyListeners(); + return -1; + } + } + + Future searchAsset({ + required Map payload, + }) async { + isLoading = true; + try { + final response = await ApiManager.instance.post(URLs.searchAsset, body: payload); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + final Map jsonData = json.decode(response.body)["data"]; + AssetInventoryModel model = AssetInventoryModel.fromJson(jsonData); + log('model json ${model.toJson()}'); + isLoading = false; + notifyListeners(); + return model; + } else { + isLoading = false; + notifyListeners(); + return null; + } + } catch (error) { + isLoading = false; + stateCode = -1; + notifyListeners(); + return null; + } + } + + Future saveAssetInSession({ + AssetInventoryModel? model, + }) async { + isLoading = true; + try { + final response = await ApiManager.instance.post(URLs.saveAssetInSession, body: model?.toJson() ?? {}); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + isLoading = false; + notifyListeners(); + return true; + } else { + isLoading = false; + notifyListeners(); + return false; + } + } catch (error) { + isLoading = false; + notifyListeners(); + return false; + } + } + + Future getClassification({ + required Map payload, + }) async { + isLoading = true; + String? classification; + try { + final response = await ApiManager.instance.post(URLs.getClassification, body: payload); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + log('classifcation on response ${json.decode(response.body)["data"]['classification']}'); + classification = json.decode(response.body)["data"]['classification']; + isLoading = false; + notifyListeners(); + return classification; + } else { + isLoading = false; + notifyListeners(); + return classification; + } + } catch (error) { + isLoading = false; + notifyListeners(); + return classification; + } + } + + Future deleteAssetISession({required num id}) async { + try { + isLoading = true; + notifyListeners(); + final response = await ApiManager.instance.get(URLs.deleteAssetInSession + "?id=$id"); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + isLoading = false; + notifyListeners(); + return true; + } + isLoading = false; + notifyListeners(); + return false; + } catch (e) { + log("Delete Asset [error] : $e"); + isLoading = false; + notifyListeners(); + return false; + } + } + + Future> getAutoCompleteDetails({ + bool? isManufacturer, + String? query, + int type = 0, + }) async { + try { + String url = ''; + switch (type) { + //for model and manufacturer.. + case 0: + url = URLs.getManufacturerOrModelAutoComplete; + break; + //for asset name.. + case 1: + url = URLs.searchAssetName; + break; + //for supplier... + case 2: + url = URLs.getSuppliersAutoCompleteInventory; + break; + default: + url = URLs.getManufacturerOrModelAutoComplete; + } + final queryParams = {}; + if (isManufacturer != null && type == 0) { + queryParams['isManufacturer'] = isManufacturer.toString(); + } + if (query != null && query.isNotEmpty) { + queryParams['searchText'] = query; + } + if (queryParams.isNotEmpty) { + url += '?${Uri(queryParameters: queryParams).query}'; + } + final response = await ApiManager.instance.get(url); + + List list = []; + if (response.statusCode >= 200 && response.statusCode < 300) { + final data = json.decode(response.body)["data"]; + list = (data as List).map((e) => Lookup.fromJson(e)).toList(); + } + return list; + } catch (error) { + log('error $error'); + return []; + } + } + + Future getAssets({DeviceSearch? search, bool isQr = false, bool isSearchBy = false, num? sessionId}) async { + if (isLoading == true) return -2; + isLoading = true; + notifyListeners(); + late Response response; + try { + final Map body = { + "pageSize": isSearchBy ? searchPageItemNumber : pageItemNumber, + "pageNumber": isSearchBy ? (searchDevices.length / searchPageItemNumber).ceil() + 1 : devices.length ~/ pageItemNumber + 1, + "sessionId": sessionId ?? 0, + "assetSerialNo": search?.assetSerialNumber ?? '', + "assetName": search?.assetName ?? '', + "model": search?.model ?? '', + "manufacturer": search?.manufacturer ?? '', + "supplier": search?.supplier ?? '', + "siteName": search?.site ?? '', + // "buildingName":search?.??'', + // "floorName": search?.f??'', + "departmentName": search?.department ?? '', + // "roomName": search?.room + }; + // if (search != null) body.addAll(search.toJson()); + response = await ApiManager.instance.post(URLs.getAssetsTemp, body: body); + } catch (error) { + isLoading = false; + stateCode = -1; + notifyListeners(); + return -1; + } + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + // client's request was successfully received + List equipmentListJson = json.decode(response.body)["data"]; + var dList = equipmentListJson.map((asset) => AssetInventoryModel.fromJson(asset)).toList() ?? []; + isSearchBy ? _searchDevices.addAll(dList) : _devices.addAll(dList); + nextPage = true; + } else { + nextPage = false; + } + isLoading = false; + notifyListeners(); + return response.statusCode; + } + + Future markAsComplete({ + required Map payload, + }) async { + isLoading = true; + try { + final response = await ApiManager.instance.post(URLs.convertDetailToComplete, body: payload); + stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + isLoading = false; + notifyListeners(); + return true; + } else { + isLoading = false; + notifyListeners(); + return false; + } + } catch (error) { + isLoading = false; + notifyListeners(); + return false; + } + } +} diff --git a/lib/modules/cm_module/views/components/service_request_detail_view.dart b/lib/modules/cm_module/views/components/service_request_detail_view.dart index 8b8da1cd..440715e9 100644 --- a/lib/modules/cm_module/views/components/service_request_detail_view.dart +++ b/lib/modules/cm_module/views/components/service_request_detail_view.dart @@ -138,20 +138,20 @@ class _ServiceRequestDetailViewState extends State { if (Provider.of(context, listen: false).showPriority) ...[ StatusLabel( label: workOrder.priority?.name, - id: workOrder.priority!.id!, + id: workOrder.priority!.value!, radius: 4, - textColor: AppColor.getPriorityStatusTextColor(context, workOrder.priority!.id!), - backgroundColor: AppColor.getPriorityStatusColor(context, workOrder.priority!.id!), + textColor: AppColor.getPriorityStatusTextColor(context, workOrder.priority!.value!), + backgroundColor: AppColor.getPriorityStatusColor(context, workOrder.priority!.value!), ), 8.width, ], if (workOrder.itgFormWorkOrderStatus != null) ...[ StatusLabel( label: workOrder.itgFormWorkOrderStatus, - id: 0, + // id: 0, radius: 4, textColor: AppColor.getPriorityStatusTextColor(context, 82), - backgroundColor: AppColor.getPriorityStatusColor(context, 0), + backgroundColor: AppColor.getPriorityStatusColor(context, 1000), ), 8.width, ], diff --git a/lib/modules/pm_module/ppm_wo/ppm_details_page.dart b/lib/modules/pm_module/ppm_wo/ppm_details_page.dart index 0ac5b3ae..85b1d569 100644 --- a/lib/modules/pm_module/ppm_wo/ppm_details_page.dart +++ b/lib/modules/pm_module/ppm_wo/ppm_details_page.dart @@ -120,16 +120,16 @@ class _PpmDetailsPageState extends State { context: context, ) ] else ...[ - FooterActionButton.footerContainer( - context: context, - child: AppFilledButton( - onPressed: () async { - await Navigator.of(context).push(MaterialPageRoute(builder: (_) => UpdatePpm(ppm: null, planPreventiveVisit: planPreventiveVisit))); - getVisitData(); - }, - label: context.translation.viewDetails, - ), - ) + // FooterActionButton.footerContainer( + // context: context, + // child: AppFilledButton( + // onPressed: () async { + // await Navigator.of(context).push(MaterialPageRoute(builder: (_) => UpdatePpm(ppm: null, planPreventiveVisit: planPreventiveVisit))); + // getVisitData(); + // }, + // label: context.translation.viewDetails, + // ), + // ) ] ]) : const Center(child: NoDataFound()); diff --git a/lib/modules/pm_module/ppm_wo/update_ppm/ppm_external_details_form.dart b/lib/modules/pm_module/ppm_wo/update_ppm/ppm_external_details_form.dart index 8b1be526..ad48a65f 100644 --- a/lib/modules/pm_module/ppm_wo/update_ppm/ppm_external_details_form.dart +++ b/lib/modules/pm_module/ppm_wo/update_ppm/ppm_external_details_form.dart @@ -58,7 +58,7 @@ class _PpmExternalDetailsFormState extends State { final ppmProvider = Provider.of(context, listen: false); return ListView.builder( itemCount: widget.models!.length + 1, - padding: const EdgeInsets.all(16), + padding: const EdgeInsets.only(left: 16, right: 16, top: 8, bottom: 16), itemBuilder: (context, index) { if (index == widget.models!.length) { return Visibility( @@ -190,7 +190,7 @@ class _ExternalDetailItemState extends State { ), child: Icon(Icons.add, color: context.isDark ? null : AppColor.neutral60), ).onPress(() async { - if(widget.model.supplier==null) { + if (widget.model.supplier == null) { "Please select supplier".showToast; return; } diff --git a/lib/modules/pm_module/ppm_wo/update_ppm/update_ppm.dart b/lib/modules/pm_module/ppm_wo/update_ppm/update_ppm.dart index e4bd68ba..4441a434 100644 --- a/lib/modules/pm_module/ppm_wo/update_ppm/update_ppm.dart +++ b/lib/modules/pm_module/ppm_wo/update_ppm/update_ppm.dart @@ -108,7 +108,7 @@ class _UpdatePpmState extends State with TickerProviderStateMixin { if (typeOfService == null) { return; } - if (typeOfService?.id == 66) { + if (typeOfService?.value == 2) { ppmProvider.totalTabs = 4; } else { ppmProvider.totalTabs = 3; diff --git a/lib/modules/pm_module/ppm_wo/update_ppm/wo_info_form.dart b/lib/modules/pm_module/ppm_wo/update_ppm/wo_info_form.dart index 3cef0f21..489ea245 100644 --- a/lib/modules/pm_module/ppm_wo/update_ppm/wo_info_form.dart +++ b/lib/modules/pm_module/ppm_wo/update_ppm/wo_info_form.dart @@ -299,7 +299,7 @@ class _WoInfoFormState extends State { onSelect: (value) { if (value != null) { widget.planPreventiveVisit.typeOfService = value; - if (widget.planPreventiveVisit.typeOfService?.id == 66) { + if (widget.planPreventiveVisit.typeOfService?.value == 2) { ppmProvider.totalTabs = 4; } else { ppmProvider.totalTabs = 3; diff --git a/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart b/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart index 5e9ff9d6..612992e7 100644 --- a/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart +++ b/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart @@ -1,3 +1,4 @@ +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/controllers/api_routes/urls.dart'; @@ -8,12 +9,15 @@ import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/models/all_requests_and_count_model.dart'; import 'package:test_sa/models/new_models/task_request/task_request_model.dart'; +import 'package:test_sa/modules/cm_module/utilities/service_request_utils.dart'; import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; +import 'package:test_sa/new_views/common_widgets/working_time_tile.dart'; import 'package:test_sa/providers/task_request_provider/task_request_provider.dart'; import 'package:test_sa/views/widgets/images/files_list.dart'; import 'package:test_sa/views/widgets/loaders/app_loading.dart'; import 'package:test_sa/views/widgets/requests/request_status.dart'; +import 'package:test_sa/views/widgets/total_working_time_detail_bottomsheet.dart'; import '../../../../controllers/providers/api/user_provider.dart'; import '../../../../models/enums/user_types.dart'; @@ -61,7 +65,6 @@ class _TaskRequestDetailsViewState extends State { Widget build(BuildContext context) { userProvider ??= Provider.of(context, listen: false); return Scaffold( - // appBar: DefaultAppBar(title:taskProvider?.taskRequestModel?.taskType?.id==7?'Recall and Alert Details': context.translation.taskRequest), appBar: DefaultAppBar(title: context.translation.taskRequest), body: Consumer(builder: (context, taskProvider, child) { TaskData? taskModel = taskProvider.taskRequestModel; @@ -122,6 +125,7 @@ class _TaskRequestDetailsViewState extends State { ], ), ], + showWorkingTime(taskModel?.taskJobActivityEngineerTimers ?? []), if ((taskProvider.taskRequestModel?.taskJobContactPersons ?? []).isNotEmpty) ...[contactInfoCard(context, taskProvider.taskRequestModel!)], if (taskProvider.taskRequestModel?.callComment != null) ...[ const Divider().defaultStyle(context), @@ -167,6 +171,55 @@ class _TaskRequestDetailsViewState extends State { ); } + Widget showWorkingTime(List timer) { + if (context.userProvider.user!.type == UsersTypes.normal_user) return const SizedBox(); + + double totalWorkingHours = timer.fold(0.0, (sum, item) { + if (item.startDate == null || item.endDate == null) return sum; + try { + final start = DateTime.parse(item.startDate!); + final end = DateTime.parse(item.endDate!); + + final diffInHours = end.difference(start).inSeconds / 3600.0; // convert to hours + + return sum + diffInHours; + } catch (_) { + return sum; + } + }); + String totalHourString = (totalWorkingHours > 0.0) ? ServiceRequestUtils.formatTotalWorkingHours(totalWorkingHours) : totalWorkingHours.toString(); + + return RichText( + text: TextSpan( + text: "${context.translation.totalWorkingTime}: ", + style: TextStyle( + fontSize: 14.toScreenWidth, fontWeight: FontWeight.w500, fontStyle: FontStyle.normal, decoration: TextDecoration.none, color: context.isDark ? AppColor.neutral10 : AppColor.neutral20), + children: [ + TextSpan(text: "$totalHourString ", style: AppTextStyles.bodyText), + const WidgetSpan( + child: Icon(Icons.info_outline_rounded, color: AppColor.primary10, size: 14), + ), + ], + ), + ).onPress(() => showModalBottomSheet( + context: context, + isDismissible: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + clipBehavior: Clip.antiAliasWithSaveLayer, + builder: (BuildContext context) => TotalWorkingTimeDetailBottomSheet( + timerList: timer.map((e) { + return TimerHistoryModel( + id: e.id, + startTime: e.startDate, + endTime: e.endDate, + workingHours: e.totalWorkingHour, + ); + }).toList()), + )); + } + Widget contactInfoCard(BuildContext context, TaskData? taskModel) { return Column( mainAxisSize: MainAxisSize.min, @@ -270,6 +323,16 @@ class _TaskRequestDetailsViewState extends State { '${context.translation.assetNo}: ${taskModel.asset?.assetNumber ?? "-"}'.bodyText(context), '${context.translation.manufacture}: ${taskModel.asset?.manufacturer ?? "-"}'.bodyText(context), '${context.translation.model}: ${taskModel.asset?.model ?? "-"}'.bodyText(context), + '${context.translation.serialNo}: ${taskModel.asset?.serialNo ?? "-"}'.bodyText(context), + '${context.translation.assetType}: ${taskModel.asset?.assetTypeName ?? "-"}'.bodyText(context), + '${context.translation.site}: ${taskModel.asset?.siteName ?? "-"}'.bodyText(context), + '${context.translation.building}: ${taskModel.asset?.buildingName ?? "-"}'.bodyText(context), + '${context.translation.floor}: ${taskModel.asset?.floorName ?? "-"}'.bodyText(context), + '${context.translation.department}: ${taskModel.asset?.departmentName ?? "-"}'.bodyText(context), + '${context.translation.room}: ${taskModel.asset?.roomName ?? "-"}'.bodyText(context), + '${context.translation.supplier}: ${taskModel.asset?.supplierName ?? "-"}'.bodyText(context), + 'PO Number: ${taskModel.asset?.poNumber ?? "-"}'.bodyText(context), + '${context.translation.installationDate}: ${taskModel.asset?.installationDate?.toAssetDetailsFormat ?? "-"}'.bodyText(context), ], ) : const SizedBox(); diff --git a/lib/modules/traf_module/asset_analytics_model.dart b/lib/modules/traf_module/asset_analytics_model.dart new file mode 100644 index 00000000..2f265c1b --- /dev/null +++ b/lib/modules/traf_module/asset_analytics_model.dart @@ -0,0 +1,172 @@ +class AssetAnalytics { + num? assetId; + String? assetNumber; + String? assetName; + num? lastPrice; + String? lastPONo; + String? lastPODate; + num? sameAssetQuantity; + String? slaStatus; + String? contractNumber; + String? contractStartDate; + String? contractEndDate; + num? assetAgeInDays; + String? assetAgeDescription; + num? totalMaintenanceCost; + num? sparePartCost; + num? laborCost; + num? exchangeCost; + num? travelCost; + num? netBookValue; + num? purchasedPrice; + num? lifeSpan; + num? mtbf; + String? mtbfDescription; + num? totalCMWorkOrders; + num? downTimeHours; + String? downTimeDescription; + num? upTimeHours; + String? upTimeDescription; + num? operationHours; + String? installationDate; + String? modelName; + String? manufacturerName; + String? serialNumber; + String? departmentName; + String? siteName; + num? id; + String? createdBy; + String? createdDate; + String? modifiedBy; + String? modifiedDate; + + AssetAnalytics( + {this.assetId, + this.assetNumber, + this.assetName, + this.lastPrice, + this.lastPONo, + this.lastPODate, + this.sameAssetQuantity, + this.slaStatus, + this.contractNumber, + this.contractStartDate, + this.contractEndDate, + this.assetAgeInDays, + this.assetAgeDescription, + this.totalMaintenanceCost, + this.sparePartCost, + this.laborCost, + this.exchangeCost, + this.travelCost, + this.netBookValue, + this.purchasedPrice, + this.lifeSpan, + this.mtbf, + this.mtbfDescription, + this.totalCMWorkOrders, + this.downTimeHours, + this.downTimeDescription, + this.upTimeHours, + this.upTimeDescription, + this.operationHours, + this.installationDate, + this.modelName, + this.manufacturerName, + this.serialNumber, + this.departmentName, + this.siteName, + this.id, + this.createdBy, + this.createdDate, + this.modifiedBy, + this.modifiedDate}); + + AssetAnalytics.fromJson(Map json) { + assetId = json['assetId']; + assetNumber = json['assetNumber']; + assetName = json['assetName']; + lastPrice = json['lastPrice']; + lastPONo = json['lastPONo']; + lastPODate = json['lastPODate']; + sameAssetQuantity = json['sameAssetQuantity']; + slaStatus = json['slaStatus']; + contractNumber = json['contractNumber']; + contractStartDate = json['contractStartDate']; + contractEndDate = json['contractEndDate']; + assetAgeInDays = json['assetAgeInDays']; + assetAgeDescription = json['assetAgeDescription']; + totalMaintenanceCost = json['totalMaintenanceCost']; + sparePartCost = json['sparePartCost']; + laborCost = json['laborCost']; + exchangeCost = json['exchangeCost']; + travelCost = json['travelCost']; + netBookValue = json['netBookValue']; + purchasedPrice = json['purchasedPrice']; + lifeSpan = json['lifeSpan']; + mtbf = json['mtbf']; + mtbfDescription = json['mtbfDescription']; + totalCMWorkOrders = json['totalCMWorkOrders']; + downTimeHours = json['downTimeHours']; + downTimeDescription = json['downTimeDescription']; + upTimeHours = json['upTimeHours']; + upTimeDescription = json['upTimeDescription']; + operationHours = json['operationHours']; + installationDate = json['installationDate']; + modelName = json['modelName']; + manufacturerName = json['manufacturerName']; + serialNumber = json['serialNumber']; + departmentName = json['departmentName']; + siteName = json['siteName']; + id = json['id']; + createdBy = json['createdBy']; + createdDate = json['createdDate']; + modifiedBy = json['modifiedBy']; + modifiedDate = json['modifiedDate']; + } + + Map toJson() { + final Map data = new Map(); + data['assetId'] = this.assetId; + data['assetNumber'] = this.assetNumber; + data['assetName'] = this.assetName; + data['lastPrice'] = this.lastPrice; + data['lastPONo'] = this.lastPONo; + data['lastPODate'] = this.lastPODate; + data['sameAssetQuantity'] = this.sameAssetQuantity; + data['slaStatus'] = this.slaStatus; + data['contractNumber'] = this.contractNumber; + data['contractStartDate'] = this.contractStartDate; + data['contractEndDate'] = this.contractEndDate; + data['assetAgeInDays'] = this.assetAgeInDays; + data['assetAgeDescription'] = this.assetAgeDescription; + data['totalMaintenanceCost'] = this.totalMaintenanceCost; + data['sparePartCost'] = this.sparePartCost; + data['laborCost'] = this.laborCost; + data['exchangeCost'] = this.exchangeCost; + data['travelCost'] = this.travelCost; + data['netBookValue'] = this.netBookValue; + data['purchasedPrice'] = this.purchasedPrice; + data['lifeSpan'] = this.lifeSpan; + data['mtbf'] = this.mtbf; + data['mtbfDescription'] = this.mtbfDescription; + data['totalCMWorkOrders'] = this.totalCMWorkOrders; + data['downTimeHours'] = this.downTimeHours; + data['downTimeDescription'] = this.downTimeDescription; + data['upTimeHours'] = this.upTimeHours; + data['upTimeDescription'] = this.upTimeDescription; + data['operationHours'] = this.operationHours; + data['installationDate'] = this.installationDate; + data['modelName'] = this.modelName; + data['manufacturerName'] = this.manufacturerName; + data['serialNumber'] = this.serialNumber; + data['departmentName'] = this.departmentName; + data['siteName'] = this.siteName; + data['id'] = this.id; + data['createdBy'] = this.createdBy; + data['createdDate'] = this.createdDate; + data['modifiedBy'] = this.modifiedBy; + data['modifiedDate'] = this.modifiedDate; + return data; + } +} diff --git a/lib/modules/traf_module/asset_auto_complete_field.dart b/lib/modules/traf_module/asset_auto_complete_field.dart index 0abb378b..e215c4a7 100644 --- a/lib/modules/traf_module/asset_auto_complete_field.dart +++ b/lib/modules/traf_module/asset_auto_complete_field.dart @@ -105,7 +105,7 @@ class _AssetAutoCompleteFieldState extends State { 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, + labelText: "Name/Oracle Code", labelStyle: AppTextStyles.tinyFont.copyWith(color: AppColor.textColor(context)), suffixIcon: loading ? const CircularProgressIndicator(color: AppColor.primary10, strokeWidth: 3.0).paddingOnly(end: 8) : null, ), diff --git a/lib/modules/traf_module/create_traf_request_page.dart b/lib/modules/traf_module/create_traf_request_page.dart index e2d066a9..66133805 100644 --- a/lib/modules/traf_module/create_traf_request_page.dart +++ b/lib/modules/traf_module/create_traf_request_page.dart @@ -165,6 +165,10 @@ class _CreateTRAFRequestPageState extends State { AppTextFormField( initialValue: "", labelText: "How would the requested technology solve the current situation and/or serve the purpose?", + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, backgroundColor: AppColor.fieldBgColor(context), labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), floatingLabelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), @@ -178,6 +182,10 @@ class _CreateTRAFRequestPageState extends State { AppTextFormField( initialValue: "", labelText: "What is the current practice?", + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, backgroundColor: AppColor.fieldBgColor(context), labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), showShadow: false, @@ -191,12 +199,16 @@ class _CreateTRAFRequestPageState extends State { initialValue: "", makeMultiLinesNull: true, labelText: "Census Q1", + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, backgroundColor: AppColor.fieldBgColor(context), labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), showShadow: false, - textInputType: TextInputType.number, + textInputType: TextInputType.text, onChange: (value) { - trafRequest?.censusQ1 = int.tryParse(value); + trafRequest?.censusQ1 = value; }, ), 12.height, @@ -204,12 +216,16 @@ class _CreateTRAFRequestPageState extends State { initialValue: "", makeMultiLinesNull: true, labelText: "Census Q2", + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, backgroundColor: AppColor.fieldBgColor(context), labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), showShadow: false, - textInputType: TextInputType.number, + textInputType: TextInputType.text, onChange: (value) { - trafRequest?.censusQ2 = int.tryParse(value); + trafRequest?.censusQ2 = value; }, ), 12.height, @@ -217,12 +233,16 @@ class _CreateTRAFRequestPageState extends State { initialValue: "", makeMultiLinesNull: true, labelText: "Census Q3", + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, backgroundColor: AppColor.fieldBgColor(context), labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), showShadow: false, - textInputType: TextInputType.number, + textInputType: TextInputType.text, onChange: (value) { - trafRequest?.censusQ3 = int.tryParse(value); + trafRequest?.censusQ3 = value; }, ), 12.height, @@ -230,12 +250,16 @@ class _CreateTRAFRequestPageState extends State { initialValue: "", makeMultiLinesNull: true, labelText: "Census Q4", + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, backgroundColor: AppColor.fieldBgColor(context), labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), showShadow: false, - textInputType: TextInputType.number, + textInputType: TextInputType.text, onChange: (value) { - trafRequest?.censusQ4 = int.tryParse(value); + trafRequest?.censusQ4 = value; }, ), 12.height, @@ -264,7 +288,8 @@ class _CreateTRAFRequestPageState extends State { dense: true, contentPadding: EdgeInsets.zero, title: Text("Name: ${userBasedOnSearch[index].userName ?? ""}"), - subtitle: Text("Email: ${userBasedOnSearch[index].email ?? ""}\nPhone: ${userBasedOnSearch[index].phoneNumber ?? ""}"), + subtitle: Text( + "Position: ${userBasedOnSearch[index].position ?? "-"}\nEmail: ${userBasedOnSearch[index].email ?? ""}\nPhone: ${userBasedOnSearch[index].phoneNumber ?? ""}"), trailing: const Icon(Icons.delete_rounded, color: Color(0xffF63939), size: 20).onPress(() { userBasedOnSearch.removeAt(index); setState(() {}); @@ -286,7 +311,7 @@ class _CreateTRAFRequestPageState extends State { initialValue: isUsedSolelyOrShared, onSelect: (value) { isUsedSolelyOrShared = value; - trafRequest?.usingSolelyOrSharedId = value?.value; + trafRequest?.usingSolelyOrSharedId = value?.id; if (isUsedSolelyOrShared?.value != 1) { departments = []; trafRequest?.trafDepartments = []; @@ -329,7 +354,7 @@ class _CreateTRAFRequestPageState extends State { initialValue: otherServicesEffects, onSelect: (value) { otherServicesEffects = value; - trafRequest?.isEffectedId = value?.value; + trafRequest?.isEffectedId = value?.id; if (otherServicesEffects?.value != 1) { trafRequest?.effectedServices = null; FocusScope.of(context).unfocus(); @@ -347,6 +372,10 @@ class _CreateTRAFRequestPageState extends State { textInputType: TextInputType.multiline, alignLabelWithHint: true, node: otherServicesEffectsNode, + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, labelText: "List down these services and stat how would it be effected", backgroundColor: AppColor.fieldBgColor(context), labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), @@ -367,7 +396,7 @@ class _CreateTRAFRequestPageState extends State { initialValue: useInCombination, onSelect: (value) { useInCombination = value; - trafRequest?.isCombinationId = value?.value; + trafRequest?.isCombinationId = value?.id; if (useInCombination?.value != 1) { trafRequest?.usedWithCombination = null; FocusScope.of(context).unfocus(); @@ -384,6 +413,10 @@ class _CreateTRAFRequestPageState extends State { textInputType: TextInputType.multiline, alignLabelWithHint: true, node: useInCombinationNode, + validator: (value) { + if ((value ?? "").isEmpty) return "Mandatory"; + return null; + }, labelText: "kindly describe in detail", backgroundColor: AppColor.fieldBgColor(context), labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), @@ -430,14 +463,51 @@ class _CreateTRAFRequestPageState extends State { } void _verifyAndSubmit() async { + if (requestType == null) { + "Select request type".showToast; + return; + } else if (requestType?.value == 1 && trafRequest?.assetNDId == null) { + "Oracle Code missing".showToast; + return; + } else if (requestType?.value == 2 && trafRequest?.assetId == null) { + "Pick asset".showToast; + return; + } + if (!_formKey.currentState!.validate()) return; + trafRequest?.employeeId = context.userProvider.user?.userID; trafRequest?.siteId = context.userProvider.user?.clientId; trafRequest?.trafContacts = []; trafRequest?.trafContacts = userBasedOnSearch.map((item) => TrafContacts(id: 0, trafId: 0, name: item.userName, userId: item.userId)).toList(); + if ((trafRequest?.trafContacts ?? []).isEmpty) { + "Please specify users going to use technology".showToast; + return; + } + if (isUsedSolelyOrShared == null) { + "Please specify technology solely or shared".showToast; + return; + } + if (isUsedSolelyOrShared?.value == 1 && (trafRequest?.trafDepartments ?? []).isEmpty) { + "Please specify departments and relations".showToast; + return; + } + + if (otherServicesEffects == null) { + "Please specify other services will be effected or not".showToast; + return; + } + + if (useInCombination == null) { + "Please specify equipment used in combination or not".showToast; + return; + } + Utils.showLoading(context); - await Provider.of(context, listen: false).addTraf(trafRequest!.toJson()); + bool isSuccess = await Provider.of(context, listen: false).addTraf(trafRequest!.toJson()); Utils.hideLoading(context); - Navigator.pop(context); + if (isSuccess) { + Navigator.pop(context); + } } } diff --git a/lib/modules/traf_module/traf_request_detail_page.dart b/lib/modules/traf_module/traf_request_detail_page.dart index 7283bf98..00f25512 100644 --- a/lib/modules/traf_module/traf_request_detail_page.dart +++ b/lib/modules/traf_module/traf_request_detail_page.dart @@ -13,7 +13,9 @@ 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 'package:test_sa/views/widgets/loaders/no_data_found.dart'; +import 'asset_analytics_model.dart'; import 'traf_request_model.dart'; class TrafRequestDetailPage extends StatefulWidget { @@ -51,30 +53,32 @@ class _TrafRequestDetailPageState extends State { 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!), - 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); - // }), - // ), - ], - ); + return trafProvider.trafRequestDataModel == null + ? const NoDataFound().center + : Column( + children: [ + ListView( + padding: const EdgeInsets.all(16), + children: [ + requesterInformation(trafProvider.trafRequestDataModel!), + 12.height, + requestInformation(trafProvider.trafRequestDataModel!), + 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); + // }), + // ), + ], + ); }, )); } @@ -90,13 +94,14 @@ class _TrafRequestDetailPageState extends State { ), 8.height, '${context.translation.employeeId}: ${data.employeeIdForDisplay ?? '-'}'.bodyText(context), - '${context.translation.name}: ${data.employeeName ?? '-'}'.bodyText(context), // todo ask shaheer - '${context.translation.email}: ${data.employeeEmail ?? '-'}'.bodyText(context), // todo ask shaheer - 'Position: ${data.positionName ?? '-'}'.bodyText(context), // todo ask shaheer + '${context.translation.name}: ${data.employeeName ?? '-'}'.bodyText(context), + '${context.translation.email}: ${data.employeeEmail ?? '-'}'.bodyText(context), + 'Position: ${data.positionName ?? '-'}'.bodyText(context), '${context.translation.site}: ${data.siteName ?? '-'}'.bodyText(context), - '${context.translation.department}: ${data.departments?.map((item) => item.departmentName).toList() ?? '-'}'.bodyText(context), - 'Requester Extension: ${data.requesterExtensionName ?? '-'}'.bodyText(context), - '${context.translation.extensionNo}: ${data.requesterExtensionNumber ?? '-'}'.bodyText(context), + '${context.translation.department}: ${data.departmentName}'.bodyText(context), + // 'Requester Extension: ${data.requesterExtensionName ?? '-'}'.bodyText(context), + 'Number/Contact Information: ${data.requesterContactNumber ?? '-'}'.bodyText(context), + 'Extension: ${data.requesterExtensionNumber ?? '-'}'.bodyText(context), ], ).toShadowContainer(context, borderRadius: 20, padding: 12); } @@ -127,10 +132,22 @@ class _TrafRequestDetailPageState extends State { .bodyText(context), 4.height, 'Is the requesting department going to use the technology solely or shared with other departments?:\n${data.usingSolelyOrSharedName ?? '-'}'.bodyText(context), + if ((data.departments ?? []).isNotEmpty) ...[ + 4.height, + 'Technology used with other departments:\n${data.departments?.map((item) => item.departmentName).toList() ?? '-'}'.bodyText(context), + ], 4.height, - 'Would other services be effected by acquiring the new equipment?:\n${data.effectedServices ?? '-'}'.bodyText(context), + 'Would other services be effected by acquiring the new equipment?:\n${data.isEffectedName ?? '-'}'.bodyText(context), + if ((data.effectedServices ?? "").isNotEmpty) ...[ + 4.height, + 'List of services would it be effected:\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), + 'Is the equipment going to be used with combination of other equipment to accomplish a specific procedure?:\n${data.isCombinationName ?? '-'}'.bodyText(context), + if ((data.usedWithCombination ?? "").isNotEmpty) ...[ + 4.height, + 'Equipment going to be used with combination of other equipments:\n${data.usedWithCombination ?? '-'}'.bodyText(context), + ], ], ).toShadowContainer(context, borderRadius: 20, padding: 12); } @@ -149,16 +166,29 @@ class _TrafRequestDetailPageState extends State { '${context.translation.model}: ${data.modelName ?? '-'}'.bodyText(context), '${context.translation.manufacture}: ${data.manufacturerName ?? '-'}'.bodyText(context), if (data.requestTypeId == 733) ...[ - '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 + FutureBuilder( + future: Provider.of(context, listen: false).getAssetAnalytics(data.assetId!), + builder: (cxt, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const SizedBox.square(dimension: 24, child: CircularProgressIndicator()).center; + } + if (snapshot.hasData) { + return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + 'Last price: ${snapshot.data?.lastPrice ?? '-'}'.bodyText(context), + 'PO: ${snapshot.data?.lastPONo ?? '-'}'.bodyText(context), + 'The quantity of the same asset: ${snapshot.data?.sameAssetQuantity ?? '-'}'.bodyText(context), // todo check with shaheer + 'Existing asset under SLA: ${snapshot.data?.slaStatus ?? '-'}'.bodyText(context), // todo check with shaheer + 'Age of the asset: ${snapshot.data?.assetAgeDescription ?? '-'}'.bodyText(context), // todo check with shaheer + 'Total Maintenance Cost (TMC): ${snapshot.data?.totalMaintenanceCost ?? '-'}'.bodyText(context), // todo check with shaheer + 'Net Book Value (NBV): ${snapshot.data?.netBookValue ?? '-'}'.bodyText(context), // todo check with shaheer + 'Mean Time Between Failure (MTBF): ${snapshot.data?.mtbfDescription ?? '-'}'.bodyText(context), // todo check with shaheer + 'Down Time (DT): ${snapshot.data?.downTimeDescription ?? '-'}'.bodyText(context), // todo check with shaheer + 'Up Time (UT): ${snapshot.data?.upTimeDescription ?? '-'}'.bodyText(context), + 'Purchased Price: ${snapshot.data?.purchasedPrice ?? '-'}'.bodyText(context), + ]); + } + return const SizedBox(); + }) ], ], ).toShadowContainer(context, borderRadius: 20, padding: 12); diff --git a/lib/modules/traf_module/traf_request_model.dart b/lib/modules/traf_module/traf_request_model.dart index b5de3280..af3977be 100644 --- a/lib/modules/traf_module/traf_request_model.dart +++ b/lib/modules/traf_module/traf_request_model.dart @@ -51,12 +51,15 @@ class TrafRequestDataModel { String? employeeEmail; int? positionId; String? positionName; + String? departmentId; + String? departmentName; List? departments; List? trafDepartments; int? siteId; String? siteName; String? requesterExtensionNumber; String? requesterExtensionName; + String? requesterContactNumber; int? requesterExtensionPositionId; String? requesterExtensionPositionName; int? requestTypeId; @@ -74,10 +77,10 @@ class TrafRequestDataModel { int? qty; String? purposeAnswer; String? currentPractise; - int? censusQ1; - int? censusQ2; - int? censusQ3; - int? censusQ4; + String? censusQ1; + String? censusQ2; + String? censusQ3; + String? censusQ4; List? trafContacts; int? usingSolelyOrSharedId; String? usingSolelyOrSharedName; @@ -124,7 +127,7 @@ class TrafRequestDataModel { String? approvalDocDate; String? isAssigned; String? poNumber; - int? apiDirectorId; + String? apiDirectorId; String? apiDirectorName; int? apiDirectorApprovalId; String? apiDirectorApprovalName; @@ -146,12 +149,15 @@ class TrafRequestDataModel { this.employeeEmail, this.positionId, this.positionName, + this.departmentId, + this.departmentName, this.departments, this.trafDepartments, this.siteId, this.siteName, this.requesterExtensionNumber, this.requesterExtensionName, + this.requesterContactNumber, this.requesterExtensionPositionId, this.requesterExtensionPositionName, this.requestTypeId, @@ -241,6 +247,8 @@ class TrafRequestDataModel { employeeEmail = json['employeeEmail']; positionId = json['positionId']; positionName = json['positionName']; + departmentId = json['departmentId']; + departmentName = json['departmentName']; if (json['departments'] != null) { departments = []; json['departments'].forEach((v) { @@ -257,6 +265,7 @@ class TrafRequestDataModel { siteName = json['siteName']; requesterExtensionNumber = json['requesterExtensionNumber']; requesterExtensionName = json['requesterExtensionName']; + requesterContactNumber = json['requesterContactNumber']; requesterExtensionPositionId = json['requesterExtensionPositionId']; requesterExtensionPositionName = json['requesterExtensionPositionName']; requestTypeId = json['requestTypeId']; @@ -363,6 +372,8 @@ class TrafRequestDataModel { data['employeeEmail'] = this.employeeEmail; data['positionId'] = this.positionId; data['positionName'] = this.positionName; + data['departmentId'] = this.departmentId; + data['departmentName'] = this.departmentName; if (this.departments != null) { data['departments'] = this.departments!.map((v) => v.toJson()).toList(); } @@ -373,6 +384,7 @@ class TrafRequestDataModel { data['siteName'] = this.siteName; data['requesterExtensionNumber'] = this.requesterExtensionNumber; data['requesterExtensionName'] = this.requesterExtensionName; + data['requesterContactNumber'] = this.requesterContactNumber; data['requesterExtensionPositionId'] = this.requesterExtensionPositionId; data['requesterExtensionPositionName'] = this.requesterExtensionPositionName; data['requestTypeId'] = this.requestTypeId; diff --git a/lib/modules/traf_module/traf_request_provider.dart b/lib/modules/traf_module/traf_request_provider.dart index 1f1dd385..346f7351 100644 --- a/lib/modules/traf_module/traf_request_provider.dart +++ b/lib/modules/traf_module/traf_request_provider.dart @@ -5,6 +5,7 @@ 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 'asset_analytics_model.dart'; import 'traf_request_model.dart'; class TrafRequestProvider extends ChangeNotifier { @@ -12,25 +13,21 @@ class TrafRequestProvider extends ChangeNotifier { TrafRequestDataModel? trafRequestDataModel; - Future addTraf(Map body) async { + Future addTraf(Map body) async { try { - isLoading = true; - notifyListeners(); Response response = await ApiManager.instance.post(URLs.addTRAF, body: body); if (response.statusCode >= 200 && response.statusCode < 300) { // trafRequestDataModel = TrafRequestDataModel.fromJson(json.decode(response.body)["data"]); + return true; } - isLoading = false; - notifyListeners(); - return 0; + return false; } catch (error) { - isLoading = false; - notifyListeners(); - return -1; + return false; } } Future getTRAFById(int trafId) async { + trafRequestDataModel = null; try { isLoading = true; notifyListeners(); @@ -47,4 +44,16 @@ class TrafRequestProvider extends ChangeNotifier { return -1; } } + + Future getAssetAnalytics(int assetId) async { + try { + Response response = await ApiManager.instance.get("${URLs.getAssetAnalytics}?assetId=$assetId"); + if (response.statusCode >= 200 && response.statusCode < 300) { + return AssetAnalytics.fromJson(json.decode(response.body)["data"]); + } + } catch (error) { + print(error); + } + return null; + } } diff --git a/lib/modules/traf_module/users_auto_complete_field.dart b/lib/modules/traf_module/users_auto_complete_field.dart index 12060844..c5d04db7 100644 --- a/lib/modules/traf_module/users_auto_complete_field.dart +++ b/lib/modules/traf_module/users_auto_complete_field.dart @@ -81,13 +81,13 @@ class _UsersAutoCompleteFieldState extends State { loading = true; }); } - List workOrders = (await _TechnologyUsersProvider.getUsersBasedOnSearch(textEditingValue.text)); + List workOrders = (await _TechnologyUsersProvider.getUsersBasedOnSearch(context.userProvider.user!.clientId!, textEditingValue.text)); setState(() { loading = false; }); return workOrders; }, - displayStringForOption: (UsersBasedOnSearchModel option) => widget.byName ? option.userName ?? "" : option.employeeId ?? "", + displayStringForOption: (UsersBasedOnSearchModel option) => widget.byName ? "${option.userName ?? "-"} (Nurse)" ?? "" : option.employeeId ?? "", fieldViewBuilder: (BuildContext context, TextEditingController fieldTextEditingController, FocusNode fieldFocusNode, VoidCallback onFieldSubmitted) { return TextField( controller: _controller, diff --git a/lib/new_views/app_style/app_color.dart b/lib/new_views/app_style/app_color.dart index 7b7df7d4..f8c42ce9 100644 --- a/lib/new_views/app_style/app_color.dart +++ b/lib/new_views/app_style/app_color.dart @@ -264,6 +264,11 @@ class AppColor { return green15; case 370: return green15; + case 0: + return green15; + case 1: + //high priority + return white10; case 82: //high priority return white10; @@ -279,9 +284,13 @@ class AppColor { return greenStatus(context); case 370: return greenStatus(context); + case 0: + return greenStatus(context); //high priority case 82: return redStatus(context); + case 1: + return redStatus(context); default: return blueStatus(context); } diff --git a/lib/new_views/common_widgets/app_filled_button.dart b/lib/new_views/common_widgets/app_filled_button.dart index 8e1e67f2..f543615a 100644 --- a/lib/new_views/common_widgets/app_filled_button.dart +++ b/lib/new_views/common_widgets/app_filled_button.dart @@ -50,8 +50,8 @@ class AppFilledButton extends StatelessWidget { ), child: loading ? SizedBox( - width: 24, - height: 24, + width: 24.toScreenHeight, + height: 24.toScreenHeight, child: CircularProgressIndicator( color: textColor ?? AppColor.background(context), strokeWidth: 2, 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 index d0df92fa..9adf0b97 100644 --- a/lib/new_views/common_widgets/multiple_item_drop_down_menu.dart +++ b/lib/new_views/common_widgets/multiple_item_drop_down_menu.dart @@ -210,7 +210,7 @@ class _MultipleItemDropDownMenuState { types["System Internal Audit".addTranslation] = 11; } - if (context.settingProvider.isUserFlowMedical) { + if (context.settingProvider.isUserFlowMedical && isEngineer) { types['Recall and Alert'] = 7; } @@ -125,6 +126,7 @@ class _AllRequestsFilterPageState extends State { body: Column( children: [ SingleChildScrollView( + padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ @@ -208,11 +210,11 @@ class _AllRequestsFilterPageState extends State { // setState(() {}); // }), ], - ).paddingOnly(start: 16, end: 16), + ), ).expanded, - AppFilledButton(onPressed: _search, label: context.translation.applyFilter).paddingOnly(start: 16, end: 16), + FooterActionButton.footerContainer(context: context, child: AppFilledButton(onPressed: _search, label: context.translation.applyFilter)) ], - ).paddingOnly(top: 16, bottom: 16), + ), ); } diff --git a/lib/new_views/pages/land_page/my_request/all_requests_search_page.dart b/lib/new_views/pages/land_page/my_request/all_requests_search_page.dart index 35b5210b..bde4ba6f 100644 --- a/lib/new_views/pages/land_page/my_request/all_requests_search_page.dart +++ b/lib/new_views/pages/land_page/my_request/all_requests_search_page.dart @@ -7,6 +7,7 @@ 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/common_widgets/app_filled_button.dart'; import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart'; @@ -90,6 +91,7 @@ class _AllRequestsSearchPageState extends State { child: Column( children: [ SingleChildScrollView( + padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -124,9 +126,9 @@ class _AllRequestsSearchPageState extends State { ], ), ).expanded, - AppFilledButton(onPressed: _search, label: context.translation.search), + FooterActionButton.footerContainer(context: context, child: AppFilledButton(onPressed: _search, label: context.translation.search)), ], - ).paddingAll(16), + ), ), ); } 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 2f7bf6d0..641c0243 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 @@ -5,10 +5,12 @@ import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/controllers/providers/api/all_requests_provider.dart'; +import 'package:test_sa/controllers/providers/api/device_transfer_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/string_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'; @@ -51,9 +53,12 @@ class _MyRequestsPageState extends State { //TODO unCommit this to enable task requestsList.add(Request(6, context.translation.taskRequest)); //after confirm name add this to translation... - if (context.settingProvider.isUserFlowMedical) { + if (context.settingProvider.isUserFlowMedical && Provider.of(context, listen: false).user!.type != UsersTypes.normal_user) { requestsList.add(Request(7, 'Recall and Alert')); } + if (Provider.of(context, listen: false).user!.type != UsersTypes.normal_user) { + requestsList.add(Request(8, 'Inventory Session'.addTranslation)); + } if (context.userProvider.user!.type == UsersTypes.normal_user) { requestsList.add(Request(9, 'TRAF')); } diff --git a/lib/new_views/pages/land_page/requests/request_paginated_listview.dart b/lib/new_views/pages/land_page/requests/request_paginated_listview.dart index 97e9bf58..750abfd7 100644 --- a/lib/new_views/pages/land_page/requests/request_paginated_listview.dart +++ b/lib/new_views/pages/land_page/requests/request_paginated_listview.dart @@ -3,6 +3,7 @@ import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/models/new_models/dashboard_detail.dart'; import 'package:test_sa/modules/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_item_view.dart'; import 'package:test_sa/modules/internal_audit_module/pages/system_internal_audit/system_internal_audit_item_view.dart'; +import 'package:test_sa/modules/asset_inventory_module/pages/inventory_session_item_view.dart'; import 'package:test_sa/modules/tm_module/tasks_wo/task_request_item_view.dart'; import 'package:test_sa/modules/traf_module/traf_request_item_view.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; @@ -48,54 +49,35 @@ class RequestPaginatedListview extends StatelessWidget { } Widget _buildRequestItem(Data request) { - //TODO need to replace with switch statement - bool isServiceRequest = request.transactionNo == 1; - bool isGasRefill = request.transactionNo == 2; - bool isAssetTransfer = request.transactionNo == 3; - bool isPPMs = request.transactionNo == 4; - bool isRecurrentTask = request.transactionNo == 5; - bool isTask = request.transactionNo == 6; - bool isTRAF = request.transactionNo == 9; - bool isEquipmentInternalAudit = request.transactionNo == 10; - bool isSystemInternalAudit = request.transactionNo == 11; - - if (isServiceRequest) { - return ServiceRequestItemView(requestData: request, refreshData: false); - } else if (isGasRefill) { - return GasRefillItemView(requestData: request); - } else if (isPPMs) { - return PpmItemView(requestData: request); - } else if (isAssetTransfer) { - return DeviceItemView(requestData: request); - } else if (isRecurrentTask) { - return RecurrentWoItemView(requestData: request); - } else if (isTask) { - return TaskRequestItemView(requestData: request); - } else if (isTRAF) { - return TrafRequestItemView(requestData: request); - } else if (isEquipmentInternalAudit) { - return EquipmentInternalAuditItemView(requestData: request); - } else if (isSystemInternalAudit) { - return SystemInternalAuditItemView(requestData: request); - } else { - return Container( - height: 100, - width: double.infinity, - color: Colors.grey, - ); + switch (request.transactionNo) { + case 1: + return ServiceRequestItemView(requestData: request); + case 2: + return GasRefillItemView(requestData: request); + case 3: + return DeviceItemView(requestData: request); + case 4: + return PpmItemView(requestData: request); + case 5: + return RecurrentWoItemView(requestData: request); + case 6: + return TaskRequestItemView(requestData: request); + case 7: + return TaskRequestItemView(requestData: request); + case 8: + return InventorySessionItemView(requestData: request); + case 9: + return TrafRequestItemView(requestData: request); + case 10: + return EquipmentInternalAuditItemView(requestData: request); + case 11: + return SystemInternalAuditItemView(requestData: request); + default: + return Container( + height: 100, + width: double.infinity, + color: Colors.grey, + ); } } } - -// return Column( -// mainAxisSize: MainAxisSize.min, -// children: List.generate(isLoading ? 6 : list.length, (index) { -// // if (isLoading) { -// // return const SizedBox().toRequestShimmer(context, isLoading); -// // } -// // else { -// return Padding( -// padding: EdgeInsets.symmetric(vertical: 10.toScreenHeight), -// child: _buildRequestItem(list[index])); -// // } -// })); diff --git a/lib/new_views/pages/land_page/widgets/request_item_view_list.dart b/lib/new_views/pages/land_page/widgets/request_item_view_list.dart index 54639c40..b8d1f6eb 100644 --- a/lib/new_views/pages/land_page/widgets/request_item_view_list.dart +++ b/lib/new_views/pages/land_page/widgets/request_item_view_list.dart @@ -8,6 +8,7 @@ import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/models/all_requests_and_count_model.dart'; import 'package:test_sa/modules/internal_audit_module/pages/equipment_internal_audit/equipment_internal_audit_item_view.dart'; import 'package:test_sa/modules/internal_audit_module/pages/system_internal_audit/system_internal_audit_item_view.dart'; +import 'package:test_sa/modules/asset_inventory_module/pages/inventory_session_item_view.dart'; import 'package:test_sa/modules/tm_module/tasks_wo/task_request_item_view.dart'; import 'package:test_sa/modules/traf_module/traf_request_item_view.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; @@ -58,6 +59,8 @@ class RequestItemViewList extends StatelessWidget { return TaskRequestItemView(requestDetails: list[index]); case 7: return TaskRequestItemView(requestDetails: list[index]); + case 8: + return InventorySessionItemView(requestDetails: list[index]); case 9: return TrafRequestItemView(requestDetails: list[index]); //Need to verify the type No.. diff --git a/lib/new_views/pages/login_page.dart b/lib/new_views/pages/login_page.dart index 35eb9134..8c54e927 100644 --- a/lib/new_views/pages/login_page.dart +++ b/lib/new_views/pages/login_page.dart @@ -110,8 +110,9 @@ class _LoginPageState extends State { backgroundColor: AppColor.fieldBgColor(context), validator: (value) => Validator.hasValue(value!) ? null : context.translation.requiredField, labelText: context.translation.username, - style: TextStyle(fontWeight: FontWeight.w500, fontSize: 12, color: context.isDark ? Colors.white : const Color(0xff3B3D4A)), - labelStyle: TextStyle(fontWeight: FontWeight.w500, fontSize: 11, color: context.isDark ? Colors.white : const Color(0xff767676)), + style: TextStyle(fontWeight: FontWeight.w500, fontSize: context.isTablet() ? 16 : 12, color: context.isDark ? Colors.white : const Color(0xff3B3D4A)), + floatingLabelStyle: TextStyle(fontWeight: FontWeight.w500, fontSize: context.isTablet() ? 16 : 12, color: context.isDark ? Colors.white : const Color(0xff3B3D4A)), + labelStyle: TextStyle(fontWeight: FontWeight.w500, fontSize: context.isTablet() ? 16 : 12, color: context.isDark ? Colors.white : const Color(0xff767676)), textInputType: TextInputType.text, showWithoutDecoration: true, contentPadding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth, vertical: 12.toScreenHeight), @@ -125,8 +126,9 @@ class _LoginPageState extends State { showWithoutDecoration: true, labelText: context.translation.password, backgroundColor: AppColor.fieldBgColor(context), - style: TextStyle(fontWeight: FontWeight.w500, fontSize: 12, color: context.isDark ? Colors.white : const Color(0xff3B3D4A)), - labelStyle: TextStyle(fontWeight: FontWeight.w500, fontSize: 11, color: context.isDark ? Colors.white : const Color(0xff767676)), + style: TextStyle(fontWeight: FontWeight.w500, fontSize: context.isTablet() ? 16 : 12, color: context.isDark ? Colors.white : const Color(0xff3B3D4A)), + labelStyle: TextStyle(fontWeight: FontWeight.w500, fontSize: context.isTablet() ? 16 : 12, color: context.isDark ? Colors.white : const Color(0xff767676)), + floatingLabelStyle: TextStyle(fontWeight: FontWeight.w500, fontSize: context.isTablet() ? 16 : 12, color: context.isDark ? Colors.white : const Color(0xff3B3D4A)), contentPadding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth, vertical: 12.toScreenHeight), obscureText: !_passwordVisible, suffixIcon: Icon( diff --git a/lib/views/pages/user/profile_page.dart b/lib/views/pages/user/profile_page.dart index 22082ca1..9af6bb61 100644 --- a/lib/views/pages/user/profile_page.dart +++ b/lib/views/pages/user/profile_page.dart @@ -175,7 +175,7 @@ class _ProfilePageState extends State { 16.height, AppFilledButton( label: "Update Information", - buttonColor: context.isDark? AppColor.primary10:AppColor.neutral50, + buttonColor: context.isDark ? AppColor.primary10 : AppColor.neutral50, onPressed: () { context.showBottomSheet( UpdateUserContactInfoBottomSheet( diff --git a/lib/views/widgets/equipment/asset_picker.dart b/lib/views/widgets/equipment/asset_picker.dart index a9d8e5f0..b5f029d5 100644 --- a/lib/views/widgets/equipment/asset_picker.dart +++ b/lib/views/widgets/equipment/asset_picker.dart @@ -17,11 +17,16 @@ class AssetPicker extends StatelessWidget { final List deviceList; final bool editable; final bool showAssetInfo; + final bool showBorder; final Color? borderColor; final Color? buttonColor; + final Color? iconColor; + final Color? labelColor; + final String? label; Color? backgroundColor; final bool forPPM; final bool showLoading; + final bool enablePickManually; final bool multiSelection; AssetPicker( @@ -34,10 +39,14 @@ class AssetPicker extends StatelessWidget { this.onAssetRemove, this.borderColor, this.buttonColor, + this.label, + this.iconColor, + this.labelColor, this.backgroundColor, this.showAssetInfo = true, + this.showBorder = false, this.multiSelection = false, - this.forPPM = false, + this.forPPM = false, this.enablePickManually = true, this.showLoading = false}) : assert( multiSelection == false || onMultiAssetPick != null, @@ -57,15 +66,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, // 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 : Colors.white), + "scan_asset".toSvgAsset(height: 22, fit: BoxFit.fitHeight, color: context.isDark ? AppColor.neutral20 :iconColor?? Colors.white), 8.width, - "Scan or Pick Asset".bodyText(context).custom(color: context.isDark ? AppColor.neutral20 : Colors.white), + ' ${label??"Scan or Pick Asset"}'.bodyText(context).custom(color: context.isDark ? AppColor.neutral20 : labelColor?? Colors.white), ], ), ).onPress(() async { @@ -74,6 +84,7 @@ class AssetPicker extends StatelessWidget { builder: (context) => AssetScanQr( title: context.translation.assetScan, multiSelection: multiSelection, + enablePickManually: enablePickManually, ))) as List?; if (device?.isNotEmpty ?? false) { onMultiAssetPick!(device!); @@ -83,6 +94,7 @@ class AssetPicker extends StatelessWidget { builder: (context) => AssetScanQr( title: context.translation.assetScan, multiSelection: multiSelection, + enablePickManually: enablePickManually, ))) as Asset?; if (device != null) { onPick!(device); @@ -112,6 +124,7 @@ class AssetPicker extends StatelessWidget { builder: (context) => AssetScanQr( title: context.translation.assetScan, multiSelection: multiSelection, + enablePickManually: enablePickManually, ))) as List?; if (device?.isNotEmpty ?? false) { onMultiAssetPick!(device!); @@ -121,6 +134,7 @@ class AssetPicker extends StatelessWidget { builder: (context) => AssetScanQr( title: context.translation.assetScan, multiSelection: multiSelection, + enablePickManually: enablePickManually, ))) as Asset?; if (device != null) { onPick!(device); @@ -131,7 +145,7 @@ class AssetPicker extends StatelessWidget { if (deviceList.isNotEmpty && showAssetInfo) ListView.separated( shrinkWrap: true, - padding: EdgeInsets.only(top: 16), + padding: const EdgeInsets.only(top: 16), physics: const NeverScrollableScrollPhysics(), itemBuilder: (cxt, index) => _assetInfoView(deviceList[index], context), separatorBuilder: (cxt, index) => 12.height, @@ -195,10 +209,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), diff --git a/lib/views/widgets/qr/asset_scan_qr.dart b/lib/views/widgets/qr/asset_scan_qr.dart index 16b76dbd..c39483a6 100644 --- a/lib/views/widgets/qr/asset_scan_qr.dart +++ b/lib/views/widgets/qr/asset_scan_qr.dart @@ -20,9 +20,10 @@ import '../../pages/device_transfer/search_device_page.dart'; class AssetScanQr extends StatefulWidget { static const String id = "/asset-scan-qr"; - const AssetScanQr({Key? key, required this.title, this.multiSelection = false}) : super(key: key); + const AssetScanQr({Key? key, required this.title, this.multiSelection = false,this.enablePickManually = true}) : super(key: key); final String title; final bool multiSelection; + final bool enablePickManually; @override _AssetScanQrState createState() => _AssetScanQrState(); @@ -86,15 +87,23 @@ class _AssetScanQrState extends State { controller.scannedDataStream.listen((scanData) async { if (!_scanDone) { _scanDone = true; - final result = await _getDevice(scanData.code!, isQr: true); - if (result.isNotEmpty) { - if (widget.multiSelection) { - Navigator.of(context).pop([result[0]]); + if(!widget.enablePickManually){ + if(scanData.code!=null){ + Navigator.of(context).pop(Asset(assetNumber: scanData.code!)); + }else{ + _scanDone = false; + } + }else{ + final result = await _getDevice(scanData.code!, isQr: true); + if (result.isNotEmpty) { + if (widget.multiSelection) { + Navigator.of(context).pop([result[0]]); + } else { + Navigator.of(context).pop(result[0]); + } } else { - Navigator.of(context).pop(result[0]); + _scanDone = false; } - } else { - _scanDone = false; } } }); @@ -109,7 +118,8 @@ class _AssetScanQrState extends State { ), ], ).expanded, - FooterActionButton.footerContainer( + if(widget.enablePickManually) + FooterActionButton.footerContainer( context: context, child: AppFilledButton( label: context.translation.pickManually, diff --git a/lib/views/widgets/requests/request_status.dart b/lib/views/widgets/requests/request_status.dart index b304eb51..272ddf42 100644 --- a/lib/views/widgets/requests/request_status.dart +++ b/lib/views/widgets/requests/request_status.dart @@ -11,23 +11,23 @@ class StatusLabel extends StatelessWidget { final double radius; final bool labelInCenter; - StatusLabel({Key? key, this.id = 0, this.label, this.backgroundColor, this.textColor, this.isPriority = false, this.radius = 4,this.labelInCenter=false}) : super(key: key); + StatusLabel({Key? key, this.id = 0, this.label, this.backgroundColor, this.textColor, this.isPriority = false, this.radius = 4, this.labelInCenter = false}) : super(key: key); @override Widget build(BuildContext context) { - if (id == 82) label = context.translation.highPriority; - if (id == 81) label = context.translation.lowPriority; if (label?.toLowerCase() == 'In progress'.toLowerCase()) label = context.translation.inProgress; if (label?.toLowerCase() == 'Completed'.toLowerCase()) label = context.translation.completed; if (label?.toLowerCase() == 'Open'.toLowerCase()) label = context.translation.open; if (label?.toLowerCase() == 'Closed'.toLowerCase()) label = context.translation.closed; if (label?.toLowerCase() == 'Low'.toLowerCase()) label = context.translation.lowPriority; + if (label?.toLowerCase() == 'No'.toLowerCase()) label = context.translation.lowPriority; if (label?.toLowerCase() == 'High'.toLowerCase()) label = context.translation.highPriority; + if (label?.toLowerCase() == 'Yes'.toLowerCase()) label = context.translation.highPriority; if (label?.toLowerCase() == 'Waiting for quotation'.toLowerCase()) label = context.translation.waitingForQuotation; return Container( padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 10), - alignment: labelInCenter ? Alignment.center: null, + alignment: labelInCenter ? Alignment.center : null, decoration: ShapeDecoration( color: backgroundColor, shape: RoundedRectangleBorder( diff --git a/pubspec.yaml b/pubspec.yaml index a7cf4381..70641f4d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.3.10+29 +version: 1.3.11+30 environment: sdk: ">=3.5.0 <4.0.0"