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 ad7786ca..d310d230 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -6,9 +6,12 @@ class URLs { // 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 + // http://10.201.111.125:9495/v4/swagger/index.html // 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 @@ -298,4 +301,27 @@ class URLs { static get addComment => "$_baseUrl/CallRequest/AddHistoryComment"; // add static get getSiteContactInfo => "$_baseUrl/AssetGroupSiteContactInfo"; // 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'; + + static get searchAssetName => '$_baseUrl/AssetInventory/SearchAssetName'; + + static get getSuppliersAutoCompleteInventory => '$_baseUrl/Supplier/GetSuppliersMobile'; + + static get getAssetsTemp => '$_baseUrl/AssetInventory/GetAssetsTemp'; + + static get convertDetailToComplete => '$_baseUrl/AssetInventory/ConvertDetailToComplete'; } diff --git a/lib/controllers/providers/api/all_requests_provider.dart b/lib/controllers/providers/api/all_requests_provider.dart index 6648965b..b1af1d28 100644 --- a/lib/controllers/providers/api/all_requests_provider.dart +++ b/lib/controllers/providers/api/all_requests_provider.dart @@ -138,12 +138,13 @@ class AllRequestsProvider extends ChangeNotifier { list.add(5); } list.add(6); // task module + if (context.settingProvider.isUserFlowMedical && context.userProvider.user!.type != UsersTypes.normal_user) { list.add(7); // task mod } - // if (context.userProvider.user!.type != UsersTypes.normal_user) { - // list.add(8); // - // } + if (context.userProvider.user!.type != UsersTypes.normal_user) { + list.add(8); // + } return list; } 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/dashboard_latest/widgets/request_category_list.dart b/lib/dashboard_latest/widgets/request_category_list.dart index 5c6fe481..744958e5 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/tm_module/tasks_wo/task_request_item_view.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/pages/land_page/requests/device_item_view.dart'; @@ -48,6 +49,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); default: return Container( height: 100, diff --git a/lib/main.dart b/lib/main.dart index e60fb1e8..d9fabe55 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -25,6 +25,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/tm_module/tasks_wo/create_task_view.dart'; @@ -280,6 +281,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 3773dc64..093019b2 100644 --- a/lib/models/new_models/dashboard_detail.dart +++ b/lib/models/new_models/dashboard_detail.dart @@ -50,17 +50,34 @@ 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({this.id, this.typeTransaction, this.nameOfType,this.transactionDate, this.statusName, this.priorityName, this.isHighPriority, this.assetName, this.assetNumber, this.requestTypeName, this.requestNo,this.transactionNo}); + Data( + {this.id, + this.typeTransaction, + this.nameOfType, + this.transactionDate, + this.statusName, + this.numberOfAssets, + this.numberOfSites, + this.sessionType, + this.priorityName, + this.isHighPriority, + this.assetName, + this.assetNumber, + this.requestTypeName, + this.requestNo, + this.transactionNo}); Data.fromJson(Map json) { - id = json['id']; typeTransaction = json['typeTransaction']; transactionDate = json['transactionDate']; @@ -68,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']; @@ -90,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/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..a07d980a --- /dev/null +++ b/lib/modules/asset_inventory_module/models/asset_inventory_model.dart @@ -0,0 +1,292 @@ +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? 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.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']; + 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, + '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, + 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..e65e192c --- /dev/null +++ b/lib/modules/asset_inventory_module/models/session_model.dart @@ -0,0 +1,231 @@ + +import 'package:test_sa/models/new_models/site.dart'; + +class SessionModel { + int? id; + 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.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']; + 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['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..889451de --- /dev/null +++ b/lib/modules/asset_inventory_module/pages/asset_detail_card_view.dart @@ -0,0 +1,109 @@ +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( + '${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( + '${'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..ae48817c --- /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.startDate!.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 Typ'.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..d7acf11c --- /dev/null +++ b/lib/modules/asset_inventory_module/pages/asset_inventory_form_view.dart @@ -0,0 +1,466 @@ +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/models/service_request/supplier_details.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_inventory_page.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'; +import 'asset_inventory_site_information_page.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(); + 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; + + 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 ?? '-'), + 12.height, + ], + if (_scannedAssetModel?.status != null && _scannedAssetModel!.status!.isNotEmpty) 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(); + } + _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(), + ); + } +} 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..ed694c44 --- /dev/null +++ b/lib/modules/asset_inventory_module/provider/asset_inventory_provider.dart @@ -0,0 +1,441 @@ +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 { + log('payload i got is ${model?.toJson()}'); + 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 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/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/pages/land_page/contact_us_bottom_sheet.dart b/lib/new_views/pages/land_page/contact_us_bottom_sheet.dart index 3d35de8e..784a125e 100644 --- a/lib/new_views/pages/land_page/contact_us_bottom_sheet.dart +++ b/lib/new_views/pages/land_page/contact_us_bottom_sheet.dart @@ -1,11 +1,9 @@ -import 'package:clipboard/clipboard.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/controllers/providers/api/user_provider.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; -import 'package:test_sa/extensions/string_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/models/site_contact_info_model.dart'; @@ -77,7 +75,7 @@ class ContactUsBottomSheet extends StatelessWidget { padding: EdgeInsets.zero, iconSize: 20, onPressed: () { - FlutterClipboard.copy('Hello Flutter friends'); + // FlutterClipboard.copy('Hello Flutter friends'); }, ) ], 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 06ce2532..25245710 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 @@ -3,10 +3,12 @@ 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'; @@ -50,7 +52,9 @@ class _MyRequestsPageState extends State { 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)); + } _provider = Provider.of(context, listen: false); _provider!.reset(); WidgetsBinding.instance.addPostFrameCallback((_) { 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 ace4047f..16ac1c20 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 @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:test_sa/extensions/int_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/tm_module/tasks_wo/task_request_item_view.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/pages/land_page/requests/device_item_view.dart'; @@ -45,44 +46,29 @@ class RequestPaginatedListview extends StatelessWidget { } Widget _buildRequestItem(Data request) { - 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; - - 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 { - 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); + 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 a3548947..5031a033 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 @@ -4,6 +4,7 @@ import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/models/all_requests_and_count_model.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/new_views/pages/land_page/requests/device_item_view.dart'; import 'package:test_sa/new_views/pages/land_page/requests/gas_refill_item_view.dart'; @@ -42,6 +43,8 @@ class RequestItemViewList extends StatelessWidget { return TaskRequestItemView(requestDetails: list[index]); case 7: return TaskRequestItemView(requestDetails: list[index]); + case 8: + return InventorySessionItemView(requestDetails: list[index]); default: Container( height: 100, 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/fullscreen_dialogs/selection_fullscreen_dialog.dart b/lib/views/widgets/fullscreen_dialogs/selection_fullscreen_dialog.dart index ce08754f..b7227dda 100644 --- a/lib/views/widgets/fullscreen_dialogs/selection_fullscreen_dialog.dart +++ b/lib/views/widgets/fullscreen_dialogs/selection_fullscreen_dialog.dart @@ -87,9 +87,7 @@ class _SelectionBottomSheetState extends State(0), - backgroundColor: WidgetStateProperty.all( - AppColor.fieldBgColor(context), // Your custom background color - ), + backgroundColor: WidgetStateProperty.all(context.isDark ? AppColor.neutral120 : null), leading: Icon(Icons.search, color: AppColor.iconColor(context)), textStyle: WidgetStateProperty.all( TextStyle(color: AppColor.textColor(context), fontSize: 16.0), 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/pubspec.lock b/pubspec.lock index 701decdc..fdc4903d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -177,6 +177,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.4.1" + clipboard: + dependency: "direct main" + description: + name: clipboard + sha256: "1920c0337f8808be4166c5f1b236301ff381ef69633b0757c502d97f1f740102" + url: "https://pub.dev" + source: hosted + version: "2.0.2" clock: dependency: transitive description: