Merge branch 'refs/heads/design_3.0_asset_inventory_module' into design_3.0_GlobalHealth

# Conflicts:
#	lib/controllers/api_routes/urls.dart
#	lib/controllers/providers/api/all_requests_provider.dart
design_3.0_GlobalHealth
Sikander Saleem 5 days ago
commit a39d8297a6

@ -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 {

@ -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';
}

@ -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<LookUpAutoCompleteField> {
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<AssetInventoryProvider>(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<Lookup>(
optionsBuilder: (TextEditingValue textEditingValue) async {
if (textEditingValue.text.isEmpty) {
return const Iterable<Lookup>.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);
},
),
);
}
}

@ -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';
}

@ -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;
}

@ -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"];

@ -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,

@ -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: () {

@ -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;

@ -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

@ -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));
});
}
}

@ -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<String, dynamic> 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;
}

@ -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>? rooms; // Now nullable
List<Rooms>? rooms=[]; // Now nullable
Department copyWith({
num? id, // Parameters are now nullable

@ -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

@ -10,8 +10,8 @@ class Rooms extends Base {
Rooms.fromJson(Map<String, dynamic>? 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'];
}

@ -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

@ -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<AssetInventoryModel>? assetList;
AssetInventoryResponse({
this.totalRows,
this.count,
this.message,
this.title,
this.innerMessage,
this.responseCode,
this.isSuccess,
this.assetList,
});
AssetInventoryResponse.fromJson(Map<String, dynamic> 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<String, dynamic> 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<String, dynamic> 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<String, dynamic> 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,
);
}
}

@ -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<Site> assetInventorySites = [];
List<AssetInventoryAssignedEmployee> assetInventoryAssignedEmployee = [];
SessionModel({
this.id,
this.sessionName,
this.sessionTypeId,
this.sessionTypeValue,
this.sessionTypeName,
this.statusId,
this.statusName,
this.startDate,
this.endDate,
List<Site>? assetInventorySites,
List<AssetInventoryAssignedEmployee>? assetInventoryAssignedEmployee,
}) {
this.assetInventorySites = assetInventorySites ?? [];
this.assetInventoryAssignedEmployee = assetInventoryAssignedEmployee ?? [];
}
SessionModel.fromJson(Map<String, dynamic> 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<String, dynamic> toJson() {
final map = <String, dynamic>{};
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<AssetInventoryBuilding> assetInventoryBuildings = [];
//
// AssetInventorySite({
// this.siteId,
// this.siteName,
// List<AssetInventoryBuilding>? assetInventoryBuildings,
// }) {
// this.assetInventoryBuildings = assetInventoryBuildings ?? [];
// }
//
// AssetInventorySite.fromJson(Map<String, dynamic> json) {
// siteId = json['siteId'];
// siteName = json['siteName'];
//
// if (json['assetInventoryBuildings'] != null) {
// assetInventoryBuildings = (json['assetInventoryBuildings'] as List).map((e) => AssetInventoryBuilding.fromJson(e)).toList();
// }
// }
//
// Map<String, dynamic> toJson() {
// final map = <String, dynamic>{};
// map['siteId'] = siteId;
// map['siteName'] = siteName;
// map['assetInventoryBuildings'] = assetInventoryBuildings.map((e) => e.toJson()).toList();
// return map;
// }
// }
//
// class AssetInventoryBuilding {
// int? buildingId;
// String? buildingName;
// List<AssetInventoryFloor> assetInventoryFloors = [];
//
// AssetInventoryBuilding({
// this.buildingId,
// this.buildingName,
// List<AssetInventoryFloor>? assetInventoryFloors,
// }) {
// this.assetInventoryFloors = assetInventoryFloors ?? [];
// }
//
// AssetInventoryBuilding.fromJson(Map<String, dynamic> json) {
// buildingId = json['buildingId'];
// buildingName = json['buildingName'];
//
// if (json['assetInventoryFloors'] != null) {
// assetInventoryFloors = (json['assetInventoryFloors'] as List).map((e) => AssetInventoryFloor.fromJson(e)).toList();
// }
// }
//
// Map<String, dynamic> toJson() {
// final map = <String, dynamic>{};
// map['buildingId'] = buildingId;
// map['buildingName'] = buildingName;
// map['assetInventoryFloors'] = assetInventoryFloors.map((e) => e.toJson()).toList();
// return map;
// }
// }
//
// class AssetInventoryFloor {
// int? floorId;
// String? floorName;
// List<AssetInventoryDepartment> assetInventoryDepartments = [];
//
// AssetInventoryFloor({
// this.floorId,
// this.floorName,
// List<AssetInventoryDepartment>? assetInventoryDepartments,
// }) {
// this.assetInventoryDepartments = assetInventoryDepartments ?? [];
// }
//
// AssetInventoryFloor.fromJson(Map<String, dynamic> json) {
// floorId = json['floorId'];
// floorName = json['floorName'];
//
// if (json['assetInventoryDepartments'] != null) {
// assetInventoryDepartments = (json['assetInventoryDepartments'] as List).map((e) => AssetInventoryDepartment.fromJson(e)).toList();
// }
// }
//
// Map<String, dynamic> toJson() {
// final map = <String, dynamic>{};
// map['floorId'] = floorId;
// map['floorName'] = floorName;
// map['assetInventoryDepartments'] = assetInventoryDepartments.map((e) => e.toJson()).toList();
// return map;
// }
// }
//
// class AssetInventoryDepartment {
// int? departmentId;
// String? departmentName;
// List<AssetInventoryRoom> assetInventoryRooms = [];
//
// AssetInventoryDepartment({
// this.departmentId,
// this.departmentName,
// List<AssetInventoryRoom>? assetInventoryRooms,
// }) {
// this.assetInventoryRooms = assetInventoryRooms ?? [];
// }
//
// AssetInventoryDepartment.fromJson(Map<String, dynamic> json) {
// departmentId = json['departmentId'];
// departmentName = json['departmentName'];
//
// if (json['assetInventoryRooms'] != null) {
// assetInventoryRooms = (json['assetInventoryRooms'] as List).map((e) => AssetInventoryRoom.fromJson(e)).toList();
// }
// }
//
// Map<String, dynamic> toJson() {
// final map = <String, dynamic>{};
// 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<String, dynamic> json) {
// roomId = json['roomId'];
// roomName = json['roomName'];
// }
//
// Map<String, dynamic> toJson() {
// final map = <String, dynamic>{};
// map['roomId'] = roomId;
// map['roomName'] = roomName;
// return map;
// }
// }
class AssetInventoryAssignedEmployee {
String? assignedEngineerId;
String? assignedEngineerName;
AssetInventoryAssignedEmployee({this.assignedEngineerId, this.assignedEngineerName});
AssetInventoryAssignedEmployee.fromJson(Map<String, dynamic> json) {
assignedEngineerId = json['assignedEngineerId'];
assignedEngineerName = json['assignedEngineerName'];
}
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['assignedEngineerId'] = assignedEngineerId;
map['assignedEngineerName'] = assignedEngineerName;
return map;
}
}

@ -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);
}
}

@ -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<AssetInventoryDetailView> createState() => _AssetInventoryDetailViewState();
}
class _AssetInventoryDetailViewState extends State<AssetInventoryDetailView> {
@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);
}
}

@ -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<AssetInventoryFormView> createState() => _AssetInventoryFormViewState();
}
class _AssetInventoryFormViewState extends State<AssetInventoryFormView> {
AssetInventoryModel? _scannedAssetModel = AssetInventoryModel();
AssetInventoryModel? _pickedAssetModel = AssetInventoryModel();
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final TextEditingController _assetNoController = TextEditingController();
final TextEditingController _serialNoController = TextEditingController();
final TextEditingController _remarksController = TextEditingController();
ValueNotifier<bool>? registeredController;
bool isRegistered = false;
final List<GenericAttachmentModel> 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<void> searchAsset({required String assetNo}) async {
AssetInventoryProvider provider = Provider.of<AssetInventoryProvider>(context, listen: false);
Map<String, dynamic> 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<AssetInventoryProvider>(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<AllRequestsProvider>(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<void> getAssetFilteredList({bool loadMore = false}) async {
AssetInventoryProvider provider = Provider.of<AssetInventoryProvider>(context, listen: false);
Map<String, dynamic> 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(),
);
}
}

@ -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<AssetInventoryPage> {
late AssetInventoryProvider _assetInventoryProvider;
@override
void initState() {
super.initState();
_assetInventoryProvider = Provider.of<AssetInventoryProvider>(context, listen: false);
WidgetsBinding.instance.addPostFrameCallback((_) {
getInitialData();
});
}
Future<void> 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<AssetInventoryProvider>(builder: (context, provider, child) {
return DefaultTabController(
length: 2,
child: provider.isLoading
? const CircularProgressIndicator(color: AppColor.primary10).center
: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
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<void> _scanAsset({required AssetInventoryProvider provider}) async {
provider.siteFilterAssetList.clear();
Navigator.push(context, MaterialPageRoute(builder: (contxt) => SiteInformationPage(sessionModel: provider.sessionModel ?? SessionModel())));
}
}

@ -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<AssetInventoryScanAssetView> createState() => _AssetInventoryScanAssetViewState();
}
class _AssetInventoryScanAssetViewState extends State<AssetInventoryScanAssetView> {
late AssetInventoryProvider assetInventoryProvider;
@override
void initState() {
assetInventoryProvider = Provider.of<AssetInventoryProvider>(context, listen: false);
super.initState();
}
Future<void> getAssetList({bool loadMore = false}) async {
await assetInventoryProvider.getAssetsInSession(
sessionId: widget.sessionId,
loadMore: loadMore,
);
}
@override
@override
Widget build(BuildContext context) {
return Consumer<AssetInventoryProvider>(
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<ScrollNotification>(
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<AssetInventoryProvider>(
// 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),
// ),
// );
// },
// );
// }
}

@ -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<SiteInformationPage> createState() => _SiteInformationPageState();
}
class _SiteInformationPageState extends State<SiteInformationPage> {
AssetInventoryModel assetInventoryModel = AssetInventoryModel();
bool showMarkAsComplete = false;
final ScrollController _scrollController = ScrollController();
@override
void initState() {
super.initState();
_scrollController.addListener(() {
final provider = Provider.of<AssetInventoryProvider>(context, listen: false);
if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent && !provider.isNextPageLoading && provider.nextPage) {
getAssetFilteredList(loadMore: true);
}
});
}
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
@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<AssetInventoryProvider>(context, listen: false);
Map<String, dynamic> 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<AllRequestsProvider>(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<AssetInventoryProvider>(
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<Site, NullableLoadingProvider>(
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<Building, NullableLoadingProvider>(
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<Floor, NullableLoadingProvider>(
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<Department, NullableLoadingProvider>(
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<Rooms, NullableLoadingProvider>(
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<bool> 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<void> getAssetFilteredList({bool loadMore = false}) async {
AssetInventoryProvider provider = Provider.of<AssetInventoryProvider>(context, listen: false);
Map<String, dynamic> 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);
}
}

@ -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();
}
}

@ -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<SearchAssetView> createState() => _SearchAssetViewState();
}
class _SearchAssetViewState extends State<SearchAssetView> {
int _selectedIndex = 0;
DeviceSearch? search;
late TextEditingController _searchController;
late AssetInventoryProvider _assetInventoryProvider;
final List<AssetInventoryModel> _searchableList = [];
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
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<String> 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<AssetInventoryProvider>(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));
}
}

@ -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<AssetInventoryModel> _devices = [];
List<AssetInventoryModel> _searchDevices = [];
List<AssetInventoryModel> get devices => _devices;
List<AssetInventoryModel> get searchDevices => _searchDevices;
SessionModel? sessionModel;
//TODO need to check i think don't need to create this obj
AssetInventoryResponse? assetInventoryResponse;
AssetInventoryResponse? siteFilterAssetResponse;
List<AssetInventoryModel> sessionAssetList = [];
List<AssetInventoryModel> 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<void> 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<int> 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<String, dynamic> 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<int> 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<String, dynamic> 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<int> getInventoryDetailsByFilter({
required Map<String, dynamic> 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<String, dynamic> 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<AssetInventoryModel?> searchAsset({
required Map<String, dynamic> 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<String, dynamic> 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<bool> 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<bool> 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<List<Lookup>> 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 = <String, String>{};
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<Lookup> 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<int> 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<String, dynamic> 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<AssetInventoryModel>((asset) => AssetInventoryModel.fromJson(asset)).toList() ?? [];
isSearchBy ? _searchDevices.addAll(dList) : _devices.addAll(dList);
nextPage = true;
} else {
nextPage = false;
}
isLoading = false;
notifyListeners();
return response.statusCode;
}
Future<bool> markAsComplete({
required Map<String, dynamic> 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;
}
}
}

@ -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,

@ -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');
},
)
],

@ -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<MyRequestsPage> {
if (context.settingProvider.isUserFlowMedical && Provider.of<UserProvider>(context, listen: false).user!.type != UsersTypes.normal_user) {
requestsList.add(Request(7, 'Recall and Alert'));
}
if (Provider.of<UserProvider>(context, listen: false).user!.type != UsersTypes.normal_user) {
requestsList.add(Request(8, 'Inventory Session'.addTranslation));
}
_provider = Provider.of<AllRequestsProvider>(context, listen: false);
_provider!.reset();
WidgetsBinding.instance.addPostFrameCallback((_) {

@ -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]));
// // }
// }));

@ -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,

@ -17,11 +17,16 @@ class AssetPicker extends StatelessWidget {
final List<Asset> 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<Asset>?;
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<Asset>?;
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),

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

@ -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<AssetScanQr> {
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(<Asset>[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(<Asset>[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<AssetScanQr> {
),
],
).expanded,
FooterActionButton.footerContainer(
if(widget.enablePickManually)
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
label: context.translation.pickManually,

@ -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:

Loading…
Cancel
Save