Merge branch 'zaid_development_new' into majd_development_new

main_design2.0
nextwo 2 years ago
commit 8df2900254

@ -339,7 +339,7 @@ class ServiceRequestsProvider extends ChangeNotifier {
?.map( ?.map(
(p) => { (p) => {
"id": p.reportPartID ?? 0, "id": p.reportPartID ?? 0,
"sparePart": {"id": p.id, "partNo": p.code, "partName": p.name}, "sparePart": {"id": p.id, "partNo": p.partNo, "partName": p.partName},
"qty": p.quantity "qty": p.quantity
}, },
) )
@ -614,6 +614,19 @@ class ServiceRequestsProvider extends ChangeNotifier {
} }
} }
Future<ServiceRequest> getServiceRequestObjectById({@required String requestId}) async {
try {
Response response = await ApiManager.instance.get(URLs.getServiceRequestById + "?callRequestId=$requestId");
if (response.statusCode >= 200 && response.statusCode < 300) {
return ServiceRequest.fromJson(json.decode(response.body)["data"]);
} else {
return null;
}
} catch (error) {
return null;
}
}
Future<Map<String, dynamic>> getServiceRequestById({@required String requestId}) async { Future<Map<String, dynamic>> getServiceRequestById({@required String requestId}) async {
Response response; Response response;
try { try {

@ -18,7 +18,7 @@ class Pentry {
DateTime expectedVisitDate; DateTime expectedVisitDate;
String travelingHours; String travelingHours;
String image; String image;
File file; List<File> files = [];
// List<Contact> contacts; // List<Contact> contacts;
List<PPMCheckList> ppmCheckLists; List<PPMCheckList> ppmCheckLists;
@ -37,7 +37,7 @@ class Pentry {
this.actualVisitDate, this.actualVisitDate,
this.expectedVisitDate, this.expectedVisitDate,
this.image, this.image,
this.file, this.files,
// this.contacts, // this.contacts,
this.ppmCheckLists, this.ppmCheckLists,
this.calibrationTools, this.calibrationTools,
@ -86,10 +86,8 @@ class Pentry {
map["endDate"] = timer.endAt?.toIso8601String() ?? DateTime.now().toIso8601String(); map["endDate"] = timer.endAt?.toIso8601String() ?? DateTime.now().toIso8601String();
map["workingHours"] = (timer.durationInSecond / 60 / 60).toStringAsFixed(5); map["workingHours"] = (timer.durationInSecond / 60 / 60).toStringAsFixed(5);
} }
if (file != null) { if (files?.isNotEmpty ?? false) {
map["vAttachments"] = [ map["vAttachments"] = files.map((file) => {"attachmentName": (file.path.split("/").last + base64Encode(file.readAsBytesSync()))}).toList();
{"attachmentName": (file.path.split("/").last + base64Encode(file.readAsBytesSync()))}
];
} }
map["travelingHours"] = travelingHours; map["travelingHours"] = travelingHours;
// if(contacts?.isNotEmpty == true) { // if(contacts?.isNotEmpty == true) {
@ -119,7 +117,11 @@ class Pentry {
if (map['vKits'] != null) { if (map['vKits'] != null) {
pmKits = (map['vKits'] as List<dynamic>).map((e) => PMKit.fromMap(e as Map<String, dynamic>)).toList(); pmKits = (map['vKits'] as List<dynamic>).map((e) => PMKit.fromMap(e as Map<String, dynamic>)).toList();
} }
List<String> files = [];
if (map["vAttachments"] != null) if (map["vAttachments"] is List) {
List list = map["vAttachments"];
files = list.map((e) => URLs.getFileUrl(e["attachmentName"])).toList();
}
List<PPMCheckList> ppmCheckLists = []; List<PPMCheckList> ppmCheckLists = [];
if (map['vChecklists'] != null) { if (map['vChecklists'] != null) {
ppmCheckLists = (map['vChecklists'] as List<dynamic>).map((e) => PPMCheckList.fromMap(e as Map<String, dynamic>)).toList(); ppmCheckLists = (map['vChecklists'] as List<dynamic>).map((e) => PPMCheckList.fromMap(e as Map<String, dynamic>)).toList();
@ -144,6 +146,7 @@ class Pentry {
ppmCheckLists: ppmCheckLists, ppmCheckLists: ppmCheckLists,
calibrationTools: calibrationTools, calibrationTools: calibrationTools,
pmKits: pmKits, pmKits: pmKits,
files: files.map((e) => File(e)).toList(),
signatureNurse: URLs.getFileUrl(map["nurseSignature"]), signatureNurse: URLs.getFileUrl(map["nurseSignature"]),
signatureEngineer: URLs.getFileUrl(map["engSignature"]), signatureEngineer: URLs.getFileUrl(map["engSignature"]),
); );
@ -157,7 +160,7 @@ class Pentry {
DateTime expectedVisitDate, DateTime expectedVisitDate,
String travelingHours, String travelingHours,
String image, String image,
File imageFile, List<File> files,
List<PPMCheckList> ppmCheckLists, List<PPMCheckList> ppmCheckLists,
List<CalibrationTool> calibrationTools, List<CalibrationTool> calibrationTools,
List<PMKit> pmKits, List<PMKit> pmKits,
@ -172,7 +175,7 @@ class Pentry {
expectedVisitDate: expectedVisitDate ?? this.expectedVisitDate, expectedVisitDate: expectedVisitDate ?? this.expectedVisitDate,
travelingHours: travelingHours ?? this.travelingHours, travelingHours: travelingHours ?? this.travelingHours,
image: image ?? this.image, image: image ?? this.image,
file: imageFile ?? this.file, files: files ?? this.files,
ppmCheckLists: ppmCheckLists ?? this.ppmCheckLists?.map((e) => e.copyWith())?.toList(), ppmCheckLists: ppmCheckLists ?? this.ppmCheckLists?.map((e) => e.copyWith())?.toList(),
calibrationTools: calibrationTools ?? this.calibrationTools?.map((e) => e.copyWith())?.toList(), calibrationTools: calibrationTools ?? this.calibrationTools?.map((e) => e.copyWith())?.toList(),
pmKits: pmKits ?? this.pmKits.map((e) => e.copyWith()).toList(), pmKits: pmKits ?? this.pmKits.map((e) => e.copyWith()).toList(),

@ -1,23 +1,28 @@
class Part { class Part {
int id; int id;
int reportPartID; int reportPartID;
String code; String partNo;
String name; String partName;
int quantity; int quantity;
double returnQty, installQty;
Part({ Part({
this.id, this.id,
this.reportPartID, this.reportPartID,
this.code, this.partNo,
this.name, this.partName,
this.quantity = 1, this.quantity = 1,
this.installQty,
this.returnQty,
}); });
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {
"id": reportPartID ?? 0, "id": reportPartID ?? 0,
"sparePart": {"id": id, "partNo": code, "partName": name}, "sparePart": {"id": id, "partNo": partNo, "partName": partName},
"qty": quantity "qty": quantity,
if (returnQty != null) "returnQty": returnQty,
if (installQty != null) "installQty": installQty,
}; };
} }
@ -25,9 +30,11 @@ class Part {
return Part( return Part(
id: parsedJson["id"], id: parsedJson["id"],
reportPartID: reportJson != null ? reportJson["id"] : null, reportPartID: reportJson != null ? reportJson["id"] : null,
code: parsedJson["partNo"], partNo: parsedJson["partNo"],
name: parsedJson["partName"], partName: parsedJson["partName"],
quantity: reportJson != null ? (reportJson["qty"] ?? 1).toInt() : 1, quantity: reportJson != null ? (reportJson["qty"] ?? 1).toInt() : 1,
returnQty: parsedJson["returnQty"],
installQty: parsedJson["installQty"],
); );
} }
} }

@ -198,6 +198,11 @@ class ServiceReport {
_parts = partsList.map((e) => Part.fromJson(e["sparePart"], reportJson: e)).toList(); _parts = partsList.map((e) => Part.fromJson(e["sparePart"], reportJson: e)).toList();
} }
} }
List<String> _files = [];
if (parsedJson["attachmentsWorkOrder"] != null && parsedJson["attachmentsWorkOrder"] is List) {
List list = parsedJson["attachmentsWorkOrder"];
_files = list.map((e) => (URLs.getFileUrl(e["name"]))).toList();
}
return ServiceReport( return ServiceReport(
id: id, id: id,
assetType: Lookup.fromJson(parsedJson["assetType"]), assetType: Lookup.fromJson(parsedJson["assetType"]),
@ -208,7 +213,7 @@ class ServiceReport {
equipmentStatus: Lookup.fromJson(parsedJson["equipmentStatus"]), equipmentStatus: Lookup.fromJson(parsedJson["equipmentStatus"]),
type: Lookup.fromJson(parsedJson["typeOfWO"]), type: Lookup.fromJson(parsedJson["typeOfWO"]),
faultDescription: parsedJson['faultDescription'] != null ? FaultDescription.fromJson(parsedJson['faultDescription']) : null, faultDescription: parsedJson['faultDescription'] != null ? FaultDescription.fromJson(parsedJson['faultDescription']) : null,
files: _files,
// faultDescription: parsedJson["faultDescription"], // faultDescription: parsedJson["faultDescription"],
// startDate: DateTime.tryParse(parsedJson["startofWorkTime"] ?? ""), // startDate: DateTime.tryParse(parsedJson["startofWorkTime"] ?? ""),
// endDate: DateTime.tryParse(parsedJson["endofWorkTime"] ?? ""), // endDate: DateTime.tryParse(parsedJson["endofWorkTime"] ?? ""),
@ -221,7 +226,7 @@ class ServiceReport {
//quantity: parsedJson["nid"], //quantity: parsedJson["nid"],
travelingHours: parsedJson["travelingHours"], travelingHours: parsedJson["travelingHours"],
travelingExpense: parsedJson["travelingExpenses"], travelingExpense: parsedJson["travelingExpenses"],
visitDate: DateTime.tryParse(parsedJson["visitDate"]), visitDate: DateTime.tryParse(parsedJson["visitDate"] ?? ""),
returnToService: DateTime.tryParse(parsedJson["replacementDate"] ?? ""), returnToService: DateTime.tryParse(parsedJson["replacementDate"] ?? ""),
workingHours: parsedJson["workingHours"], workingHours: parsedJson["workingHours"],
timer: TimerModel( timer: TimerModel(

@ -1,4 +1,5 @@
import 'package:test_sa/models/lookup.dart'; import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/models/service_request/sub_work_order_details.dart';
import 'package:test_sa/models/timer_model.dart'; import 'package:test_sa/models/timer_model.dart';
import '../fault_description.dart'; import '../fault_description.dart';
@ -163,7 +164,6 @@ class SearchWorkOrder {
Lookup currentSituation; Lookup currentSituation;
Lookup repairLocation; Lookup repairLocation;
Lookup reason; Lookup reason;
// String startofWorkTime; // String startofWorkTime;
// String endofWorkTime; // String endofWorkTime;
num workingHours; num workingHours;
@ -321,6 +321,15 @@ class SearchWorkOrder {
} }
return map; return map;
} }
void copyDetails(SubWorkOrderDetails subWoDetails) {
equipmentStatus = subWoDetails.equipmentStatus;
reason = subWoDetails.reason;
faultDescription = subWoDetails.faultDescription;
sparePartsWorkOrders = subWoDetails.sparePartsWorkOrders;
suppEngineerWorkOrders = subWoDetails.suppEngineerWorkOrders;
supplier = subWoDetails.supplier;
}
} }
class WoParentDto { class WoParentDto {

@ -1,5 +1,6 @@
import 'package:test_sa/controllers/api_routes/urls.dart'; import 'package:test_sa/controllers/api_routes/urls.dart';
import 'package:test_sa/models/device/device.dart'; import 'package:test_sa/models/device/device.dart';
import 'package:test_sa/models/fault_description.dart';
import 'package:test_sa/models/lookup.dart'; import 'package:test_sa/models/lookup.dart';
class ServiceRequest { class ServiceRequest {
@ -27,8 +28,9 @@ class ServiceRequest {
bool viewReport; bool viewReport;
String engineerMobile; String engineerMobile;
String deviceModel; String deviceModel;
String faultDescription; FaultDescription faultDescription;
String workPerformed;
//String workPerformed;
String visitDate; String visitDate;
DateTime nextVisitDate; DateTime nextVisitDate;
String jobSheetNumber; String jobSheetNumber;
@ -68,7 +70,7 @@ class ServiceRequest {
this.jobSheetNumber, this.jobSheetNumber,
this.visitDate, this.visitDate,
this.nextVisitDate, this.nextVisitDate,
this.workPerformed, //this.workPerformed,
this.reportID, this.reportID,
this.defectType, this.defectType,
this.priority, this.priority,
@ -84,9 +86,11 @@ class ServiceRequest {
factory ServiceRequest.fromJson(Map<String, dynamic> parsedJson) { factory ServiceRequest.fromJson(Map<String, dynamic> parsedJson) {
List<String> images = []; List<String> images = [];
print(parsedJson["attachmentsCallRequest"]);
if (parsedJson["attachmentsCallRequest"] != null) if (parsedJson["attachmentsCallRequest"] is List) { if (parsedJson["attachmentsCallRequest"] != null) if (parsedJson["attachmentsCallRequest"] is List) {
List list = parsedJson["attachmentsCallRequest"]; List list = parsedJson["attachmentsCallRequest"];
images = list.map((e) => URLs.getFileUrl(e["attachmentsCallRequest"])).toList(); images = list.map((e) => URLs.getFileUrl(e["name"])).toList();
print(images);
} }
return ServiceRequest( return ServiceRequest(
id: parsedJson["id"].toString(), id: parsedJson["id"].toString(),
@ -114,16 +118,21 @@ class ServiceRequest {
viewReport: parsedJson["workOrder"] != null, viewReport: parsedJson["workOrder"] != null,
deviceModel: parsedJson["asset"]["modelDefinition"]["modelName"], deviceModel: parsedJson["asset"]["modelDefinition"]["modelName"],
engineerMobile: parsedJson["assignedEmployee"] == null ? null : parsedJson["assignedEmployee"]["phone"], engineerMobile: parsedJson["assignedEmployee"] == null ? null : parsedJson["assignedEmployee"]["phone"],
faultDescription: parsedJson["workOrder"] != null ? parsedJson["workOrder"]["faultDescription"] : null, faultDescription: parsedJson["workOrder"] != null
? parsedJson["workOrder"]["faultDescription"] != null
? FaultDescription.fromJson(parsedJson["workOrder"]["faultDescription"])
: null
: null,
jobSheetNumber: parsedJson["workOrder"] != null ? parsedJson["workOrder"]["jobSheetNumber"] : null, jobSheetNumber: parsedJson["workOrder"] != null ? parsedJson["workOrder"]["jobSheetNumber"] : null,
visitDate: DateTime.tryParse(parsedJson["visitDate"] ?? "").toString().split(" ").first, visitDate: DateTime.tryParse(parsedJson["visitDate"] ?? "").toString().split(" ").first,
nextVisitDate: DateTime.tryParse(parsedJson["nextVisitDate"] ?? ""), nextVisitDate: DateTime.tryParse(parsedJson["nextVisitDate"] ?? ""),
workPerformed: parsedJson["workOrder"] != null ? parsedJson["workOrder"]["workPerformed"] : null, //workPerformed: parsedJson["workOrder"] != null ? parsedJson["workOrder"]["workPerformed"] : null,
device: Device.fromJson(parsedJson["asset"]), device: Device.fromJson(parsedJson["asset"]),
reviewComment: parsedJson["reviewComment"], reviewComment: parsedJson["reviewComment"],
type: Lookup.fromJson(parsedJson['typeofRequest']), type: Lookup.fromJson(parsedJson['typeofRequest']),
defectType: Lookup.fromJson(parsedJson['defectType']), defectType: Lookup.fromJson(parsedJson['defectType']),
loanAvailability: Lookup.fromJson(parsedJson['loanAvailability']), loanAvailability: Lookup.fromJson(parsedJson['loanAvailablity']),
firstAction: Lookup.fromJson(parsedJson['firstAction']), firstAction: Lookup.fromJson(parsedJson['firstAction']),
requestedThrough: Lookup.fromJson(parsedJson['requestedThrough']), requestedThrough: Lookup.fromJson(parsedJson['requestedThrough']),
priority: Lookup.fromJson(parsedJson['priority']), priority: Lookup.fromJson(parsedJson['priority']),

@ -7,43 +7,53 @@ class SubWorkOrderDetails {
Lookup equipmentStatus; Lookup equipmentStatus;
Lookup reason; Lookup reason;
FaultDescription faultDescription; FaultDescription faultDescription;
// Lookup loanAvailablity;
List<SparePartsWorkOrders> sparePartsWorkOrders; List<SparePartsWorkOrders> sparePartsWorkOrders;
List<SuppEngineerWorkOrders> suppEngineerWorkOrders;
SupplierModel supplier;
SubWorkOrderDetails({ SubWorkOrderDetails({
this.equipmentStatus, this.equipmentStatus,
this.reason, this.reason,
this.faultDescription, this.faultDescription,
// this.loanAvailablity,
this.sparePartsWorkOrders, this.sparePartsWorkOrders,
this.supplier,
this.suppEngineerWorkOrders,
}); });
SubWorkOrderDetails.fromJson(dynamic json) { SubWorkOrderDetails.fromJson(dynamic json) {
if (json['equipmentStatus'] != null) equipmentStatus = Lookup.fromJson(json['equipmentStatus']); if (json['equipmentStatus'] != null) equipmentStatus = Lookup.fromJson(json['equipmentStatus']);
if (json['reason'] != null) reason = Lookup.fromJson(json['reason']); if (json['reason'] != null) reason = Lookup.fromJson(json['reason']);
if (json['faultDescription'] != null) faultDescription = FaultDescription.fromJson(json['faultDescription']); if (json['faultDescription'] != null) faultDescription = FaultDescription.fromJson(json['faultDescription']);
// loanAvailablity = Lookup.fromJson(json['loanAvailablity']); if (json['supplier'] != null) supplier = SupplierModel.fromJson(json['supplier']);
if (json['sparePartsWorkOrders'] != null) { if (json['sparePartsWorkOrders'] != null) {
sparePartsWorkOrders = []; sparePartsWorkOrders = [];
json['sparePartsWorkOrders'].forEach((v) { json['sparePartsWorkOrders'].forEach((v) {
sparePartsWorkOrders.add(SparePartsWorkOrders.fromJson(v)); sparePartsWorkOrders.add(SparePartsWorkOrders.fromJson(v));
}); });
} }
if (json['suppEngineerWorkOrders'] != null) {
suppEngineerWorkOrders = [];
json['suppEngineerWorkOrders'].forEach((v) {
suppEngineerWorkOrders.add(SuppEngineerWorkOrders.fromJson(v));
});
}
} }
SubWorkOrderDetails copyWith({ SubWorkOrderDetails copyWith({
Lookup equipmentStatus, Lookup equipmentStatus,
Lookup reason, Lookup reason,
FaultDescription faultDescription, FaultDescription faultDescription,
// Lookup loanAvailablity,
List<SparePartsWorkOrders> sparePartsWorkOrders, List<SparePartsWorkOrders> sparePartsWorkOrders,
SupplierModel supplier,
SuppEngineerWorkOrders suppEngineerWorkOrders,
}) => }) =>
SubWorkOrderDetails( SubWorkOrderDetails(
equipmentStatus: equipmentStatus ?? this.equipmentStatus, equipmentStatus: equipmentStatus ?? this.equipmentStatus,
reason: reason ?? this.reason, reason: reason ?? this.reason,
faultDescription: faultDescription ?? this.faultDescription, faultDescription: faultDescription ?? this.faultDescription,
// loanAvailablity: loanAvailablity ?? this.loanAvailablity,
sparePartsWorkOrders: sparePartsWorkOrders ?? this.sparePartsWorkOrders, sparePartsWorkOrders: sparePartsWorkOrders ?? this.sparePartsWorkOrders,
supplier: supplier ?? this.supplier,
suppEngineerWorkOrders: suppEngineerWorkOrders ?? this.suppEngineerWorkOrders,
); );
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
@ -51,10 +61,13 @@ class SubWorkOrderDetails {
map['equipmentStatus'] = equipmentStatus?.toMap(); map['equipmentStatus'] = equipmentStatus?.toMap();
map['reason'] = reason?.toMap(); map['reason'] = reason?.toMap();
map['faultDescription'] = faultDescription?.toJson(); map['faultDescription'] = faultDescription?.toJson();
// map['loanAvailablity'] = loanAvailablity?.toMap();
if (sparePartsWorkOrders?.isNotEmpty ?? false) { if (sparePartsWorkOrders?.isNotEmpty ?? false) {
map['sparePartsWorkOrders'] = sparePartsWorkOrders?.map((e) => e.toJson())?.toList(); map['sparePartsWorkOrders'] = sparePartsWorkOrders?.map((e) => e.toJson())?.toList();
} }
if (suppEngineerWorkOrders?.isNotEmpty ?? false) {
map['suppEngineerWorkOrders'] = suppEngineerWorkOrders?.map((e) => e.toJson())?.toList();
}
map['supplier'] = supplier?.toJson();
return map; return map;
} }
} }

@ -1,3 +1,4 @@
import 'package:test_sa/controllers/api_routes/urls.dart';
import 'package:test_sa/models/lookup.dart'; import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/models/pantry/pentry.dart'; import 'package:test_sa/models/pantry/pentry.dart';
@ -50,7 +51,9 @@ class Visit {
List<String> images = []; List<String> images = [];
if (parsedJson["vAttachments"] is List) { if (parsedJson["vAttachments"] is List) {
List list = parsedJson["vAttachments"]; List list = parsedJson["vAttachments"];
images = list.map((e) => e["attachmentURL"].toString()).toList(); images = list.map((e) {
return URLs.getFileUrl(e["attachmentURL"].toString());
}).toList();
} }
return Visit( return Visit(
id: parsedJson["id"], id: parsedJson["id"],

@ -16,6 +16,7 @@ import 'package:test_sa/views/app_style/colors.dart';
import 'package:test_sa/views/pages/sub_workorder/spare_parts_details_bottom_sheet.dart'; import 'package:test_sa/views/pages/sub_workorder/spare_parts_details_bottom_sheet.dart';
import 'package:test_sa/views/pages/sub_workorder/work_order_details_bottom_sheet.dart'; import 'package:test_sa/views/pages/sub_workorder/work_order_details_bottom_sheet.dart';
import 'package:test_sa/views/pages/sub_workorder/workorder_details.dart'; import 'package:test_sa/views/pages/sub_workorder/workorder_details.dart';
import 'package:test_sa/views/widgets/images/multi_image_picker.dart';
import 'package:test_sa/views/widgets/loaders/app_loading.dart'; import 'package:test_sa/views/widgets/loaders/app_loading.dart';
import 'package:test_sa/views/widgets/status/service_request/service_request_defect_types_mune.dart'; import 'package:test_sa/views/widgets/status/service_request/service_request_defect_types_mune.dart';
@ -27,7 +28,6 @@ import '../../widgets/app_text_form_field.dart';
import '../../widgets/buttons/app_back_button.dart'; import '../../widgets/buttons/app_back_button.dart';
import '../../widgets/buttons/app_button.dart'; import '../../widgets/buttons/app_button.dart';
import '../../widgets/date_and_time/date_picker.dart'; import '../../widgets/date_and_time/date_picker.dart';
import '../../widgets/images/mini_one_file_picker.dart';
import '../../widgets/status/report/service_report_fault_description.dart'; import '../../widgets/status/report/service_report_fault_description.dart';
import '../../widgets/status/report/service_report_reasons.dart'; import '../../widgets/status/report/service_report_reasons.dart';
import '../../widgets/titles/app_sub_title.dart'; import '../../widgets/titles/app_sub_title.dart';
@ -47,7 +47,7 @@ class _CreateSubWorkOrderPageState extends State<CreateSubWorkOrderPage> {
SearchWorkOrder _subWorkOrders; SearchWorkOrder _subWorkOrders;
Lookup _serviceReportReason = const Lookup(); Lookup _serviceReportReason = const Lookup();
ServiceReport _serviceReport; ServiceReport _serviceReport;
File _file; final List<File> _files = [];
bool _isLoading = false; bool _isLoading = false;
bool _validate = false; bool _validate = false;
@ -75,15 +75,13 @@ class _CreateSubWorkOrderPageState extends State<CreateSubWorkOrderPage> {
assetTypesProvider = Provider.of<ServiceStatusProvider>(context, listen: false); assetTypesProvider = Provider.of<ServiceStatusProvider>(context, listen: false);
_callRequestForWorkOrder = await serviceRequestProvider.getCallRequestForWorkOrder(callId: widget.workOrder.callRequest.id.toString()); _callRequestForWorkOrder = await serviceRequestProvider.getCallRequestForWorkOrder(callId: widget.workOrder.callRequest.id.toString());
if (_subWorkOrders?.parentWOId != null) { if (_subWorkOrders?.parentWOId != null) {
print(_subWorkOrders?.parentWOId.toString());
final subWoDetails = await serviceRequestProvider.getSubWorkOrderDetails(parentId: _subWorkOrders?.parentWOId.toString()); final subWoDetails = await serviceRequestProvider.getSubWorkOrderDetails(parentId: _subWorkOrders?.parentWOId.toString());
_subWorkOrders.equipmentStatus = subWoDetails.equipmentStatus; _subWorkOrders.copyDetails(subWoDetails);
_subWorkOrders.reason = subWoDetails.reason;
_serviceReportReason = _subWorkOrders.reason;
_subWorkOrders.faultDescription = subWoDetails.faultDescription;
_subWorkOrders.sparePartsWorkOrders = subWoDetails.sparePartsWorkOrders;
_serviceReport.equipmentStatus = subWoDetails.equipmentStatus; _serviceReport.equipmentStatus = subWoDetails.equipmentStatus;
_serviceReport.reason = subWoDetails.reason; _serviceReport.reason = subWoDetails.reason;
_serviceReport.faultDescription = subWoDetails.faultDescription; _serviceReport.faultDescription = subWoDetails.faultDescription;
_serviceReportReason = _subWorkOrders.reason;
} }
await assetTypesProvider.getTypes(); await assetTypesProvider.getTypes();
_subWorkOrders?.assetType = assetTypesProvider.statuses?.firstWhere( _subWorkOrders?.assetType = assetTypesProvider.statuses?.firstWhere(
@ -213,7 +211,7 @@ class _CreateSubWorkOrderPageState extends State<CreateSubWorkOrderPage> {
onSelect: (status) { onSelect: (status) {
_serviceReportReason = status; _serviceReportReason = status;
_subWorkOrders.reason = status; _subWorkOrders.reason = status;
setState(() {}); // setState(() {});
}, },
), ),
], ],
@ -251,6 +249,8 @@ class _CreateSubWorkOrderPageState extends State<CreateSubWorkOrderPage> {
], ],
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
MultiFilesPicker(label: "Attachments", files: _files),
const SizedBox(height: 8),
InkWell( InkWell(
onTap: () async { onTap: () async {
await showModalBottomSheet( await showModalBottomSheet(
@ -304,15 +304,14 @@ class _CreateSubWorkOrderPageState extends State<CreateSubWorkOrderPage> {
), ),
), ),
), ),
const SizedBox(height: 8), // AMiniOneFilePicker(
AMiniOneFilePicker( // file: _file,
file: _file, // onPick: (file) {
onPick: (file) { // _file = file;
_file = file; // _subWorkOrders.attachmentsWorkOrder ??= [];
_subWorkOrders.attachmentsWorkOrder ??= []; // _subWorkOrders.attachmentsWorkOrder.add(AttachmentsWorkOrder(name: "${file.path.split("/").last}|${base64Encode(file.readAsBytesSync())}"));
_subWorkOrders.attachmentsWorkOrder.add(AttachmentsWorkOrder(name: "${file.path.split("/").last}|${base64Encode(file.readAsBytesSync())}")); // },
}, // ),
),
const SizedBox(height: 50), const SizedBox(height: 50),
], ],
), ),
@ -329,6 +328,10 @@ class _CreateSubWorkOrderPageState extends State<CreateSubWorkOrderPage> {
_validate = true; _validate = true;
setState(() {}); setState(() {});
if (validate()) { if (validate()) {
_subWorkOrders.attachmentsWorkOrder ??= [];
for (var file in _files) {
_subWorkOrders.attachmentsWorkOrder.add(AttachmentsWorkOrder(name: "${file.path.split("/").last}|${base64Encode(file.readAsBytesSync())}"));
}
if (_subWorkOrders.timer?.startAt == null || _subWorkOrders.timer?.endAt == null) { if (_subWorkOrders.timer?.startAt == null || _subWorkOrders.timer?.endAt == null) {
Fluttertoast.showToast(msg: "Working hours required"); Fluttertoast.showToast(msg: "Working hours required");
return; return;

@ -6,6 +6,7 @@ import 'package:test_sa/views/app_style/colors.dart';
import '../../../controllers/localization/localization.dart'; import '../../../controllers/localization/localization.dart';
import '../../../models/subtitle.dart'; import '../../../models/subtitle.dart';
import '../../app_style/sizing.dart'; import '../../app_style/sizing.dart';
import '../../widgets/app_text_form_field.dart';
import '../../widgets/buttons/app_button.dart'; import '../../widgets/buttons/app_button.dart';
import '../../widgets/parts/auto_complete_parts_field.dart'; import '../../widgets/parts/auto_complete_parts_field.dart';
import '../../widgets/parts/part_item.dart'; import '../../widgets/parts/part_item.dart';
@ -101,7 +102,9 @@ class _SparePartsBottomSheetState extends State<SparePartsBottomSheet> {
_workOrder.sparePartsWorkOrders.add(SparePartsWorkOrders( _workOrder.sparePartsWorkOrders.add(SparePartsWorkOrders(
id: part.reportPartID, id: part.reportPartID,
qty: part.quantity, qty: part.quantity,
sparePart: SparePart(id: part.id, partName: part.name, partNo: part.code), sparePart: SparePart(id: part.id, partName: part.partName, partNo: part.partNo),
installQty: part.installQty,
returnQty: part.returnQty,
)); ));
}, },
), ),
@ -129,19 +132,55 @@ class _SparePartsBottomSheetState extends State<SparePartsBottomSheet> {
Part part = Part( Part part = Part(
id: spare.sparePart?.id, id: spare.sparePart?.id,
reportPartID: spare.id, reportPartID: spare.id,
code: spare.sparePart?.partNo, partNo: spare.sparePart?.partNo,
name: spare.sparePart?.partName, partName: spare.sparePart?.partName,
quantity: spare.qty?.toInt(), quantity: spare.qty?.toInt(),
installQty: spare.installQty,
returnQty: spare.returnQty,
); );
return PartItem( return Column(
part: part, children: [
onEdit: (qty) { PartItem(
spare.qty = qty; part: part,
}, onEdit: (qty) {
onDelete: (part) { spare.qty = qty;
_workOrder.sparePartsWorkOrders.remove(spare); },
setState(() {}); onDelete: (part) {
}, _workOrder.sparePartsWorkOrders.remove(spare);
setState(() {});
},
),
if (_workOrder.calllastSituation?.name?.toLowerCase()?.contains("part installation") ?? false)
Row(
children: [
Expanded(
child: ATextFormField(
initialValue: part?.returnQty?.toString(),
labelText: "Return Quantity",
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleMedium,
textInputType: TextInputType.number,
onSaved: (value) {
spare.returnQty = num.tryParse(value)?.toDouble();
},
),
),
const SizedBox(width: 8),
Expanded(
child: ATextFormField(
initialValue: part?.installQty?.toString(),
labelText: "Install Quantity",
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleMedium,
textInputType: TextInputType.number,
onSaved: (value) {
spare.installQty = num.tryParse(value)?.toDouble();
},
),
),
],
),
],
); );
}, },
), ),

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/models/service_request/search_work_order.dart'; import 'package:test_sa/models/service_request/search_work_order.dart';
import 'package:test_sa/models/subtitle.dart'; import 'package:test_sa/models/subtitle.dart';
import 'package:test_sa/views/widgets/app_text_form_field.dart'; import 'package:test_sa/views/widgets/app_text_form_field.dart';
@ -33,10 +34,12 @@ class _WorkOrderDetailsBottomSheetState extends State<WorkOrderDetailsBottomShee
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_workOrder.copyFrom(widget.subWorkOrder); _workOrder.copyFrom(widget.subWorkOrder);
if (_workOrder.calllastSituation?.name?.toLowerCase()?.contains("under repair-vendor") ?? false) { if (checkVendorFieldsVisibility(_workOrder.calllastSituation)) {
_showVendorFields = true; if (_workOrder.suppEngineerWorkOrders?.isNotEmpty ?? false) {
engineer = _workOrder.suppEngineerWorkOrders?.last;
engineer?.id = engineer?.supplierContactId;
}
_workOrder.supplier ??= SupplierModel(id: _workOrder?.supplier?.id); _workOrder.supplier ??= SupplierModel(id: _workOrder?.supplier?.id);
} }
if (context.mounted) { if (context.mounted) {
@ -44,6 +47,14 @@ class _WorkOrderDetailsBottomSheetState extends State<WorkOrderDetailsBottomShee
} }
} }
bool checkVendorFieldsVisibility(Lookup callsLastSituation) {
bool result = (_workOrder.supplier?.suppliername?.isNotEmpty ?? false) ||
(callsLastSituation?.name?.toLowerCase()?.contains("under repair-vendor") ?? false) ||
(callsLastSituation?.name?.toLowerCase()?.contains("waiting for vendor") ?? false);
_showVendorFields = result;
return result;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final size = MediaQuery.of(context).size; final size = MediaQuery.of(context).size;
@ -126,20 +137,17 @@ class _WorkOrderDetailsBottomSheetState extends State<WorkOrderDetailsBottomShee
ServiceReportMaintenanceSituation( ServiceReportMaintenanceSituation(
initialValue: _workOrder.calllastSituation, initialValue: _workOrder.calllastSituation,
onSelect: (status) { onSelect: (status) {
if (status.name.toLowerCase().contains("under repair-vendor") || status.name.toLowerCase().contains("waiting for vendor")) { if (checkVendorFieldsVisibility(status)) {
_showVendorFields = true;
_workOrder.supplier ??= SupplierModel(id: _workOrder?.supplier?.id); _workOrder.supplier ??= SupplierModel(id: _workOrder?.supplier?.id);
engineer = null; engineer = null;
} else {
_showVendorFields = false;
} }
setState(() {});
if (status?.value == 12 || _workOrder.calllastSituation?.value == 12) { if (status?.value == 12 || _workOrder.calllastSituation?.value == 12) {
_workOrder.calllastSituation = status; _workOrder.calllastSituation = status;
setState(() {}); setState(() {});
} else { } else {
_workOrder.calllastSituation = status; _workOrder.calllastSituation = status;
} }
setState(() {});
}, },
woId: widget.subWorkOrder?.parentWOId?.toString(), woId: widget.subWorkOrder?.parentWOId?.toString(),
), ),
@ -158,7 +166,7 @@ class _WorkOrderDetailsBottomSheetState extends State<WorkOrderDetailsBottomShee
if (_showVendorFields) if (_showVendorFields)
SupplierEngineersMenu( SupplierEngineersMenu(
initialValue: engineer, initialValue: engineer,
engineers: _workOrder.supplier.suppPersons, engineers: _workOrder?.supplier?.suppPersons,
onSelect: (engineer) { onSelect: (engineer) {
if (engineer != null) { if (engineer != null) {
this.engineer = engineer; this.engineer = engineer;

@ -65,6 +65,8 @@ class CreateRequestPageState extends State<CreateRequestPage> {
TextEditingController _maintenanceController, _commentController, _reviewCommentController; TextEditingController _maintenanceController, _commentController, _reviewCommentController;
DateTime _dateTime; DateTime _dateTime;
bool isFirstActionSubmitted = false;
@override @override
void initState() { void initState() {
_maintenanceController = TextEditingController(); _maintenanceController = TextEditingController();
@ -74,15 +76,35 @@ class CreateRequestPageState extends State<CreateRequestPage> {
_device = _serviceRequest.device; _device = _serviceRequest.device;
_deviceImages.addAll(_serviceRequest.devicePhotos.map((e) => File(e)).toList()); _deviceImages.addAll(_serviceRequest.devicePhotos.map((e) => File(e)).toList());
_showDatePicker = _serviceRequest.firstAction != null && _serviceRequest.firstAction.name == "Need a visit"; _showDatePicker = _serviceRequest.firstAction != null && _serviceRequest.firstAction.name == "Need a visit";
if (_showDatePicker && _serviceRequest.visitDate != null) {
_dateTime = DateTime.tryParse(_serviceRequest.visitDate);
}
} else { } else {
_serviceRequest = ServiceRequest(); _serviceRequest = ServiceRequest();
} }
isFirstActionSubmitted = _serviceRequest?.firstAction != null;
super.initState(); super.initState();
} }
getServiceRequestById(String id) async { //
await _serviceRequestsProvider.getServiceRequestById(requestId: id) ?? ""; // getServiceRequestById(String id) async {
} // try {
// ServiceRequest request = await _serviceRequestsProvider.getServiceRequestObjectById(requestId: id) ?? "";
// _serviceRequest = request;
// _device = _serviceRequest.device;
// _deviceImages.addAll(_serviceRequest.devicePhotos.map((e) {
// return File(e);
// }).toList());
// _showDatePicker = _serviceRequest.firstAction != null && _serviceRequest.firstAction.name == "Need a visit";
// if (_showDatePicker && _serviceRequest.visitDate != null) {
// _dateTime = DateTime.tryParse(_serviceRequest.visitDate);
// }
// _isLoading = false;
// } catch (ex) {
// _isLoading = false;
// }
// setState(() {});
// }
@override @override
void dispose() { void dispose() {
@ -101,8 +123,8 @@ class CreateRequestPageState extends State<CreateRequestPage> {
_height = MediaQuery.of(context).size.height; _height = MediaQuery.of(context).size.height;
_userProvider = Provider.of<UserProvider>(context); _userProvider = Provider.of<UserProvider>(context);
_settingProvider = Provider.of<SettingProvider>(context); _settingProvider = Provider.of<SettingProvider>(context);
_serviceRequestsProvider = Provider.of<ServiceRequestsProvider>(context);
_subtitle = AppLocalization.of(context).subtitle; _subtitle = AppLocalization.of(context).subtitle;
_serviceRequestsProvider = Provider.of<ServiceRequestsProvider>(context);
return Scaffold( return Scaffold(
key: _scaffoldKey, key: _scaffoldKey,
@ -255,6 +277,7 @@ class CreateRequestPageState extends State<CreateRequestPage> {
if (widget.serviceRequest != null) if (widget.serviceRequest != null)
ServiceRequestedFirstAction( ServiceRequestedFirstAction(
initialValue: _serviceRequest.firstAction, initialValue: _serviceRequest.firstAction,
enabled: !isFirstActionSubmitted,
onSelect: (status) { onSelect: (status) {
_dateTime = null; _dateTime = null;
_serviceRequest.firstAction = status; _serviceRequest.firstAction = status;
@ -266,6 +289,7 @@ class CreateRequestPageState extends State<CreateRequestPage> {
if (_showDatePicker) if (_showDatePicker)
ADatePicker( ADatePicker(
date: _dateTime, date: _dateTime,
enable: !isFirstActionSubmitted,
from: DateTime.now(), from: DateTime.now(),
onDatePicker: (date) { onDatePicker: (date) {
_dateTime = date; _dateTime = date;
@ -378,7 +402,7 @@ class CreateRequestPageState extends State<CreateRequestPage> {
_serviceRequest.requestedThrough = Provider.of<ServiceRequestedThroughProvider>(context, listen: false).getDefaultItem(); _serviceRequest.requestedThrough = Provider.of<ServiceRequestedThroughProvider>(context, listen: false).getDefaultItem();
_isLoading = true; _isLoading = true;
setState(() {}); setState(() {});
_serviceRequest.devicePhotos = _deviceImages.map((e) => "${e.path.split("/").last}|${base64Encode(e.readAsBytesSync())}").toList(); _serviceRequest.devicePhotos = _deviceImages.map((e) => _isLocalUrl(e.path) ? "${e.path.split("/").last}|${base64Encode(e.readAsBytesSync())}" : e.path).toList();
if (_serviceRequest.audio != null) { if (_serviceRequest.audio != null) {
if (_isLocalUrl(_serviceRequest.audio)) { if (_isLocalUrl(_serviceRequest.audio)) {
final File file = File(_serviceRequest.audio); final File file = File(_serviceRequest.audio);

@ -24,7 +24,6 @@ import 'package:test_sa/views/widgets/buttons/app_back_button.dart';
import 'package:test_sa/views/widgets/buttons/app_button.dart'; import 'package:test_sa/views/widgets/buttons/app_button.dart';
import 'package:test_sa/views/widgets/e_signature/e_signature.dart'; import 'package:test_sa/views/widgets/e_signature/e_signature.dart';
import 'package:test_sa/views/widgets/equipment/auto_complete_devices_field.dart'; import 'package:test_sa/views/widgets/equipment/auto_complete_devices_field.dart';
import 'package:test_sa/views/widgets/images/mini_one_file_picker.dart';
import 'package:test_sa/views/widgets/loaders/loading_manager.dart'; import 'package:test_sa/views/widgets/loaders/loading_manager.dart';
import 'package:test_sa/views/widgets/parts/auto_complete_parts_field.dart'; import 'package:test_sa/views/widgets/parts/auto_complete_parts_field.dart';
import 'package:test_sa/views/widgets/parts/part_item.dart'; import 'package:test_sa/views/widgets/parts/part_item.dart';
@ -35,6 +34,7 @@ import 'package:test_sa/views/widgets/titles/app_sub_title.dart';
import '../../../../../controllers/providers/api/status_drop_down/report/service_types_provider.dart'; import '../../../../../controllers/providers/api/status_drop_down/report/service_types_provider.dart';
import '../../../../widgets/date_and_time/date_picker.dart'; import '../../../../widgets/date_and_time/date_picker.dart';
import '../../../../widgets/images/multi_image_picker.dart';
import '../../../../widgets/status/report/service_report_fault_description.dart'; import '../../../../widgets/status/report/service_report_fault_description.dart';
import '../../../../widgets/status/report/service_report_repair_location.dart'; import '../../../../widgets/status/report/service_report_repair_location.dart';
import '../../../../widgets/timer/app_timer.dart'; import '../../../../widgets/timer/app_timer.dart';
@ -60,7 +60,7 @@ class _CreateServiceReportState extends State<CreateServiceReport> with TickerPr
bool _showCommentField = false; bool _showCommentField = false;
Subtitle _subtitle; Subtitle _subtitle;
File _file; final List<File> _files = [];
final GlobalKey<FormState> _formKey = GlobalKey<FormState>(); final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final TextEditingController _faultController = TextEditingController(); final TextEditingController _faultController = TextEditingController();
@ -547,8 +547,10 @@ class _CreateServiceReportState extends State<CreateServiceReport> with TickerPr
initialValue: _serviceReport.faultDescription, initialValue: _serviceReport.faultDescription,
enabled: false, enabled: false,
onSelect: (status) { onSelect: (status) {
_serviceReport.faultDescription = status; if (status != null) {
setState(() {}); _serviceReport.faultDescription = status;
setState(() {});
}
}, },
), ),
const SizedBox( const SizedBox(
@ -628,25 +630,27 @@ class _CreateServiceReportState extends State<CreateServiceReport> with TickerPr
), ),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
Expanded( // Expanded(
child: Column( // child: Column(
crossAxisAlignment: CrossAxisAlignment.start, // crossAxisAlignment: CrossAxisAlignment.start,
children: [ // children: [
const ASubTitle("Attachment"), // const ASubTitle("Attachments"),
AMiniOneFilePicker( // AMiniOneFilePicker(
//error: _validate && _serviceReport.image == null, // //error: _validate && _serviceReport.image == null,
file: _file, // file: _file,
onPick: (file) { // onPick: (file) {
_file = file; // _file = file;
_serviceReport.files[0] = "${file.path.split("/").last}|${base64Encode(file.readAsBytesSync())}"; // _serviceReport.files[0] = "${file.path.split("/").last}|${base64Encode(file.readAsBytesSync())}";
}, // },
), // ),
], // ],
), // ),
), // ),
], ],
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
MultiFilesPicker(label: "Attachments", files: _files),
const SizedBox(height: 8),
ASubTitle(_subtitle.repairLocation), ASubTitle(_subtitle.repairLocation),
const SizedBox(height: 4), const SizedBox(height: 4),
ServiceReportRepairLocation( ServiceReportRepairLocation(
@ -924,7 +928,10 @@ class _CreateServiceReportState extends State<CreateServiceReport> with TickerPr
_isLoading = true; _isLoading = true;
setState(() {}); setState(() {});
_serviceReport.files ??= [];
for (var file in _files) {
_serviceReport.files.add("${file.path.split("/").last}|${base64Encode(file.readAsBytesSync())}");
}
int status = await _serviceRequestsProvider.createServiceReport( int status = await _serviceRequestsProvider.createServiceReport(
user: _userProvider.user, user: _userProvider.user,
host: _settingProvider.host, host: _settingProvider.host,

@ -61,7 +61,7 @@ class _EditServiceReportState extends State<EditServiceReport> with TickerProvid
bool _showCommentField = false; bool _showCommentField = false;
Subtitle _subtitle; Subtitle _subtitle;
final List<File> _images = []; List<File> _images = [];
final GlobalKey<FormState> _formKey = GlobalKey<FormState>(); final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
TextEditingController _faultController = TextEditingController(); TextEditingController _faultController = TextEditingController();
@ -74,6 +74,7 @@ class _EditServiceReportState extends State<EditServiceReport> with TickerProvid
@override @override
void initState() { void initState() {
_serviceReport = widget.report; _serviceReport = widget.report;
_images = widget.report?.files?.map((e) => File(e))?.toList();
super.initState(); super.initState();
} }
@ -1070,7 +1071,7 @@ class _EditServiceReportState extends State<EditServiceReport> with TickerProvid
_isLoading = true; _isLoading = true;
setState(() {}); setState(() {});
_serviceReport.files = _images.map((e) => base64Encode(e.readAsBytesSync())).toList(); _serviceReport.files = _images.map((e) => _isLocalUrl(e.path) ? "${e.path.split("/").last}|${base64Encode(e.readAsBytesSync())}" : e.path).toList();
int status = await _serviceRequestsProvider.updateServiceReport(user: _userProvider.user, host: _settingProvider.host, report: _serviceReport, request: widget.request); int status = await _serviceRequestsProvider.updateServiceReport(user: _userProvider.user, host: _settingProvider.host, report: _serviceReport, request: widget.request);
_isLoading = false; _isLoading = false;
setState(() {}); setState(() {});
@ -1098,4 +1099,9 @@ class _EditServiceReportState extends State<EditServiceReport> with TickerProvid
), ),
); );
} }
bool _isLocalUrl(String url) {
if (url?.isEmpty != false) return false;
return url.startsWith("/") || url.startsWith("file://") || url.substring(1).startsWith(':\\');
}
} }

@ -6,6 +6,7 @@ import 'package:test_sa/controllers/providers/api/service_requests_provider.dart
import 'package:test_sa/controllers/providers/api/user_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/controllers/providers/settings/setting_provider.dart';
import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/models/enums/user_types.dart'; import 'package:test_sa/models/enums/user_types.dart';
import 'package:test_sa/models/service_request/service_request.dart'; import 'package:test_sa/models/service_request/service_request.dart';
import 'package:test_sa/models/subtitle.dart'; import 'package:test_sa/models/subtitle.dart';
@ -16,7 +17,9 @@ import 'package:test_sa/views/pages/user/requests/report/create_service_report.d
import 'package:test_sa/views/widgets/buttons/app_back_button.dart'; import 'package:test_sa/views/widgets/buttons/app_back_button.dart';
import 'package:test_sa/views/widgets/buttons/app_button.dart'; import 'package:test_sa/views/widgets/buttons/app_button.dart';
import 'package:test_sa/views/widgets/buttons/app_icon_button.dart'; import 'package:test_sa/views/widgets/buttons/app_icon_button.dart';
import 'package:test_sa/views/widgets/images/files_list.dart';
import 'package:test_sa/views/widgets/images/images_list.dart'; import 'package:test_sa/views/widgets/images/images_list.dart';
import 'package:test_sa/views/widgets/loaders/app_loading.dart';
import 'package:test_sa/views/widgets/loaders/image_loader.dart'; import 'package:test_sa/views/widgets/loaders/image_loader.dart';
import 'package:test_sa/views/widgets/requests/info_row.dart'; import 'package:test_sa/views/widgets/requests/info_row.dart';
import 'package:test_sa/views/widgets/requests/request_status.dart'; import 'package:test_sa/views/widgets/requests/request_status.dart';
@ -28,9 +31,9 @@ import 'report/future_service_report.dart';
class RequestDetailsPage extends StatelessWidget { class RequestDetailsPage extends StatelessWidget {
static const String id = "/call-details"; static const String id = "/call-details";
final ServiceRequest serviceRequest; ServiceRequest serviceRequest;
const RequestDetailsPage({Key key, this.serviceRequest}) : super(key: key); RequestDetailsPage({Key key, this.serviceRequest}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -39,404 +42,423 @@ class RequestDetailsPage extends StatelessWidget {
SettingProvider _settingProvider = Provider.of<SettingProvider>(context); SettingProvider _settingProvider = Provider.of<SettingProvider>(context);
List<SearchWorkOrder> workOrders = []; List<SearchWorkOrder> workOrders = [];
ServiceRequestsProvider _serviceRequestsProvider = Provider.of<ServiceRequestsProvider>(context); ServiceRequestsProvider _serviceRequestsProvider = Provider.of<ServiceRequestsProvider>(context);
_serviceRequestsProvider.getServiceRequestObjectById(requestId: serviceRequest.id);
return DefaultTabController( return DefaultTabController(
length: 2, length: 2,
child: Scaffold( child: Scaffold(
body: SafeArea( body: SafeArea(
child: Column( child: FutureBuilder(
children: [ future: _serviceRequestsProvider.getServiceRequestObjectById(requestId: serviceRequest.id),
Container( builder: (context, snap) {
color: AColors.primaryColor, if (snap.connectionState == ConnectionState.waiting) {
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 4), return const ALoading();
child: Row( } else if (snap.hasData) {
children: [ serviceRequest = snap.data;
ABackButton(), return Column(
Expanded( children: [
child: Center( Container(
child: Text( color: AColors.primaryColor,
_subtitle.details, padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 4),
style: Theme.of(context).textTheme.headline6.copyWith(color: AColors.white, fontStyle: FontStyle.italic), child: Row(
),
),
),
_userProvider.user.type == UsersTypes.normal_user
?
// AIconButton(
// iconData: Icons.warning_amber_rounded,
// color: AColors.white,
// buttonSize: 42,
// backgroundColor: AColors.deepOrange,
// onPressed: (){
// Navigator.of(context).push(
// MaterialPageRoute(
// builder: (_) => ReportIssuesPage(serviceRequest: serviceRequest,)
// )
// );
// },
// )
const SizedBox(
width: 48,
)
: AIconButton(
iconData: Icons.edit,
color: AColors.white,
buttonSize: 42,
backgroundColor: AColors.green,
onPressed: () async {
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => CreateRequestPage(serviceRequest: serviceRequest)),
);
// showModalBottomSheet(
// context: context,
// builder: (context) {
// return ServiceRequestsUpdateDialog(
// request: serviceRequest,
// );
// });
// DateTime picked = await showDatePicker(
// context: context,
// initialDate: DateTime.now(),
// firstDate: DateTime.now(),
// lastDate: DateTime.now().add(Duration(days: 182))
// );
// if(picked == null){return;}
// showDialog<void>(
// context: context,
// barrierDismissible: false,
// builder: (BuildContext context) {
// return CupertinoAlertDialog(
// title: Text(_subtitle.updatingDots),
// content: Center(child: CircularProgressIndicator()),
// );
// },
// );
// int status = await _serviceRequestsProvider.updateDate(
// user: _userProvider.user,
// host: _settingProvider.host,
// request: serviceRequest,
// newDate: picked.toString().split(" ").first
// );
// Navigator.of(context).pop();
// Fluttertoast.showToast(
// msg: HttpStatusManger.getStatusMessage(status: status, subtitle: _subtitle),
// );
},
),
SizedBox(
width: 16,
)
],
),
),
serviceRequest.devicePhotos.isEmpty
? SizedBox.shrink()
: Column(
children: [
SizedBox(
height: 8,
),
MaterialButton(
padding: EdgeInsets.zero,
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => Scaffold(
body: InteractiveViewer(
child: Center(
child: ImageLoader(
url: serviceRequest.devicePhotos.first,
boxFit: BoxFit.contain,
),
),
),
)));
},
child: SizedBox(
height: 140 * AppStyle.getScaleFactor(context),
width: MediaQuery.of(context).size.width,
child: ImageLoader(
url: serviceRequest.devicePhotos.first,
boxFit: BoxFit.cover,
),
),
),
SizedBox(height: 8),
SizedBox(
height: 60 * AppStyle.getScaleFactor(context),
child: ImagesList(
images: serviceRequest.devicePhotos,
),
),
],
),
TabBar(labelColor: AColors.primaryColor, tabs: [
Tab(
text: _subtitle.general,
),
Tab(
text: _subtitle.serviceRequestInformation,
),
]),
SizedBox(
height: 8,
),
Expanded(
child: TabBarView(
children: [
ListView(
padding: EdgeInsets.symmetric(horizontal: 16),
children: [
RequestInfoRow(
title: _subtitle.callId,
info: serviceRequest.requestCode,
),
RequestInfoRow(
title: "Asset Number",
info: serviceRequest.deviceNumber,
),
RequestInfoRow(
title: _subtitle.assetName,
info: serviceRequest.deviceEnName,
),
RequestInfoRow(
title: _subtitle.deviceModel,
info: serviceRequest.deviceModel,
),
RequestInfoRow(
title: _subtitle.engineerName,
info: serviceRequest.engineerName,
),
RequestInfoRow(
title: _subtitle.engineerPhone,
info: serviceRequest.engineerMobile,
),
RequestInfoRow(
title: _subtitle.date,
info: serviceRequest.date,
),
serviceRequest.nextVisitDate == null
? SizedBox.shrink()
: RequestInfoRow(
title: _subtitle.nextVisitDate,
info: DateFormat('EE dd/MM/yyyy').format(serviceRequest.nextVisitDate),
),
Row(
children: [ children: [
ABackButton(),
Expanded( Expanded(
child: Text( child: Center(
"${_subtitle.status} : ", child: Text(
style: Theme.of(context).textTheme.subtitle1, _subtitle.details,
textScaleFactor: AppStyle.getScaleFactor(context), style: Theme.of(context).textTheme.headline6.copyWith(color: AColors.white, fontStyle: FontStyle.italic),
),
), ),
), ),
StatusLabel(label: serviceRequest.statusLabel, color: AColors.getRequestStatusColor(serviceRequest.statusValue)), _userProvider.user.type == UsersTypes.normal_user
?
// AIconButton(
// iconData: Icons.warning_amber_rounded,
// color: AColors.white,
// buttonSize: 42,
// backgroundColor: AColors.deepOrange,
// onPressed: (){
// Navigator.of(context).push(
// MaterialPageRoute(
// builder: (_) => ReportIssuesPage(serviceRequest: serviceRequest,)
// )
// );
// },
// )
const SizedBox(
width: 48,
)
: AIconButton(
iconData: Icons.edit,
color: AColors.white,
buttonSize: 42,
backgroundColor: AColors.green,
onPressed: () async {
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => CreateRequestPage(serviceRequest: serviceRequest)),
);
// showModalBottomSheet(
// context: context,
// builder: (context) {
// return ServiceRequestsUpdateDialog(
// request: serviceRequest,
// );
// });
// DateTime picked = await showDatePicker(
// context: context,
// initialDate: DateTime.now(),
// firstDate: DateTime.now(),
// lastDate: DateTime.now().add(Duration(days: 182))
// );
// if(picked == null){return;}
// showDialog<void>(
// context: context,
// barrierDismissible: false,
// builder: (BuildContext context) {
// return CupertinoAlertDialog(
// title: Text(_subtitle.updatingDots),
// content: Center(child: CircularProgressIndicator()),
// );
// },
// );
// int status = await _serviceRequestsProvider.updateDate(
// user: _userProvider.user,
// host: _settingProvider.host,
// request: serviceRequest,
// newDate: picked.toString().split(" ").first
// );
// Navigator.of(context).pop();
// Fluttertoast.showToast(
// msg: HttpStatusManger.getStatusMessage(status: status, subtitle: _subtitle),
// );
},
),
SizedBox(
width: 16,
)
], ],
), ),
Divider( ),
color: Theme.of(context).primaryColor, serviceRequest.devicePhotos.isEmpty
), ? const SizedBox.shrink()
RequestInfoRow( : SizedBox(
title: _subtitle.hospital, height: 60 * AppStyle.getScaleFactor(context),
info: serviceRequest.hospitalName, child: FilesList(
), images: serviceRequest.devicePhotos,
RequestInfoRow( ),
title: _subtitle.unite, ).paddingOnly(top: 4, bottom: 4),
info: serviceRequest.departmentName,
// Column(
// children: [
// SizedBox(
// height: 8,
// ),
// MaterialButton(
// padding: EdgeInsets.zero,
// onPressed: () {
// Navigator.of(context).push(MaterialPageRoute(
// builder: (_) => Scaffold(
// body: InteractiveViewer(
// child: Center(
// child: ImageLoader(
// url: serviceRequest.devicePhotos.first,
// boxFit: BoxFit.contain,
// ),
// ),
// ),
// )));
// },
// child: SizedBox(
// height: 140 * AppStyle.getScaleFactor(context),
// width: MediaQuery.of(context).size.width,
// child: ImageLoader(
// url: serviceRequest.devicePhotos.first,
// boxFit: BoxFit.cover,
// ),
// ),
// ),
// SizedBox(height: 8),
// SizedBox(
// height: 60 * AppStyle.getScaleFactor(context),
// child: FilesList(
// images: serviceRequest.devicePhotos,
// ),
// ),
// ],
// ),
TabBar(labelColor: AColors.primaryColor, tabs: [
Tab(
text: _subtitle.general,
), ),
// RequestInfoRow( Tab(
// title: _subtitle.deviceArName, text: _subtitle.serviceRequestInformation,
// content: serviceRequest.deviceArName,
// ),
// RequestInfoRow(
// title: _subtitle.deviceName,
// content: serviceRequest.deviceEnName,
// ),
RequestInfoRow(
title: _subtitle.maintenanceIssue,
content: serviceRequest.callComments,
), ),
if (serviceRequest.audio?.isNotEmpty == true) ]),
ASoundPlayer( SizedBox(
audio: serviceRequest.audio, height: 8,
), ),
// Expanded(
// Center( child: TabBarView(
// child: Padding( children: [
// padding: EdgeInsets.all(32), ListView(
// child: AButton( padding: EdgeInsets.symmetric(horizontal: 16),
// text: _subtitle.duplicateRequest,
// onPressed: () async {
// bool result = await showDialog(
// context: context,
// builder: (_) => AAlertDialog(
// title: _subtitle.duplicateAlert,
// content: _subtitle.duplicateAlertMessage,
// )
// );
// if(result == true){
// showDialog(
// context: context,
// builder: (context){
// return Center(child: CircularProgressIndicator());
// }
// );
// int status = await _serviceRequestsProvider.createDuplicatedReport(
// host: _settingProvider.host,
// user: _userProvider.user,
// request: serviceRequest
// );
// Navigator.of(context).pop();
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(
// content: Text(
// HttpStatusManger.getStatusMessage(status: status, subtitle: _subtitle)
// )
// )
// );
// }
// },
// ),
// ),
// )
],
),
// workOrders.isEmpty ?
FutureBuilder(
future: _serviceRequestsProvider.searchWorkOrders(callId: serviceRequest.requestCode),
builder: (context, snap) {
workOrders = snap.data as List<SearchWorkOrder>;
if (snap.connectionState == ConnectionState.waiting) return Center(child: CircularProgressIndicator());
if (snap.connectionState == ConnectionState.done && (snap.data?.length ?? 0) != 0) {
return SingleChildScrollView(
child: Column(
children: [ children: [
ListView.separated( RequestInfoRow(
shrinkWrap: true, title: _subtitle.callId,
physics: const NeverScrollableScrollPhysics(), info: serviceRequest.requestCode,
padding: EdgeInsets.all(21), ),
itemCount: workOrders.length, RequestInfoRow(
separatorBuilder: (czt, index) => 21.height, title: "Asset Number",
itemBuilder: (context, index) { info: serviceRequest.deviceNumber,
Color itemColor = index % 2 == 0 ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.onPrimary; ),
Color onItemColor = index % 2 != 0 ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.onPrimary; RequestInfoRow(
title: _subtitle.assetName,
return ElevatedButton( info: serviceRequest.deviceEnName,
style: ElevatedButton.styleFrom( ),
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 8), RequestInfoRow(
primary: itemColor.withOpacity(.7), title: _subtitle.deviceModel,
shape: RoundedRectangleBorder( info: serviceRequest.deviceModel,
borderRadius: BorderRadius.circular(AppStyle.getBorderRadius(context)),
),
),
//padding: EdgeInsets.symmetric(vertical: 8,horizontal: 8),
onPressed: () {
// onPressed(request);
},
child: Column(
children: [
RequestInfoRow(
title: _subtitle.callId,
content: serviceRequest.requestCode,
),
RequestInfoRow(
title: _subtitle.orderWorkNumber,
info: workOrders[index].workOrderNo.toString(),
),
RequestInfoRow(
title: _subtitle.visitDate,
info: workOrders[index].visitDate,
),
RequestInfoRow(title: _subtitle.assignedEmployee, info: workOrders[index].assignedEmployee?.name ?? ""),
RequestInfoRow(
title: _subtitle.assetSN,
info: workOrders[index].callRequest.asset.assetSerialNo,
),
RequestInfoRow(
title: _subtitle.assetName,
info: workOrders[index].callRequest.asset.modelDefinition.assetName,
),
RequestInfoRow(
title: _subtitle.assetNumber,
info: workOrders[index].callRequest.asset.assetNumber,
),
RequestInfoRow(
title: _subtitle.model,
info: workOrders[index].callRequest.asset.modelDefinition.modelName,
),
RequestInfoRow(
title: _subtitle.site,
info: workOrders[index].callRequest.asset.site.custName,
),
RequestInfoRow(
title: "Call last Situation",
info: workOrders[index].currentSituation.name ?? '',
),
_userProvider.user.type == UsersTypes.engineer && workOrders[index].workOrderNo != null
? Padding(
padding: EdgeInsets.all(32),
child: AButton(
text: _subtitle.editServiceReport,
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => FutureServiceReport(
request: serviceRequest,
workOrder: workOrders[index],
)),
);
},
),
)
: SizedBox.shrink(),
],
),
);
},
), ),
if (_userProvider.user.type == UsersTypes.engineer) RequestInfoRow(
Center( title: _subtitle.engineerName,
child: Padding( info: serviceRequest.engineerName,
padding: const EdgeInsets.all(32), ),
child: AButton( RequestInfoRow(
text: "Create Work Order", title: _subtitle.engineerPhone,
onPressed: () { info: serviceRequest.engineerMobile,
Navigator.of(context).push(MaterialPageRoute(builder: (_) => CreateServiceReport(request: serviceRequest))); ),
}, RequestInfoRow(
title: _subtitle.date,
info: serviceRequest.date,
),
serviceRequest.nextVisitDate == null
? SizedBox.shrink()
: RequestInfoRow(
title: _subtitle.nextVisitDate,
info: DateFormat('EE dd/MM/yyyy').format(serviceRequest.nextVisitDate),
),
Row(
children: [
Expanded(
child: Text(
"${_subtitle.status} : ",
style: Theme.of(context).textTheme.subtitle1,
textScaleFactor: AppStyle.getScaleFactor(context),
), ),
), ),
StatusLabel(label: serviceRequest.statusLabel, color: AColors.getRequestStatusColor(serviceRequest.statusValue)),
],
),
Divider(
color: Theme.of(context).primaryColor,
),
RequestInfoRow(
title: _subtitle.hospital,
info: serviceRequest.hospitalName,
),
RequestInfoRow(
title: _subtitle.unite,
info: serviceRequest.departmentName,
),
// RequestInfoRow(
// title: _subtitle.deviceArName,
// content: serviceRequest.deviceArName,
// ),
// RequestInfoRow(
// title: _subtitle.deviceName,
// content: serviceRequest.deviceEnName,
// ),
RequestInfoRow(
title: _subtitle.maintenanceIssue,
content: serviceRequest.callComments,
),
if (serviceRequest.audio?.isNotEmpty == true)
ASoundPlayer(
audio: serviceRequest.audio,
), ),
//
// Center(
// child: Padding(
// padding: EdgeInsets.all(32),
// child: AButton(
// text: _subtitle.duplicateRequest,
// onPressed: () async {
// bool result = await showDialog(
// context: context,
// builder: (_) => AAlertDialog(
// title: _subtitle.duplicateAlert,
// content: _subtitle.duplicateAlertMessage,
// )
// );
// if(result == true){
// showDialog(
// context: context,
// builder: (context){
// return Center(child: CircularProgressIndicator());
// }
// );
// int status = await _serviceRequestsProvider.createDuplicatedReport(
// host: _settingProvider.host,
// user: _userProvider.user,
// request: serviceRequest
// );
// Navigator.of(context).pop();
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(
// content: Text(
// HttpStatusManger.getStatusMessage(status: status, subtitle: _subtitle)
// )
// )
// );
// }
// },
// ),
// ),
// )
], ],
), ),
); // workOrders.isEmpty ?
} else { FutureBuilder(
return Column( future: _serviceRequestsProvider.searchWorkOrders(callId: serviceRequest.requestCode),
children: [ builder: (context, snap) {
Expanded( workOrders = snap.data as List<SearchWorkOrder>;
child: Center( if (snap.connectionState == ConnectionState.waiting) return Center(child: CircularProgressIndicator());
child: ASubTitle(_subtitle.dataNotFound), if (snap.connectionState == ConnectionState.done && (snap.data?.length ?? 0) != 0) {
), return SingleChildScrollView(
), child: Column(
if (_userProvider.user.type == UsersTypes.engineer) children: [
Center( ListView.separated(
child: Padding( shrinkWrap: true,
padding: const EdgeInsets.all(32), physics: const NeverScrollableScrollPhysics(),
child: AButton( padding: EdgeInsets.all(21),
text: "Create Work Order", itemCount: workOrders.length,
onPressed: () { separatorBuilder: (czt, index) => 21.height,
Navigator.of(context).push(MaterialPageRoute(builder: (_) => CreateServiceReport(request: serviceRequest))); itemBuilder: (context, index) {
}, Color itemColor = index % 2 == 0 ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.onPrimary;
Color onItemColor = index % 2 != 0 ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.onPrimary;
return ElevatedButton(
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 8),
primary: itemColor.withOpacity(.7),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppStyle.getBorderRadius(context)),
),
),
//padding: EdgeInsets.symmetric(vertical: 8,horizontal: 8),
onPressed: () {
// onPressed(request);
},
child: Column(
children: [
RequestInfoRow(
title: _subtitle.callId,
content: serviceRequest.requestCode,
),
RequestInfoRow(
title: _subtitle.orderWorkNumber,
info: workOrders[index].workOrderNo.toString(),
),
RequestInfoRow(
title: _subtitle.visitDate,
info: workOrders[index].visitDate,
),
RequestInfoRow(title: _subtitle.assignedEmployee, info: workOrders[index].assignedEmployee?.name ?? ""),
RequestInfoRow(
title: _subtitle.assetSN,
info: workOrders[index].callRequest.asset.assetSerialNo,
),
RequestInfoRow(
title: _subtitle.assetName,
info: workOrders[index].callRequest.asset.modelDefinition.assetName,
),
RequestInfoRow(
title: _subtitle.assetNumber,
info: workOrders[index].callRequest.asset.assetNumber,
),
RequestInfoRow(
title: _subtitle.model,
info: workOrders[index].callRequest.asset.modelDefinition.modelName,
),
RequestInfoRow(
title: _subtitle.site,
info: workOrders[index].callRequest.asset.site.custName,
),
RequestInfoRow(
title: "Call last Situation",
info: workOrders[index].currentSituation.name ?? '',
),
_userProvider.user.type == UsersTypes.engineer && workOrders[index].workOrderNo != null
? Padding(
padding: EdgeInsets.all(32),
child: AButton(
text: _subtitle.editServiceReport,
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => FutureServiceReport(
request: serviceRequest,
workOrder: workOrders[index],
)),
);
},
),
)
: SizedBox.shrink(),
],
),
);
},
),
if (_userProvider.user.type == UsersTypes.engineer)
Center(
child: Padding(
padding: const EdgeInsets.all(32),
child: AButton(
text: "Create Work Order",
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(builder: (_) => CreateServiceReport(request: serviceRequest)));
},
),
),
),
],
), ),
), );
), } else {
], return Column(
); children: [
} Expanded(
}, child: Center(
), child: ASubTitle(_subtitle.dataNotFound),
], ),
), ),
), if (_userProvider.user.type == UsersTypes.engineer)
], Center(
), child: Padding(
padding: const EdgeInsets.all(32),
child: AButton(
text: "Create Work Order",
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(builder: (_) => CreateServiceReport(request: serviceRequest)));
},
),
),
),
],
);
}
},
),
],
),
),
],
);
}
return const SizedBox();
}),
), ),
), ),
); );

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:test_sa/controllers/localization/localization.dart'; import 'package:test_sa/controllers/localization/localization.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/models/subtitle.dart'; import 'package:test_sa/models/subtitle.dart';
import 'package:test_sa/models/visits/visit.dart'; import 'package:test_sa/models/visits/visit.dart';
import 'package:test_sa/views/app_style/colors.dart'; import 'package:test_sa/views/app_style/colors.dart';
@ -8,19 +9,21 @@ import 'package:test_sa/views/app_style/sizing.dart';
import 'package:test_sa/views/pages/user/visits/pantry/edit_pentry.dart'; import 'package:test_sa/views/pages/user/visits/pantry/edit_pentry.dart';
import 'package:test_sa/views/widgets/buttons/app_back_button.dart'; import 'package:test_sa/views/widgets/buttons/app_back_button.dart';
import 'package:test_sa/views/widgets/buttons/app_icon_button.dart'; import 'package:test_sa/views/widgets/buttons/app_icon_button.dart';
import 'package:test_sa/views/widgets/images/files_list.dart';
import 'package:test_sa/views/widgets/images/images_list.dart'; import 'package:test_sa/views/widgets/images/images_list.dart';
import 'package:test_sa/views/widgets/loaders/image_loader.dart';
import 'package:test_sa/views/widgets/requests/info_row.dart'; import 'package:test_sa/views/widgets/requests/info_row.dart';
import 'package:test_sa/views/widgets/visits/visit_status.dart'; import 'package:test_sa/views/widgets/visits/visit_status.dart';
import '../../../../controllers/providers/api/user_provider.dart'; import '../../../../controllers/providers/api/user_provider.dart';
import '../../../../models/enums/user_types.dart'; import '../../../../models/enums/user_types.dart';
import '../../../widgets/loaders/image_loader.dart';
class VisitDetailsPage extends StatelessWidget { class VisitDetailsPage extends StatelessWidget {
static const String id = "/visit-details"; static const String id = "/visit-details";
final Visit visit; final Visit visit;
const VisitDetailsPage({Key key, this.visit}) : super(key: key); const VisitDetailsPage({Key key, this.visit}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Subtitle subtitle = AppLocalization.of(context).subtitle; Subtitle subtitle = AppLocalization.of(context).subtitle;
@ -51,11 +54,14 @@ class VisitDetailsPage extends StatelessWidget {
buttonSize: 42, buttonSize: 42,
backgroundColor: AColors.green, backgroundColor: AColors.green,
onPressed: () async { onPressed: () async {
Navigator.of(context).push(MaterialPageRoute( Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => EditPentry( builder: (_) => EditPentry(
visit: visit, visit: visit,
pentry: visit.pentry, pentry: visit.pentry,
))); ),
),
);
}, },
), ),
const SizedBox(width: 42) const SizedBox(width: 42)
@ -66,44 +72,46 @@ class VisitDetailsPage extends StatelessWidget {
child: ListView( child: ListView(
padding: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 16),
children: [ children: [
const SizedBox( const SizedBox(height: 8),
height: 8, // MaterialButton(
), // padding: EdgeInsets.zero,
MaterialButton( // onPressed: () {
padding: EdgeInsets.zero, // if ((visit.images?.isNotEmpty ?? false) && (visit.images?.first?.isNotEmpty ?? false)) {
onPressed: () { // Navigator.of(context).push(
if (visit.images.isEmpty || (visit.images?.first?.isEmpty ?? false)) { // MaterialPageRoute(
Navigator.of(context).push(MaterialPageRoute( // builder: (_) => Scaffold(
builder: (_) => Scaffold( // body: InteractiveViewer(
body: InteractiveViewer( // child: Center(
child: Center( // child: ImageLoader(
child: ImageLoader( // url: visit.images.first,
url: visit.images.first, // boxFit: BoxFit.contain,
boxFit: BoxFit.contain, // ),
), // ),
), // ),
), // ),
))); // ),
} // );
}, // }
child: SizedBox( // },
height: 140 * AppStyle.getScaleFactor(context), // child: SizedBox(
width: MediaQuery.of(context).size.width, // height: 140 * AppStyle.getScaleFactor(context),
child: ImageLoader( // width: MediaQuery.of(context).size.width,
url: visit.images.isEmpty ? " " : visit.images.first, // child: ImageLoader(
boxFit: BoxFit.cover, // url: visit.images.isEmpty ? " " : visit.images.first,
// boxFit: BoxFit.cover,
// ),
// ),
// ),
// const SizedBox(
// height: 8,
// ),
if (visit?.images?.isNotEmpty ?? false)
SizedBox(
height: 60 * AppStyle.getScaleFactor(context),
child: FilesList(
images: visit.images,
), ),
), ).paddingOnly(top: 4, bottom: 4),
),
const SizedBox(
height: 8,
),
SizedBox(
height: 60 * AppStyle.getScaleFactor(context),
child: ImagesList(
images: visit.images,
),
),
const SizedBox( const SizedBox(
height: 8, height: 8,
), ),

@ -6,8 +6,9 @@ class ADatePicker extends StatelessWidget {
final DateTime from; final DateTime from;
final DateTime to; final DateTime to;
final Function(DateTime) onDatePicker; final Function(DateTime) onDatePicker;
final bool enable;
const ADatePicker({Key key, this.date, this.onDatePicker, this.from, this.to}) : super(key: key); const ADatePicker({Key key, this.date, this.onDatePicker, this.from, this.to, this.enable = true}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -19,14 +20,16 @@ class ADatePicker extends StatelessWidget {
borderRadius: BorderRadius.circular(12 * AppStyle.getScaleFactor(context)), borderRadius: BorderRadius.circular(12 * AppStyle.getScaleFactor(context)),
), ),
), ),
onPressed: enable
? () async {
DateTime picked = await showDatePicker(context: context, initialDate: DateTime.now(), firstDate: from ?? DateTime.now(), lastDate: to ?? DateTime.now().add(Duration(days: 365)));
onDatePicker(picked);
}
: null,
child: Text( child: Text(
date == null ? "Pick Date" : date.toString().split(" ").first, date == null ? "Pick Date" : date.toString().split(" ").first,
textScaleFactor: AppStyle.getScaleFactor(context), textScaleFactor: AppStyle.getScaleFactor(context),
), ),
onPressed: () async {
DateTime picked = await showDatePicker(context: context, initialDate: DateTime.now(), firstDate: from ?? DateTime.now(), lastDate: to ?? DateTime.now().add(Duration(days: 365)));
onDatePicker(picked);
},
); );
} }
} }

@ -0,0 +1,83 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:open_file/open_file.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/views/app_style/sizing.dart';
import 'package:test_sa/views/widgets/buttons/app_back_button.dart';
import 'package:test_sa/views/widgets/loaders/image_loader.dart';
import 'package:url_launcher/url_launcher.dart';
import 'image_item.dart';
import 'images_viewer.dart';
class FilesList extends StatelessWidget {
final List<String> images;
final EdgeInsets padding;
const FilesList({Key key, this.images, this.padding}) : super(key: key);
@override
Widget build(BuildContext context) {
return ListView.separated(
padding: padding ?? EdgeInsets.symmetric(horizontal: 32 * AppStyle.getScaleFactor(context)),
scrollDirection: Axis.horizontal,
itemCount: images?.length ?? 0,
separatorBuilder: (context, itemIndex) => 8.width,
itemBuilder: (context, itemIndex) {
if (images[itemIndex] == null || !images[itemIndex].contains(".")) {
return Text("Bad\nFile");
}
var isImage =
images[itemIndex].split(".").last.toLowerCase() == "png" || images[itemIndex].split(".").last.toLowerCase() == "jpg" || images[itemIndex].split(".").last.toLowerCase() == "jpeg";
var isPdf = images[itemIndex].split(".").last.toLowerCase() == "pdf";
var isExcel = images[itemIndex].split(".").last.toLowerCase() == "xlsx";
return (isImage
? (_isLocalUrl(images[itemIndex])
? Image.file(File(images[itemIndex]))
: ImageLoader(
url: images[itemIndex],
boxFit: BoxFit.cover,
))
: Image.asset("assets/images/${isPdf ? "pdf" : isExcel ? "excel" : "doc"}.png"))
.onPress(() async {
if (isImage) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => Scaffold(
body: SafeArea(
child: Stack(
children: [
InteractiveViewer(child: Image(image: (_isLocalUrl(images[itemIndex]) ? FileImage(File(images[itemIndex])) : NetworkImage(images[itemIndex])))).center,
const ABackButton(),
],
),
),
),
),
);
} else if (_isLocalUrl(images[itemIndex])) {
OpenFile.open(images[itemIndex]);
} else {
try {
if (!await launchUrl(Uri.parse(images[itemIndex]), mode: LaunchMode.externalApplication)) {
Fluttertoast.showToast(msg: "UnExpected Error with file.");
throw Exception('Could not launch ');
}
} catch (ex) {
Fluttertoast.showToast(msg: "No Viewer found.");
}
}
});
});
}
bool _isLocalUrl(String url) {
if (url?.isEmpty != false) return false;
return url.startsWith("/") || url.startsWith("file://") || url.substring(1).startsWith(':\\');
}
}

@ -130,6 +130,9 @@ class _MultiFilesPickerState extends State<MultiFilesPicker> with TickerProvider
return MultiFilesPickerItem( return MultiFilesPickerItem(
file: _image, file: _image,
onRemoveTap: (image) { onRemoveTap: (image) {
if (!widget.enabled) {
return;
}
widget.files.remove(image); widget.files.remove(image);
setState(() {}); setState(() {});
}, },

@ -1,9 +1,14 @@
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:open_file/open_file.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/views/app_style/colors.dart'; import 'package:test_sa/views/app_style/colors.dart';
import 'package:test_sa/views/app_style/sizing.dart'; import 'package:test_sa/views/app_style/sizing.dart';
import 'package:test_sa/views/widgets/buttons/app_back_button.dart'; import 'package:test_sa/views/widgets/buttons/app_back_button.dart';
import 'package:test_sa/views/widgets/loaders/image_loader.dart';
import 'package:url_launcher/url_launcher.dart';
class MultiFilesPickerItem extends StatelessWidget { class MultiFilesPickerItem extends StatelessWidget {
final File file; final File file;
@ -23,7 +28,7 @@ class MultiFilesPickerItem extends StatelessWidget {
boxShadow: [BoxShadow(color: isImage ? Colors.black38 : AColors.cyan.withOpacity(0.5), blurRadius: 2)], boxShadow: [BoxShadow(color: isImage ? Colors.black38 : AColors.cyan.withOpacity(0.5), blurRadius: 2)],
image: DecorationImage( image: DecorationImage(
image: isImage image: isImage
? FileImage(file) ? (_isLocalUrl(file.path) ? FileImage(file) : NetworkImage(file.path))
: AssetImage("assets/images/${isPdf ? "pdf" : isExcel ? "excel" : "doc"}.png"), : AssetImage("assets/images/${isPdf ? "pdf" : isExcel ? "excel" : "doc"}.png"),
fit: BoxFit.cover, fit: BoxFit.cover,
), ),
@ -31,7 +36,7 @@ class MultiFilesPickerItem extends StatelessWidget {
), ),
child: MaterialButton( child: MaterialButton(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
onPressed: () { onPressed: () async {
if (isImage) { if (isImage) {
Navigator.of(context).push( Navigator.of(context).push(
MaterialPageRoute( MaterialPageRoute(
@ -39,7 +44,14 @@ class MultiFilesPickerItem extends StatelessWidget {
body: SafeArea( body: SafeArea(
child: Stack( child: Stack(
children: [ children: [
Center(child: isImage ? InteractiveViewer(child: Image(image: FileImage(file))) : const SizedBox.shrink()), InteractiveViewer(
child: _isLocalUrl(file.path)
? Image.file(file)
: ImageLoader(
url: file.path,
boxFit: BoxFit.cover,
))
.center,
const ABackButton(), const ABackButton(),
], ],
), ),
@ -47,6 +59,13 @@ class MultiFilesPickerItem extends StatelessWidget {
), ),
), ),
); );
} else if (_isLocalUrl(file.path)) {
OpenFile.open(file.path);
} else {
if (!await launchUrl(Uri.parse(file.path), mode: LaunchMode.externalApplication)) {
Fluttertoast.showToast(msg: "UnExpected Error with file.");
throw Exception('Could not launch ');
}
} }
}, },
child: Align( child: Align(
@ -69,4 +88,9 @@ class MultiFilesPickerItem extends StatelessWidget {
), ),
); );
} }
bool _isLocalUrl(String url) {
if (url?.isEmpty != false) return false;
return url.startsWith("/") || url.startsWith("file://") || url.substring(1).startsWith(':\\');
}
} }

@ -68,9 +68,9 @@ class _AutoCompletePartsFieldState extends State<AutoCompletePartsField> {
}, },
itemBuilder: (context, part) { itemBuilder: (context, part) {
return ListTile( return ListTile(
title: Text(part.code), title: Text(part.partNo),
subtitle: Text( subtitle: Text(
part.name, part.partName,
style: Theme.of(context).textTheme.caption, style: Theme.of(context).textTheme.caption,
), ),
); );
@ -79,7 +79,7 @@ class _AutoCompletePartsFieldState extends State<AutoCompletePartsField> {
if (widget.clearAfterPick) { if (widget.clearAfterPick) {
_controller.clear(); _controller.clear();
} else { } else {
_controller.text = part.code; _controller.text = part.partNo;
} }
widget.onPick(part); widget.onPick(part);
}, },

@ -21,7 +21,8 @@ class _PartItemState extends State<PartItem> {
return Column( return Column(
children: [ children: [
const Divider(), const Divider(),
Row(crossAxisAlignment: CrossAxisAlignment.start, Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Expanded( Expanded(
child: Column( child: Column(
@ -31,7 +32,7 @@ class _PartItemState extends State<PartItem> {
children: [ children: [
Expanded( Expanded(
child: Text( child: Text(
widget?.part?.code ?? "", widget?.part?.partNo ?? "",
style: Theme.of(context).textTheme.bodyText1.copyWith(fontSize: 12, fontWeight: FontWeight.bold), style: Theme.of(context).textTheme.bodyText1.copyWith(fontSize: 12, fontWeight: FontWeight.bold),
), ),
), ),
@ -76,16 +77,16 @@ class _PartItemState extends State<PartItem> {
), ),
], ],
), ),
widget.part.name == null widget.part.partName == null
? const SizedBox.shrink() ? const SizedBox.shrink()
: Text( : Text(
widget.part.name, widget.part.partName,
style: Theme.of(context).textTheme.caption.copyWith(fontSize: 11, fontWeight: FontWeight.bold), style: Theme.of(context).textTheme.caption.copyWith(fontSize: 11, fontWeight: FontWeight.bold),
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
// Row(crossAxisAlignment: ,) // Row(crossAxisAlignment: ,)
], ],
), ),
), ),

@ -9,11 +9,11 @@ import 'package:test_sa/views/app_style/sizing.dart';
import 'package:test_sa/views/widgets/app_text_form_field.dart'; import 'package:test_sa/views/widgets/app_text_form_field.dart';
import 'package:test_sa/views/widgets/date_and_time/date_picker.dart'; import 'package:test_sa/views/widgets/date_and_time/date_picker.dart';
import 'package:test_sa/views/widgets/e_signature/e_signature.dart'; import 'package:test_sa/views/widgets/e_signature/e_signature.dart';
import 'package:test_sa/views/widgets/images/mini_one_file_picker.dart';
import 'package:test_sa/views/widgets/status/pentry/pentry_visit_status_mune.dart'; import 'package:test_sa/views/widgets/status/pentry/pentry_visit_status_mune.dart';
import 'package:test_sa/views/widgets/timer/app_timer.dart'; import 'package:test_sa/views/widgets/timer/app_timer.dart';
import 'package:test_sa/views/widgets/titles/app_sub_title.dart'; import 'package:test_sa/views/widgets/titles/app_sub_title.dart';
import '../images/multi_image_picker.dart';
import '../status/pentry/pentry_status_mune.dart'; import '../status/pentry/pentry_status_mune.dart';
class PentryInfoForm extends StatefulWidget { class PentryInfoForm extends StatefulWidget {
@ -33,6 +33,7 @@ class PentryInfoForm extends StatefulWidget {
class _PentryInfoFormState extends State<PentryInfoForm> { class _PentryInfoFormState extends State<PentryInfoForm> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
widget.model?.files ??= [];
final subtitle = AppLocalization.of(context).subtitle; final subtitle = AppLocalization.of(context).subtitle;
final userProvider = Provider.of<UserProvider>(context); final userProvider = Provider.of<UserProvider>(context);
return ListView( return ListView(
@ -149,23 +150,21 @@ class _PentryInfoFormState extends State<PentryInfoForm> {
ATextFormField( ATextFormField(
initialValue: (widget.model.travelingHours ?? "").toString(), initialValue: (widget.model.travelingHours ?? "").toString(),
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: Theme.of(context).textTheme.subtitle1, style: Theme.of(context).textTheme.titleMedium,
textInputType: TextInputType.number, textInputType: TextInputType.number,
onChange: (value) { onChange: (value) {
widget.model.travelingHours = value; widget.model.travelingHours = value;
}, },
), ),
const SizedBox( const SizedBox(height: 12),
height: 12, MultiFilesPicker(label: "PPM Attachments", files: widget.model.files),
), // AMiniOneFilePicker(
const ASubTitle("PPM Attachment"), // //error: _validate && _serviceReport.image == null,
AMiniOneFilePicker( // file: widget.model.file,
//error: _validate && _serviceReport.image == null, // onPick: (file) {
file: widget.model.file, // widget.model.file = file;
onPick: (file) { // },
widget.model.file = file; // ),
},
),
const SizedBox( const SizedBox(
height: 8, height: 8,
), ),

@ -74,7 +74,7 @@ class _PentryPMKitFormState extends State<PentryPMKitForm> {
clearAfterPick: false, clearAfterPick: false,
initialValue: (model.itemCode?.name ?? "").toString(), initialValue: (model.itemCode?.name ?? "").toString(),
onPick: (part) { onPick: (part) {
model.itemCode = Lookup(id: part.id, name: part.code); model.itemCode = Lookup(id: part.id, name: part.partNo);
}, },
), ),
const SizedBox( const SizedBox(

@ -7,8 +7,9 @@ class FaultDescriptionMenu extends StatefulWidget {
final List<FaultDescription> statuses; final List<FaultDescription> statuses;
final FaultDescription initialStatus; final FaultDescription initialStatus;
final Function(FaultDescription) onSelect; final Function(FaultDescription) onSelect;
final bool enabled;
const FaultDescriptionMenu({Key key, this.statuses, this.onSelect, this.initialStatus}) : super(key: key); const FaultDescriptionMenu({Key key, this.statuses, this.onSelect, this.initialStatus, this.enabled}) : super(key: key);
@override @override
_SingleStatusMenuState createState() => _SingleStatusMenuState(); _SingleStatusMenuState createState() => _SingleStatusMenuState();
@ -71,39 +72,42 @@ class _SingleStatusMenuState extends State<FaultDescriptionMenu> {
// AppStyle.boxShadow // AppStyle.boxShadow
// ] // ]
), ),
child: DropdownButton<FaultDescription>( child: IgnorePointer(
value: _selectedStatus, ignoring: !widget.enabled,
iconSize: 24, child: DropdownButton<FaultDescription>(
icon: const Icon(Icons.keyboard_arrow_down_rounded), value: _selectedStatus,
elevation: 0, iconSize: 24,
isExpanded: true, icon: const Icon(Icons.keyboard_arrow_down_rounded),
hint: Text( elevation: 0,
"Select", isExpanded: true,
style: Theme.of(context).textTheme.subtitle1, hint: Text(
), "Select",
style: TextStyle(color: Theme.of(context).primaryColor), style: Theme.of(context).textTheme.subtitle1,
underline: SizedBox.shrink(), ),
onChanged: (FaultDescription newValue) { style: TextStyle(color: Theme.of(context).primaryColor),
setState(() { underline: SizedBox.shrink(),
_selectedStatus = newValue; onChanged: (FaultDescription newValue) {
}); setState(() {
widget.onSelect(newValue); _selectedStatus = newValue;
}, });
items: widget.statuses.map<DropdownMenuItem<FaultDescription>>( widget.onSelect(newValue);
(FaultDescription value) {
return DropdownMenuItem<FaultDescription>(
value: value,
child: Text(
value.defectName ?? "",
style: Theme.of(context).textTheme.subtitle1.copyWith(
color: Theme.of(context).primaryColor,
fontSize: 11,
//fontWeight: FontWeight.bold
),
),
);
}, },
).toList(), items: widget.statuses.map<DropdownMenuItem<FaultDescription>>(
(FaultDescription value) {
return DropdownMenuItem<FaultDescription>(
value: value,
child: Text(
value.defectName ?? "",
style: Theme.of(context).textTheme.subtitle1.copyWith(
color: Theme.of(context).primaryColor,
fontSize: 11,
//fontWeight: FontWeight.bold
),
),
);
},
).toList(),
),
), ),
); );
} }

@ -7,8 +7,6 @@ import 'package:test_sa/models/fault_description.dart';
import 'package:test_sa/views/widgets/loaders/loading_manager.dart'; import 'package:test_sa/views/widgets/loaders/loading_manager.dart';
import 'package:test_sa/views/widgets/status/report/fault_desc_menu.dart'; import 'package:test_sa/views/widgets/status/report/fault_desc_menu.dart';
import '../../app_text_form_field.dart';
class ServiceReportFaultDescription extends StatelessWidget { class ServiceReportFaultDescription extends StatelessWidget {
final String requestId; final String requestId;
final Function(FaultDescription) onSelect; final Function(FaultDescription) onSelect;
@ -29,16 +27,19 @@ class ServiceReportFaultDescription extends StatelessWidget {
menuProvider.reset(); menuProvider.reset();
await menuProvider.getCallRequestForWorkOrder(user: userProvider.user, host: settingProvider.host, requestId: requestId); await menuProvider.getCallRequestForWorkOrder(user: userProvider.user, host: settingProvider.host, requestId: requestId);
}, },
child: ((menuProvider.items?.isEmpty ?? true) || (!enabled)) child: Builder(builder: (context) {
? ATextFormField( if (initialValue == null && !enabled && menuProvider.items.isNotEmpty) {
initialValue: (!enabled) ? initialValue?.defectName ?? "" : "", WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
enable: false, onSelect(menuProvider.items.first);
) });
: FaultDescriptionMenu( }
initialStatus: initialValue, return FaultDescriptionMenu(
statuses: menuProvider.items, initialStatus: initialValue,
onSelect: onSelect, statuses: menuProvider.items,
), onSelect: onSelect,
enabled: enabled,
);
}),
); );
} }
} }

@ -11,8 +11,9 @@ import '../../../../controllers/providers/api/status_drop_down/service_reqest/se
class ServiceRequestedFirstAction extends StatelessWidget { class ServiceRequestedFirstAction extends StatelessWidget {
final Function(Lookup) onSelect; final Function(Lookup) onSelect;
final Lookup initialValue; final Lookup initialValue;
final bool enabled;
const ServiceRequestedFirstAction({Key key, this.onSelect, this.initialValue}) : super(key: key); const ServiceRequestedFirstAction({Key key, this.onSelect, this.initialValue, this.enabled = true}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final settingProvider = Provider.of<SettingProvider>(context); final settingProvider = Provider.of<SettingProvider>(context);
@ -30,6 +31,7 @@ class ServiceRequestedFirstAction extends StatelessWidget {
initialStatus: initialValue, initialStatus: initialValue,
statuses: menuProvider.items, statuses: menuProvider.items,
onSelect: onSelect, onSelect: onSelect,
enabled: enabled,
), ),
); );
} }

@ -701,6 +701,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.2" version: "1.0.2"
open_file:
dependency: "direct main"
description:
name: open_file
sha256: a5a32d44acb7c899987d0999e1e3cbb0a0f1adebbf41ac813ec6d2d8faa0af20
url: "https://pub.dev"
source: hosted
version: "3.3.2"
package_info: package_info:
dependency: "direct main" dependency: "direct main"
description: description:

@ -64,6 +64,7 @@ dependencies:
file_picker: ^5.2.5 file_picker: ^5.2.5
record_mp3: ^2.1.0 record_mp3: ^2.1.0
path_provider: ^2.1.0 path_provider: ^2.1.0
open_file: ^3.3.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

Loading…
Cancel
Save