feat: implement submit inspection functionality in poultry science feature, including new data source methods, repository updates, and UI enhancements for image uploads and inspection submission

This commit is contained in:
2025-12-11 19:50:20 +03:30
parent 3d73d9a17a
commit b8a914ec0e
18 changed files with 2358 additions and 248 deletions

View File

@@ -1,5 +1,5 @@
sdk.dir=C:\\Users\\Housh11\\AppData\\Local\\Android\\sdk sdk.dir=/Users/mojtaba/Library/Android/sdk
flutter.sdk=C:\\src\\flutter flutter.sdk=/Users/mojtaba/develop/flutter
flutter.buildMode=debug flutter.buildMode=debug
flutter.versionName=1.3.35 flutter.versionName=1.3.35
flutter.versionCode=32 flutter.versionCode=32

View File

@@ -11,6 +11,7 @@ import 'package:rasadyar_chicken/features/poultry_science/data/model/response/po
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/kill_house_poultry/kill_house_poultry.dart'; import 'package:rasadyar_chicken/features/poultry_science/data/model/response/kill_house_poultry/kill_house_poultry.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/kill_registration/kill_registration.dart'; import 'package:rasadyar_chicken/features/poultry_science/data/model/request/kill_registration/kill_registration.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_order/poultry_order.dart'; import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_order/poultry_order.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_core/core.dart'; import 'package:rasadyar_core/core.dart';
abstract class PoultryScienceRemoteDataSource { abstract class PoultryScienceRemoteDataSource {
@@ -94,4 +95,14 @@ abstract class PoultryScienceRemoteDataSource {
required String token, required String token,
required List<XFile> images, required List<XFile> images,
}); });
Future<PaginationModel<SubmitInspectionResponse>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
});
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
});
} }

View File

@@ -11,6 +11,7 @@ import 'package:rasadyar_chicken/features/poultry_science/data/model/response/po
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/kill_house_poultry/kill_house_poultry.dart'; import 'package:rasadyar_chicken/features/poultry_science/data/model/response/kill_house_poultry/kill_house_poultry.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/kill_registration/kill_registration.dart'; import 'package:rasadyar_chicken/features/poultry_science/data/model/request/kill_registration/kill_registration.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_order/poultry_order.dart'; import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_order/poultry_order.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/datasources/remote/poultry_science_remote_data_source.dart'; import 'package:rasadyar_chicken/features/poultry_science/data/datasources/remote/poultry_science_remote_data_source.dart';
import 'package:rasadyar_core/core.dart'; import 'package:rasadyar_core/core.dart';
@@ -261,5 +262,35 @@ class PoultryScienceRemoteDataSourceImpl
return res.data; return res.data;
} }
@override
Future<PaginationModel<SubmitInspectionResponse>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
}) async {
var res = await _httpClient.get(
'/poultry_science_report/',
headers: {'Authorization': 'Bearer $token'},
queryParameters: queryParameters,
fromJson: (json) => PaginationModel<SubmitInspectionResponse>.fromJson(
json,
(json) =>
SubmitInspectionResponse.fromJson(json as Map<String, dynamic>),
),
);
return res.data;
}
@override
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
}) async {
await _httpClient.post(
'/poultry_science_report/',
headers: {'Authorization': 'Bearer $token'},
data: request.toJson(),
);
}
//endregion //endregion
} }

View File

@@ -0,0 +1,396 @@
class SubmitInspectionResponse {
SubmitInspectionResponse({
this.lat,
this.log,
this.poultryHatchingId,
this.role,
this.generalConditionHall,
this.casualties,
this.technicalOfficer,
this.inputStatus,
this.infrastructureEnergy,
this.hr,
this.facilities,
this.inspectionStatus,
this.inspectionNotes,
});
String? lat;
String? log;
int? poultryHatchingId;
String? role;
GeneralConditionHall? generalConditionHall;
Casualties? casualties;
TechnicalOfficer? technicalOfficer;
InputStatus? inputStatus;
InfrastructureEnergy? infrastructureEnergy;
Hr? hr;
Facilities? facilities;
String? inspectionStatus;
String? inspectionNotes;
Map<String, dynamic> toJson() {
return {
'lat': lat,
'log': log,
'hatching_id': poultryHatchingId,
'role': role,
'report_information': {
'general_condition_hall': generalConditionHall?.toJson(),
'casualties': casualties?.toJson(),
'technical_officer': technicalOfficer?.toJson(),
'input_status': inputStatus?.toJson(),
'infrastructure_energy': infrastructureEnergy?.toJson(),
'hr': hr?.toJson(),
'facilities': facilities?.toJson(),
'inspection_status': inspectionStatus,
'inspection_notes': inspectionNotes,
},
};
}
factory SubmitInspectionResponse.fromJson(Map<String, dynamic> json) {
return SubmitInspectionResponse(
lat: json['lat'].toString(),
log: json['log'].toString(),
poultryHatchingId: json['poultry_hatching_id'] as int?,
role: json['role'] as String?,
generalConditionHall: json['general_condition_hall'] != null
? GeneralConditionHall.fromJson(
json['general_condition_hall'] as Map<String, dynamic>,
)
: null,
casualties: json['casualties'] != null
? Casualties.fromJson(json['casualties'] as Map<String, dynamic>)
: null,
technicalOfficer: json['technical_officer'] != null
? TechnicalOfficer.fromJson(
json['technical_officer'] as Map<String, dynamic>,
)
: null,
inputStatus: json['input_status'] != null
? InputStatus.fromJson(json['input_status'] as Map<String, dynamic>)
: null,
infrastructureEnergy: json['infrastructure_energy'] != null
? InfrastructureEnergy.fromJson(
json['infrastructure_energy'] as Map<String, dynamic>,
)
: null,
hr: json['hr'] != null
? Hr.fromJson(json['hr'] as Map<String, dynamic>)
: null,
facilities: json['facilities'] != null
? Facilities.fromJson(json['facilities'] as Map<String, dynamic>)
: null,
inspectionStatus: json['inspection_status'] as String?,
inspectionNotes: json['inspection_notes'] as String?,
);
}
}
class GeneralConditionHall {
GeneralConditionHall({
this.images,
this.healthStatus,
this.ventilationStatus,
this.bedCondition,
this.temperature,
this.drinkingWaterSource,
this.drinkingWaterQuality,
});
List<String>? images;
String? healthStatus;
String? ventilationStatus;
String? bedCondition;
String? temperature;
String? drinkingWaterSource;
String? drinkingWaterQuality;
Map<String, dynamic> toJson() {
return {
'images': images,
'health_status': healthStatus,
'ventilation_status': ventilationStatus,
'bed_condition': bedCondition,
'temperature': temperature,
'drinking_water_source': drinkingWaterSource,
'drinking_water_quality': drinkingWaterQuality,
};
}
factory GeneralConditionHall.fromJson(Map<String, dynamic> json) {
return GeneralConditionHall(
images: json['images'] != null
? List<String>.from(json['images'] as List)
: null,
healthStatus: json['health_status'] as String?,
ventilationStatus: json['ventilation_status'] as String?,
bedCondition: json['bed_condition'] as String?,
temperature: json['temperature'] as String?,
drinkingWaterSource: json['drinking_water_source'] as String?,
drinkingWaterQuality: json['drinking_water_quality'] as String?,
);
}
}
class Casualties {
Casualties({
this.normalLosses,
this.abnormalLosses,
this.sourceOfHatching,
this.causeAbnormalLosses,
this.typeDisease,
this.samplingDone,
this.typeSampling,
this.images,
});
int? normalLosses;
int? abnormalLosses;
String? sourceOfHatching;
String? causeAbnormalLosses;
String? typeDisease;
bool? samplingDone;
String? typeSampling;
List<String>? images;
Map<String, dynamic> toJson() {
return {
'normal_losses': normalLosses,
'abnormal_losses': abnormalLosses,
'source_of_hatching': sourceOfHatching,
'cause_abnormal_losses': causeAbnormalLosses,
'type_disease': typeDisease,
'sampling_done': samplingDone,
'type_sampling': typeSampling,
'images': images,
};
}
factory Casualties.fromJson(Map<String, dynamic> json) {
return Casualties(
normalLosses: json['normal_losses'] as int?,
abnormalLosses: json['abnormal_losses'] as int?,
sourceOfHatching: json['source_of_hatching'] as String?,
causeAbnormalLosses: json['cause_abnormal_losses'] as String?,
typeDisease: json['type_disease'] as String?,
samplingDone: json['sampling_done'] as bool?,
typeSampling: json['type_sampling'] as String?,
images: json['images'] != null
? List<String>.from(json['images'] as List)
: null,
);
}
}
class TechnicalOfficer {
TechnicalOfficer({
this.technicalHealthOfficer,
this.technicalEngineeringOfficer,
});
String? technicalHealthOfficer;
String? technicalEngineeringOfficer;
Map<String, dynamic> toJson() {
return {
'technical_health_officer': technicalHealthOfficer,
'technical_engineering_officer': technicalEngineeringOfficer,
};
}
factory TechnicalOfficer.fromJson(Map<String, dynamic> json) {
return TechnicalOfficer(
technicalHealthOfficer: json['technical_health_officer'] as String?,
technicalEngineeringOfficer:
json['technical_engineering_officer'] as String?,
);
}
}
class InputStatus {
InputStatus({
this.inputStatus,
this.companyName,
this.trackingCode,
this.typeOfGrain,
this.inventoryInWarehouse,
this.inventoryUntilVisit,
this.gradeGrain,
this.images,
});
String? inputStatus;
String? companyName;
String? trackingCode;
String? typeOfGrain;
String? inventoryInWarehouse;
String? inventoryUntilVisit;
String? gradeGrain;
List<String>? images;
Map<String, dynamic> toJson() {
return {
'input_status': inputStatus,
'company_name': companyName,
'tracking_code': trackingCode,
'type_of_grain': typeOfGrain,
'inventory_in_warehouse': inventoryInWarehouse,
'inventory_until_visit': inventoryUntilVisit,
'grade_grain': gradeGrain,
'images': images,
};
}
factory InputStatus.fromJson(Map<String, dynamic> json) {
return InputStatus(
inputStatus: json['input_status'] as String?,
companyName: json['company_name'] as String?,
trackingCode: json['tracking_code'] as String?,
typeOfGrain: json['type_of_grain'] as String?,
inventoryInWarehouse: json['inventory_in_warehouse'] as String?,
inventoryUntilVisit: json['inventory_until_visit'] as String?,
gradeGrain: json['grade_grain'] as String?,
images: json['images'] != null
? List<String>.from(json['images'] as List)
: null,
);
}
}
class InfrastructureEnergy {
InfrastructureEnergy({
this.generatorType,
this.generatorModel,
this.generatorCount,
this.generatorCapacity,
this.fuelType,
this.generatorPerformance,
this.emergencyFuelInventory,
this.hasPowerCutHistory,
this.powerCutDuration,
this.powerCutHour,
this.additionalNotes,
});
String? generatorType;
String? generatorModel;
String? generatorCount;
String? generatorCapacity;
String? fuelType;
String? generatorPerformance;
String? emergencyFuelInventory;
bool? hasPowerCutHistory;
String? powerCutDuration;
String? powerCutHour;
String? additionalNotes;
Map<String, dynamic> toJson() {
return {
'generator_type': generatorType,
'generator_model': generatorModel,
'generator_count': generatorCount,
'generator_capacity': generatorCapacity,
'fuel_type': fuelType,
'generator_performance': generatorPerformance,
'emergency_fuel_inventory': emergencyFuelInventory,
'has_power_cut_history': hasPowerCutHistory,
'power_cut_duration': powerCutDuration,
'power_cut_hour': powerCutHour,
'additional_notes': additionalNotes,
};
}
factory InfrastructureEnergy.fromJson(Map<String, dynamic> json) {
return InfrastructureEnergy(
generatorType: json['generator_type'] as String?,
generatorModel: json['generator_model'] as String?,
generatorCount: json['generator_count'] as String?,
generatorCapacity: json['generator_capacity'] as String?,
fuelType: json['fuel_type'] as String?,
generatorPerformance: json['generator_performance'] as String?,
emergencyFuelInventory: json['emergency_fuel_inventory'] as String?,
hasPowerCutHistory: json['has_power_cut_history'] as bool?,
powerCutDuration: json['power_cut_duration'] as String?,
powerCutHour: json['power_cut_hour'] as String?,
additionalNotes: json['additional_notes'] as String?,
);
}
}
class Hr {
Hr({
this.numberEmployed,
this.numberIndigenous,
this.numberNonIndigenous,
this.contractStatus,
this.trained,
});
int? numberEmployed;
int? numberIndigenous;
int? numberNonIndigenous;
String? contractStatus;
bool? trained;
Map<String, dynamic> toJson() {
return {
'number_employed': numberEmployed,
'number_indigenous': numberIndigenous,
'number_non_indigenous': numberNonIndigenous,
'contract_status': contractStatus,
'trained': trained,
};
}
factory Hr.fromJson(Map<String, dynamic> json) {
return Hr(
numberEmployed: json['number_employed'] as int?,
numberIndigenous: json['number_indigenous'] as int?,
numberNonIndigenous: json['number_non_indigenous'] as int?,
contractStatus: json['contract_status'] as String?,
trained: json['trained'] as bool?,
);
}
}
class Facilities {
Facilities({
this.hasFacilities,
this.typeOfFacility,
this.amount,
this.date,
this.repaymentStatus,
this.requestFacilities,
});
bool? hasFacilities;
String? typeOfFacility;
int? amount;
String? date;
String? repaymentStatus;
String? requestFacilities;
Map<String, dynamic> toJson() {
return {
'has_facilities': hasFacilities,
'type_of_facility': typeOfFacility,
'amount': amount,
'date': date,
'repayment_status': repaymentStatus,
'request_facilities': requestFacilities,
};
}
factory Facilities.fromJson(Map<String, dynamic> json) {
return Facilities(
hasFacilities: json['has_facilities'] as bool?,
typeOfFacility: json['type_of_facility'] as String?,
amount: json['amount'] as int?,
date: json['date'] as String?,
repaymentStatus: json['repayment_status'] as String?,
requestFacilities: json['request_facilities'] as String?,
);
}
}

View File

@@ -11,6 +11,7 @@ import 'package:rasadyar_chicken/features/poultry_science/data/model/response/po
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/kill_house_poultry/kill_house_poultry.dart'; import 'package:rasadyar_chicken/features/poultry_science/data/model/response/kill_house_poultry/kill_house_poultry.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/kill_registration/kill_registration.dart'; import 'package:rasadyar_chicken/features/poultry_science/data/model/request/kill_registration/kill_registration.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_order/poultry_order.dart'; import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_order/poultry_order.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_core/core.dart'; import 'package:rasadyar_core/core.dart';
abstract class PoultryScienceRepository { abstract class PoultryScienceRepository {
@@ -94,4 +95,14 @@ abstract class PoultryScienceRepository {
required String token, required String token,
required List<XFile> images, required List<XFile> images,
}); });
Future<PaginationModel<SubmitInspectionResponse>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
});
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
});
} }

View File

@@ -11,6 +11,7 @@ import 'package:rasadyar_chicken/features/poultry_science/data/model/response/po
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/kill_house_poultry/kill_house_poultry.dart'; import 'package:rasadyar_chicken/features/poultry_science/data/model/response/kill_house_poultry/kill_house_poultry.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/kill_registration/kill_registration.dart'; import 'package:rasadyar_chicken/features/poultry_science/data/model/request/kill_registration/kill_registration.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_order/poultry_order.dart'; import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_order/poultry_order.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/datasources/remote/poultry_science_remote_data_source.dart'; import 'package:rasadyar_chicken/features/poultry_science/data/datasources/remote/poultry_science_remote_data_source.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/repositories/poultry_science_repository.dart'; import 'package:rasadyar_chicken/features/poultry_science/data/repositories/poultry_science_repository.dart';
import 'package:rasadyar_core/core.dart'; import 'package:rasadyar_core/core.dart';
@@ -188,4 +189,23 @@ class PoultryScienceRepositoryImpl implements PoultryScienceRepository {
}) async { }) async {
return await _dataSource.uploadImages(token: token, images: images); return await _dataSource.uploadImages(token: token, images: images);
} }
@override
Future<PaginationModel<SubmitInspectionResponse>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
}) async {
return await _dataSource.getSubmitInspectionList(
token: token,
queryParameters: queryParameters,
);
}
@override
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
}) async {
return await _dataSource.submitInspection(token: token, request: request);
}
} }

View File

@@ -0,0 +1,157 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/root/logic.dart';
import 'package:rasadyar_core/core.dart';
class NewInspectionPoultryScienceLogic extends GetxController {
BaseLogic baseLogic = Get.find<BaseLogic>();
Rx<Resource<PaginationModel<SubmitInspectionResponse>>> submitInspectionList =
Resource<PaginationModel<SubmitInspectionResponse>>.loading().obs;
PoultryScienceRootLogic rootLogic = Get.find<PoultryScienceRootLogic>();
final RxBool isLoadingMoreAllocationsMade = false.obs;
RxInt currentPage = 1.obs;
RxInt expandedIndex = RxInt(-1);
RxList<XFile> pickedImages = <XFile>[].obs;
final List<MultipartFile> _multiPartPickedImages = <MultipartFile>[];
RxBool isOnUpload = false.obs;
RxDouble presentUpload = 0.0.obs;
RxList<String> routesName = RxList();
RxInt selectedSegmentIndex = 0.obs;
RxnString searchedValue = RxnString();
Rx<Jalali> fromDateFilter = Jalali.now().obs;
Rx<Jalali> toDateFilter = Jalali.now().obs;
@override
void onInit() {
super.onInit();
routesName.value = ['اقدام'].toList();
ever(selectedSegmentIndex, (callback) {
routesName.removeLast();
routesName.add(callback == 0 ? 'بازرسی' : 'بایگانی');
});
}
@override
void onReady() {
super.onReady();
getReport();
}
@override
void onClose() {
super.onClose();
baseLogic.clearSearch();
}
Future<void> getReport([bool isLoadingMore = false]) async {
if (isLoadingMore) {
isLoadingMoreAllocationsMade.value = true;
} else {
submitInspectionList.value =
Resource<PaginationModel<SubmitInspectionResponse>>.loading();
}
if (searchedValue.value != null &&
searchedValue.value!.trim().isNotEmpty &&
currentPage.value > 1) {
currentPage.value = 1;
}
safeCall(
call: () async =>
await rootLogic.poultryRepository.getSubmitInspectionList(
token: rootLogic.tokenService.accessToken.value!,
queryParameters: buildQueryParams(
role: 'PoultryScience',
pageSize: 50,
search: 'filter',
page: currentPage.value,
),
),
onSuccess: (res) {
if ((res?.count ?? 0) == 0) {
submitInspectionList.value =
Resource<PaginationModel<SubmitInspectionResponse>>.empty();
} else {
submitInspectionList.value =
Resource<PaginationModel<SubmitInspectionResponse>>.success(
PaginationModel<SubmitInspectionResponse>(
count: res?.count ?? 0,
next: res?.next,
previous: res?.previous,
results: [
...(submitInspectionList.value.data?.results ?? []),
...(res?.results ?? []),
],
),
);
}
},
);
}
Future<void> pickImages() async {
determineCurrentPosition();
var tmp = await pickCameraImage();
if (tmp?.path != null && pickedImages.length < 7) {
pickedImages.add(tmp!);
}
}
void removeImage(int index) {
pickedImages.removeAt(index);
}
void closeBottomSheet() {
Get.back();
}
double calculateUploadProgress({required int sent, required int total}) {
if (total != 0) {
double progress = (sent * 100 / total) / 100;
return progress;
} else {
return 0.0;
}
}
void toggleExpanded(int index) {
expandedIndex.value = expandedIndex.value == index ? -1 : index;
}
void setSearchValue(String? data) {
dLog('Search Value: $data');
searchedValue.value = data?.trim();
getReport();
}
Future<void> onRefresh() async {
currentPage.value = 1;
await getReport();
}
String getStatus(SubmitInspectionResponse item) {
if (item.inspectionStatus == null || item.inspectionStatus!.isEmpty) {
return 'در حال بررسی';
}
return item.inspectionStatus!;
}
Color getStatusColor(SubmitInspectionResponse item) {
if (item.inspectionStatus == null || item.inspectionStatus!.isEmpty) {
return AppColor.yellowNormal;
}
// می‌توانید منطق رنگ را بر اساس inspectionStatus تنظیم کنید
return AppColor.greenNormal;
}
}

View File

@@ -39,7 +39,7 @@ class PoultryActionLogic extends GetxController {
PoultryActionItem( PoultryActionItem(
title: "بازرسی مزارع طیور", title: "بازرسی مزارع طیور",
route: ChickenRoutes.poultryFarmInspectionHome, route: PoultryScienceRoutes.newInspectionPoultryScience,
icon: Assets.vec.activeFramSvg.path, icon: Assets.vec.activeFramSvg.path,
), ),
].obs; ].obs;

View File

@@ -7,6 +7,8 @@ import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/gen
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/home/logic.dart'; import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/home/logic.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/inspection/logic.dart'; import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/inspection/logic.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/inspection/view.dart'; import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/inspection/view.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/new_inspection/logic.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/new_inspection/view.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/killing_registration/logic.dart'; import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/killing_registration/logic.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/killing_registration/view.dart'; import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/killing_registration/view.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/poultry_action/logic.dart'; import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/poultry_action/logic.dart';
@@ -46,6 +48,17 @@ class PoultrySciencePages {
}), }),
], ],
), ),
GetPage(
name: PoultryScienceRoutes.newInspectionPoultryScience,
page: () => NewInspectionPoultrySciencePage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => NewInspectionPoultryScienceLogic());
}),
],
),
GetPage( GetPage(
name: PoultryScienceRoutes.actionPoultryScience, name: PoultryScienceRoutes.actionPoultryScience,
page: () => PoultryActionPage(), page: () => PoultryActionPage(),

View File

@@ -5,6 +5,7 @@ sealed class PoultryScienceRoutes {
static const initPoultryScience = '$_base/'; static const initPoultryScience = '$_base/';
static const actionPoultryScience = '$_base/action'; static const actionPoultryScience = '$_base/action';
static const inspectionPoultryScience = '$_base/inspection'; static const inspectionPoultryScience = '$_base/inspection';
static const newInspectionPoultryScience = '$_base/newInspection';
static const farmPoultryScience = '$_base/farm'; static const farmPoultryScience = '$_base/farm';
static const activeHatchingPoultryScience = '$_base/activeHatching'; static const activeHatchingPoultryScience = '$_base/activeHatching';
static const genocidePoultryScience = '$_base/genocidePoultryScience'; static const genocidePoultryScience = '$_base/genocidePoultryScience';

View File

@@ -16,7 +16,6 @@ class CreateInspectionBottomSheet
step2Page(controller), step2Page(controller),
step3Page(controller), step3Page(controller),
step4Page(controller), step4Page(controller),
step5Page(controller),
]; ];
@override @override
@@ -47,20 +46,40 @@ class CreateInspectionBottomSheet
child: ObxValue((data) { child: ObxValue((data) {
return RElevated( return RElevated(
height: 40.h, height: 40.h,
enabled: data.value, enabled:
data.value && !controller.isUploadingImages.value,
backgroundColor: AppColor.greenNormal, backgroundColor: AppColor.greenNormal,
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Icon(Icons.arrow_back_ios, color: Colors.white), if (controller.isUploadingImages.value)
SizedBox(
Text('ادامه'), width: 16.w,
height: 16.h,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(
Colors.white,
),
),
)
else
Icon(Icons.arrow_back_ios, color: Colors.white),
SizedBox(width: 8.w),
Text(
controller.isUploadingImages.value
? 'در حال آپلود...'
: (controller.activeStepperIndex.value < 3)
? 'ادامه'
: 'ثبت',
),
], ],
), ),
onPressed: () { onPressed: () {
if (controller.activeStepperIndex.value < 4) { if (controller.activeStepperIndex.value < 3) {
controller.activeStepperIndex.value++; controller.activeStepperIndex.value++;
} else {
controller.submitInspection();
} }
}, },
); );
@@ -71,7 +90,9 @@ class CreateInspectionBottomSheet
borderColor: AppColor.error, borderColor: AppColor.error,
height: 40.h, height: 40.h,
child: Text('برگشت'), child: Text('برگشت'),
enabled: controller.activeStepperIndex.value > 0, enabled:
controller.activeStepperIndex.value > 0 &&
!controller.isUploadingImages.value,
onPressed: () { onPressed: () {
if (controller.activeStepperIndex.value > 0) { if (controller.activeStepperIndex.value > 0) {
controller.activeStepperIndex.value--; controller.activeStepperIndex.value--;
@@ -83,6 +104,53 @@ class CreateInspectionBottomSheet
), ),
); );
}, controller.activeStepperIndex), }, controller.activeStepperIndex),
// نمایش وضعیت آپلود
Obx(() {
if (controller.isUploadingImages.value) {
return Container(
padding: EdgeInsets.all(16),
margin: EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColor.whiteNormalActive,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 4,
offset: Offset(0, 2),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
controller.uploadStatusMessage.value,
style: AppFonts.yekan14,
textAlign: TextAlign.center,
),
SizedBox(height: 12.h),
LinearProgressIndicator(
value: controller.uploadProgress.value,
backgroundColor: AppColor.whiteNormalActive,
valueColor: AlwaysStoppedAnimation<Color>(
AppColor.greenNormal,
),
),
SizedBox(height: 8.h),
Text(
'${(controller.uploadProgress.value * 100).toInt()}%',
style: AppFonts.yekan12.copyWith(
color: AppColor.iconColor,
),
),
],
),
);
}
return SizedBox.shrink();
}),
], ],
), ),
); );
@@ -199,7 +267,7 @@ class stepper extends StatelessWidget {
), ),
), ),
), ),
Expanded( /* Expanded(
child: Divider( child: Divider(
color: activeStep >= 4 color: activeStep >= 4
? AppColor.greenNormalHover ? AppColor.greenNormalHover
@@ -207,7 +275,7 @@ class stepper extends StatelessWidget {
thickness: 8, thickness: 8,
), ),
), ),
Container( Container(
alignment: Alignment.center, alignment: Alignment.center,
decoration: BoxDecoration( decoration: BoxDecoration(
color: activeStep >= 4 color: activeStep >= 4
@@ -224,7 +292,7 @@ class stepper extends StatelessWidget {
color: activeStep >= 3 ? Colors.white : AppColor.iconColor, color: activeStep >= 3 ? Colors.white : AppColor.iconColor,
), ),
), ),
), ), */
], ],
), ),
), ),

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/data/di/chicken_di.dart'; import 'package:rasadyar_chicken/data/di/chicken_di.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/hatching/hatching_models.dart'; import 'package:rasadyar_chicken/features/poultry_science/data/model/response/hatching/hatching_models.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/repositories/poultry_science_repository.dart'; import 'package:rasadyar_chicken/features/poultry_science/data/repositories/poultry_science_repository.dart';
import 'package:rasadyar_core/core.dart'; import 'package:rasadyar_core/core.dart';
@@ -12,10 +13,9 @@ class CreateInspectionBottomSheetLogic extends GetxController
.get<PoultryScienceRepository>(); .get<PoultryScienceRepository>();
TokenStorageService tokenStorageService = Get.find<TokenStorageService>(); TokenStorageService tokenStorageService = Get.find<TokenStorageService>();
var gService = Get.find<GService>();
RxInt selectedSegmentIndex = 0.obs; RxInt selectedSegmentIndex = 0.obs;
RxInt expandedIndex = RxInt(-1); RxInt expandedIndex = RxInt(-1);
late TabController tabController; late TabController tabController;
@@ -28,7 +28,14 @@ class CreateInspectionBottomSheetLogic extends GetxController
RxBool nextStepButtonEnabled = false.obs; RxBool nextStepButtonEnabled = false.obs;
RxInt casualtiesInformationHeight = 300.obs; RxInt casualtiesInformationHeight = 310.obs;
SubmitInspectionResponse? submitInspectionResponse;
// Upload states
RxBool isUploadingImages = false.obs;
RxString uploadStatusMessage = ''.obs;
RxDouble uploadProgress = 0.0.obs;
//step1 //step1
@@ -155,6 +162,7 @@ class CreateInspectionBottomSheetLogic extends GetxController
RxBool trainingStatus = false.obs; RxBool trainingStatus = false.obs;
RxnString newBeneficiaryRequest = RxnString(); RxnString newBeneficiaryRequest = RxnString();
RxBool overdueStatus = false.obs; RxBool overdueStatus = false.obs;
RxBool hasFacilities = false.obs;
TextEditingController paymentYearController = TextEditingController(); TextEditingController paymentYearController = TextEditingController();
Rx<Jalali> selectedPaymentYear = Jalali.now().obs; Rx<Jalali> selectedPaymentYear = Jalali.now().obs;
@@ -213,11 +221,164 @@ class CreateInspectionBottomSheetLogic extends GetxController
@override @override
void onClose() { void onClose() {
// TODO: implement onClose resetAllData();
super.onClose(); super.onClose();
} }
void resetAllData() {
// Reset stepper and navigation
activeStepperIndex.value = 0;
selectedTabIndex.value = 0;
selectedSegmentIndex.value = 0;
expandedIndex.value = -1;
nextStepButtonEnabled.value = false;
casualtiesInformationHeight.value = 310;
// Reset upload states
isUploadingImages.value = false;
uploadStatusMessage.value = '';
uploadProgress.value = 0.0;
// Reset location
currentLocation.value = null;
isLoadingLocation.value = false;
// Clear all TextEditingControllers - Step 1
unitNameController.clear();
breedingUniqueIdController.clear();
healthLicenseController.clear();
tenantStatusController.clear();
tenantNameController.clear();
tenantNationalIdController.clear();
tenantPhoneNumberController.clear();
ownerNameController.clear();
ownerNationalCodeController.clear();
ownerPhoneNumberController.clear();
totalCapacityController.clear();
// Clear Step 1 additional fields
hatchingDateController.clear();
visitDateController.clear();
hatchingCountController.clear();
hatchingAverageWeightController.clear();
hatchingBreedController.clear();
// Clear Step 2 fields
causeOfUnusualCasualties.value = null;
isOthercauseOfUnusualCasualties.value = false;
otherCauseOfUnusualCasualtiesController.clear();
typeOfDisease.value = null;
isOtherTypeOfDiseaseSelected.value = false;
otherTypeOfDiseaseController.clear();
hatchingTemperatureController.clear();
waterHardnessController.clear();
normalLossesController.clear();
abnormalLossesController.clear();
sourceOfHatchingController.clear();
samplingDone.value = false;
technicalHealthOfficerNameController.clear();
technicalEngineeringOfficerNameController.clear();
// Reset Step 2 selection indices
sanitaryConditionOfTheHallIndex.value = -1;
ventilationStatusIndex.value = -1;
beddingStatusIndex.value = -1;
waterQualityIndex.value = -1;
sampleTypeIndex.value = -1;
sanitaryConditionOfTheHall.value = null;
ventilationStatus.value = null;
beddingStatus.value = null;
waterQuality.value = null;
sampleType.value = null;
// Clear Step 3 fields
inputStatus.value = null;
companyNameController.clear();
trackingCodeController.clear();
typeOfGrain.value = null;
inputInventoryInWarehouseController.clear();
inputInventoryUntilVisitController.clear();
generatorTypeController.clear();
generatorModelController.clear();
generatorCountController.clear();
generatorCapacityController.clear();
emergencyFuelInventoryController.clear();
powerCutDurationController.clear();
powerCutHourController.clear();
additionalNotesController.clear();
fuelType.value = null;
powerCutHistory.value = false;
// Reset Step 3 selection indices
grainQualityInputIndex.value = -1;
generatorOperatingStatusIndex.value = -1;
workerContractStatusIndex.value = -1;
newBeneficiaryRequestIndex.value = -1;
grainQualityInput.value = null;
generatorOperatingStatus.value = null;
workerContractStatus.value = null;
newBeneficiaryRequest.value = null;
trainingStatus.value = false;
overdueStatus.value = false;
hasFacilities.value = false;
// Clear Step 3 worker fields
employedWorkersCountController.clear();
nativeWorkersCountController.clear();
nonNativeWorkersCountController.clear();
activeFacilityController.clear();
facilityTypeController.clear();
facilityAmountController.clear();
paymentYearController.clear();
selectedPaymentYear.value = Jalali.now();
// Clear Step 4 fields
facilityYearController.clear();
selectedFacilityYear.value = Jalali.now();
inspectorConclusionIndex.value = -1;
inspectorConclusion.value = null;
inspectorConclusionDescriptionController.clear();
// Clear all images
pultryImages.clear();
pultryImagesUrls.clear();
hallImages.clear();
hallImagesUrls.clear();
inputWarehouseImages.clear();
inputWarehouseImagesUrls.clear();
lossesImages.clear();
lossesImagesUrls.clear();
// Reset fuel type index
fuelTypeIndex.value = -1;
// Reset submitInspectionResponse
submitInspectionResponse = null;
// Reset hatching model
hatchingModel = null;
// Reset page controller
if (pageController.hasClients) {
pageController.jumpToPage(0);
}
}
void initData() { void initData() {
submitInspectionResponse = SubmitInspectionResponse(
generalConditionHall: GeneralConditionHall(),
casualties: Casualties(),
technicalOfficer: TechnicalOfficer(),
inputStatus: InputStatus(),
infrastructureEnergy: InfrastructureEnergy(),
hr: Hr(),
facilities: Facilities(),
);
submitInspectionResponse?.poultryHatchingId = hatchingModel?.id;
submitInspectionResponse?.role = gService.getRole(Module.chicken);
unitNameController.text = unitNameController.text =
hatchingModel?.poultry?.unitName ?? 'واحد مرغداری نامشخص'; hatchingModel?.poultry?.unitName ?? 'واحد مرغداری نامشخص';
@@ -296,6 +457,8 @@ class CreateInspectionBottomSheetLogic extends GetxController
void setSanitaryConditionOfTheHallIndex(int index, String item) { void setSanitaryConditionOfTheHallIndex(int index, String item) {
sanitaryConditionOfTheHall.value = item; sanitaryConditionOfTheHall.value = item;
submitInspectionResponse?.generalConditionHall?.healthStatus = item;
sanitaryConditionOfTheHallIndex.value = sanitaryConditionOfTheHallIndex.value =
index == sanitaryConditionOfTheHallIndex.value ? -1 : index; index == sanitaryConditionOfTheHallIndex.value ? -1 : index;
} }
@@ -307,6 +470,7 @@ class CreateInspectionBottomSheetLogic extends GetxController
void setVentilationStatusIndex(int index, String item) { void setVentilationStatusIndex(int index, String item) {
ventilationStatus.value = item; ventilationStatus.value = item;
submitInspectionResponse?.generalConditionHall?.ventilationStatus = item;
ventilationStatusIndex.value = index == ventilationStatusIndex.value ventilationStatusIndex.value = index == ventilationStatusIndex.value
? -1 ? -1
: index; : index;
@@ -314,21 +478,25 @@ class CreateInspectionBottomSheetLogic extends GetxController
void setBeddingStatusIndex(int index, String item) { void setBeddingStatusIndex(int index, String item) {
beddingStatus.value = item; beddingStatus.value = item;
submitInspectionResponse?.generalConditionHall?.bedCondition = item;
beddingStatusIndex.value = index == beddingStatusIndex.value ? -1 : index; beddingStatusIndex.value = index == beddingStatusIndex.value ? -1 : index;
} }
void setWaterQualityIndex(int index, String item) { void setWaterQualityIndex(int index, String item) {
waterQuality.value = item; waterQuality.value = item;
submitInspectionResponse?.generalConditionHall?.drinkingWaterSource = item;
waterQualityIndex.value = index == waterQualityIndex.value ? -1 : index; waterQualityIndex.value = index == waterQualityIndex.value ? -1 : index;
} }
void setSampleTypeIndex(int index, String item) { void setSampleTypeIndex(int index, String item) {
sampleType.value = item; sampleType.value = item;
submitInspectionResponse?.casualties?.typeSampling = item;
sampleTypeIndex.value = index == sampleTypeIndex.value ? -1 : index; sampleTypeIndex.value = index == sampleTypeIndex.value ? -1 : index;
} }
void setGrainQualityInput(int index, String item) { void setGrainQualityInput(int index, String item) {
grainQualityInput.value = item; grainQualityInput.value = item;
submitInspectionResponse?.inputStatus?.gradeGrain = item;
grainQualityInputIndex.value = index == grainQualityInputIndex.value grainQualityInputIndex.value = index == grainQualityInputIndex.value
? -1 ? -1
: index; : index;
@@ -336,13 +504,14 @@ class CreateInspectionBottomSheetLogic extends GetxController
void setGeneratorOperatingStatusIndex(int index, String item) { void setGeneratorOperatingStatusIndex(int index, String item) {
generatorOperatingStatus.value = item; generatorOperatingStatus.value = item;
submitInspectionResponse?.infrastructureEnergy?.generatorPerformance = item;
generatorOperatingStatusIndex.value = generatorOperatingStatusIndex.value =
index == generatorOperatingStatusIndex.value ? -1 : index; index == generatorOperatingStatusIndex.value ? -1 : index;
} }
void setWorkerContractStatusIndex(int index, String item) { void setWorkerContractStatusIndex(int index, String item) {
workerContractStatus.value = item; workerContractStatus.value = item;
submitInspectionResponse?.hr?.contractStatus = item;
workerContractStatusIndex.value = index == workerContractStatusIndex.value workerContractStatusIndex.value = index == workerContractStatusIndex.value
? -1 ? -1
: index; : index;
@@ -374,6 +543,10 @@ class CreateInspectionBottomSheetLogic extends GetxController
final latLng = await determineCurrentLatLng(); final latLng = await determineCurrentLatLng();
currentLocation.value = latLng; currentLocation.value = latLng;
isLoadingLocation.value = false; isLoadingLocation.value = false;
submitInspectionResponse?.lat = latLng.latitude.toString();
submitInspectionResponse?.log = latLng.longitude.toString();
setUpNextButtonListeners(); setUpNextButtonListeners();
} catch (e) { } catch (e) {
isLoadingLocation.value = false; isLoadingLocation.value = false;
@@ -390,7 +563,7 @@ class CreateInspectionBottomSheetLogic extends GetxController
} }
} }
Future<void> pickImageFromCamera() async { Future<void> pickImageFromCamera(RxMap<XFile, bool> images) async {
try { try {
final XFile? image = await imagePicker.pickImage( final XFile? image = await imagePicker.pickImage(
source: ImageSource.camera, source: ImageSource.camera,
@@ -400,7 +573,7 @@ class CreateInspectionBottomSheetLogic extends GetxController
); );
if (image != null) { if (image != null) {
pultryImages[image] = false; images[image] = false;
//await uploadImage(image); //await uploadImage(image);
} }
@@ -436,8 +609,137 @@ class CreateInspectionBottomSheetLogic extends GetxController
); );
} }
void removeImage(XFile image) { Future<List<String>?> uploadImageBatch(List<XFile> images) async {
pultryImages.remove(image); if (images.isEmpty) return [];
List<String>? result;
await safeCall(
call: () async {
final urls = await repository.uploadImages(
token: tokenStorageService.accessToken.value ?? '',
images: images,
);
return urls;
},
onSuccess: (urls) {
result = urls;
},
onError: (error, stackTrace) {
result = null;
},
showError: false, // خطاها در uploadAllImages مدیریت می‌شوند
);
return result;
}
Future<bool> uploadAllImages() async {
isUploadingImages.value = true;
uploadProgress.value = 0.0;
uploadStatusMessage.value = 'در حال آپلود عکس‌ها...';
try {
int totalImages = 0;
int uploadedCount = 0;
// شمارش کل عکس‌ها
totalImages =
hallImages.length +
inputWarehouseImages.length +
lossesImages.length +
pultryImages.length;
if (totalImages == 0) {
isUploadingImages.value = false;
return true;
}
// آپلود عکس‌های سالن
if (hallImages.isNotEmpty) {
uploadStatusMessage.value = 'در حال آپلود عکس‌های سالن...';
final hallImageFiles = hallImages.keys.toList();
final hallUrls = await uploadImageBatch(hallImageFiles);
if (hallUrls != null && hallUrls.isNotEmpty) {
submitInspectionResponse?.generalConditionHall?.images = hallUrls;
uploadedCount += hallImageFiles.length;
uploadProgress.value = uploadedCount / totalImages;
} else {
throw Exception('خطا در آپلود عکس‌های سالن');
}
}
// آپلود عکس‌های انبار دان
if (inputWarehouseImages.isNotEmpty) {
uploadStatusMessage.value = 'در حال آپلود عکس‌های انبار دان...';
final inputImageFiles = inputWarehouseImages.keys.toList();
final inputUrls = await uploadImageBatch(inputImageFiles);
if (inputUrls != null && inputUrls.isNotEmpty) {
submitInspectionResponse?.inputStatus?.images = inputUrls;
uploadedCount += inputImageFiles.length;
uploadProgress.value = uploadedCount / totalImages;
} else {
throw Exception('خطا در آپلود عکس‌های انبار دان');
}
}
// آپلود عکس‌های تلفات
if (lossesImages.isNotEmpty) {
uploadStatusMessage.value = 'در حال آپلود عکس‌های تلفات...';
final lossesImageFiles = lossesImages.keys.toList();
final lossesUrls = await uploadImageBatch(lossesImageFiles);
if (lossesUrls != null && lossesUrls.isNotEmpty) {
submitInspectionResponse?.casualties?.images = lossesUrls;
uploadedCount += lossesImageFiles.length;
uploadProgress.value = uploadedCount / totalImages;
} else {
throw Exception('خطا در آپلود عکس‌های تلفات');
}
}
// آپلود عکس‌های مرغداری
if (pultryImages.isNotEmpty) {
uploadStatusMessage.value = 'در حال آپلود عکس‌های مرغداری...';
final poultryImageFiles = pultryImages.keys.toList();
final poultryUrls = await uploadImageBatch(poultryImageFiles);
if (poultryUrls != null && poultryUrls.isNotEmpty) {
// اگر فیلد جداگانه‌ای برای عکس‌های مرغداری نداریم، به generalConditionHall اضافه می‌کنیم
if (submitInspectionResponse?.generalConditionHall?.images == null) {
submitInspectionResponse?.generalConditionHall?.images = [];
}
submitInspectionResponse?.generalConditionHall?.images?.addAll(
poultryUrls,
);
uploadedCount += poultryImageFiles.length;
uploadProgress.value = uploadedCount / totalImages;
} else {
throw Exception('خطا در آپلود عکس‌های مرغداری');
}
}
uploadProgress.value = 1.0;
uploadStatusMessage.value = 'آپلود عکس‌ها با موفقیت انجام شد';
isUploadingImages.value = false;
return true;
} catch (e) {
isUploadingImages.value = false;
uploadStatusMessage.value = 'خطا در آپلود عکس‌ها: ${e.toString()}';
Get.snackbar(
'خطا',
'خطا در آپلود عکس‌ها: ${e.toString()}',
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);
return false;
}
}
void removeImage(RxMap<XFile, bool> images, XFile image) {
images.remove(image);
} }
void setTypeOfDiseaseIndex(String item) { void setTypeOfDiseaseIndex(String item) {
@@ -469,9 +771,10 @@ class CreateInspectionBottomSheetLogic extends GetxController
void setSamplingDone(String value) { void setSamplingDone(String value) {
if (samplingDone.value && value != 'انجام شد') { if (samplingDone.value && value != 'انجام شد') {
casualtiesInformationHeight.value = casualtiesInformationHeight.value =
casualtiesInformationHeight.value -80; casualtiesInformationHeight.value - 80;
} }
samplingDone.value = value == 'انجام شد'; samplingDone.value = value == 'انجام شد';
submitInspectionResponse?.casualties?.samplingDone = samplingDone.value;
if (value == 'انجام شد') { if (value == 'انجام شد') {
casualtiesInformationHeight.value = casualtiesInformationHeight.value =
casualtiesInformationHeight.value + 80; casualtiesInformationHeight.value + 80;
@@ -480,30 +783,45 @@ class CreateInspectionBottomSheetLogic extends GetxController
void setInputStatus(String item) { void setInputStatus(String item) {
inputStatus.value = item; inputStatus.value = item;
submitInspectionResponse?.inputStatus?.inputStatus = item;
} }
void setTypeOfGrain(String item) { void setTypeOfGrain(String item) {
typeOfGrain.value = item; typeOfGrain.value = item;
submitInspectionResponse?.inputStatus?.typeOfGrain = item;
} }
void setFuelType(String item) { void setFuelType(String item) {
fuelType.value = item; fuelType.value = item;
submitInspectionResponse?.infrastructureEnergy?.fuelType = item;
} }
void setPowerCutHistory(String item) { void setPowerCutHistory(String item) {
powerCutHistory.value = item == 'دارد'; powerCutHistory.value = item == 'دارد';
submitInspectionResponse?.infrastructureEnergy?.hasPowerCutHistory =
powerCutHistory.value;
} }
void setTrainingStatus(String item) { void setTrainingStatus(String item) {
trainingStatus.value = item == 'بله'; trainingStatus.value = item == 'بله';
submitInspectionResponse?.hr?.trained = item == 'بله';
submitInspectionResponse?.hr?.trained = trainingStatus.value;
} }
void setNewBeneficiaryRequest(String item) { void setNewBeneficiaryRequest(String item) {
newBeneficiaryRequest.value = item; newBeneficiaryRequest.value = item;
submitInspectionResponse?.facilities?.requestFacilities =
newBeneficiaryRequest.value;
}
void setHasFacilities(String item) {
hasFacilities.value = item == 'دارد';
submitInspectionResponse?.facilities?.hasFacilities = hasFacilities.value;
} }
void setOverdueStatus(String item) { void setOverdueStatus(String item) {
overdueStatus.value = item == 'دارای معوقه'; overdueStatus.value = item == 'دارای معوقه';
submitInspectionResponse?.facilities?.repaymentStatus = item;
} }
void showPaymentYearDatePicker() { void showPaymentYearDatePicker() {
@@ -542,4 +860,117 @@ class CreateInspectionBottomSheetLogic extends GetxController
void removeLossesImage(XFile key) { void removeLossesImage(XFile key) {
lossesImages.remove(key); lossesImages.remove(key);
} }
Future<void> submitInspection() async {
// ابتدا عکس‌ها را آپلود می‌کنیم
final imagesUploaded = await uploadAllImages();
if (!imagesUploaded) {
return; // اگر آپلود عکس‌ها ناموفق بود، متوقف می‌شویم
}
// پر کردن داده‌های فرم
submitInspectionResponse?.generalConditionHall?.temperature =
hatchingTemperatureController.text;
submitInspectionResponse?.generalConditionHall?.drinkingWaterQuality =
waterQuality.value;
submitInspectionResponse?.casualties?.abnormalLosses = int.parse(
abnormalLossesController.text,
);
submitInspectionResponse?.casualties?.normalLosses = int.parse(
normalLossesController.text,
);
if (isOthercauseOfUnusualCasualties.value) {
submitInspectionResponse?.casualties?.causeAbnormalLosses =
otherCauseOfUnusualCasualtiesController.text;
} else {
submitInspectionResponse?.casualties?.causeAbnormalLosses =
causeOfUnusualCasualties.value;
}
if (isOtherTypeOfDiseaseSelected.value) {
submitInspectionResponse?.casualties?.typeDisease =
otherTypeOfDiseaseController.text;
} else {
submitInspectionResponse?.casualties?.typeDisease = typeOfDisease.value;
}
submitInspectionResponse?.technicalOfficer?.technicalHealthOfficer =
technicalHealthOfficerNameController.text;
submitInspectionResponse?.technicalOfficer?.technicalEngineeringOfficer =
technicalEngineeringOfficerNameController.text;
if (inputStatus.value == 'وابسته') {
submitInspectionResponse?.inputStatus?.companyName =
companyNameController.text;
} else {
submitInspectionResponse?.inputStatus?.trackingCode =
trackingCodeController.text;
}
submitInspectionResponse?.inputStatus?.inventoryUntilVisit =
inputInventoryUntilVisitController.text;
submitInspectionResponse?.inputStatus?.inventoryInWarehouse =
inputInventoryInWarehouseController.text;
submitInspectionResponse?.infrastructureEnergy?.generatorType =
generatorTypeController.text;
submitInspectionResponse?.infrastructureEnergy?.generatorModel =
generatorModelController.text;
submitInspectionResponse?.infrastructureEnergy?.generatorCount =
generatorCountController.text;
submitInspectionResponse?.infrastructureEnergy?.generatorCapacity =
generatorCapacityController.text;
submitInspectionResponse?.infrastructureEnergy?.emergencyFuelInventory =
emergencyFuelInventoryController.text;
submitInspectionResponse?.infrastructureEnergy?.powerCutDuration =
powerCutDurationController.text;
submitInspectionResponse?.infrastructureEnergy?.powerCutHour =
powerCutHourController.text;
submitInspectionResponse?.infrastructureEnergy?.additionalNotes =
additionalNotesController.text;
submitInspectionResponse?.hr?.numberEmployed = int.parse(
employedWorkersCountController.text.clearComma,
);
submitInspectionResponse?.hr?.numberIndigenous = int.parse(
nativeWorkersCountController.text.clearComma,
);
submitInspectionResponse?.hr?.numberNonIndigenous = int.parse(
nonNativeWorkersCountController.text.clearComma,
);
iLog(submitInspectionResponse?.toJson());
await safeCall(
call: () async {
await repository.submitInspection(
token: tokenStorageService.accessToken.value ?? '',
request: submitInspectionResponse!,
);
},
onSuccess: (result) {
Get.back();
Future.delayed(Duration(seconds: 2), () { Get.snackbar(
'موفق',
'بازرسی با موفقیت ثبت شد',
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.green,
colorText: Colors.white,
);});
},
onError: (error, stackTrace) {
Get.snackbar(
'خطا',
'خطا در ثبت بازرسی: ${error.toString()}',
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red,
colorText: Colors.white,
);
},
showError: true,
);
}
} }

View File

@@ -13,7 +13,7 @@ Widget step1Page(CreateInspectionBottomSheetLogic controller) {
Container( Container(
height: controller.tenantStatusController.text == 'دارد' height: controller.tenantStatusController.text == 'دارد'
? 570.h ? 588.h
: 445.h, : 445.h,
clipBehavior: Clip.none, clipBehavior: Clip.none,
width: Get.width, width: Get.width,

View File

@@ -14,7 +14,7 @@ Widget step2Page(CreateInspectionBottomSheetLogic controller) {
SizedBox(height: 35.h), SizedBox(height: 35.h),
Container( Container(
height: 580.h, height: 600.h,
clipBehavior: Clip.none, clipBehavior: Clip.none,
width: Get.width, width: Get.width,
child: farmInfoWidget( child: farmInfoWidget(
@@ -46,7 +46,7 @@ Widget step2Page(CreateInspectionBottomSheetLogic controller) {
width: Get.width, width: Get.width,
child: farmInfoWidget( child: farmInfoWidget(
controller: controller, controller: controller,
title: 'بیماری‌ها و وضعیت سلامت', title: 'مسئول فنی ',
child: diseasesAndHealthInformation(controller), child: diseasesAndHealthInformation(controller),
), ),
), ),
@@ -102,7 +102,7 @@ Column generalConditionOfTheHall(CreateInspectionBottomSheetLogic controller) {
right: -4, right: -4,
child: GestureDetector( child: GestureDetector(
onTap: () => onTap: () =>
controller.removeImage(entry.key), controller.removeImage(controller.pultryImages, entry.key),
child: Container( child: Container(
width: 24, width: 24,
height: 24, height: 24,
@@ -144,10 +144,10 @@ Column generalConditionOfTheHall(CreateInspectionBottomSheetLogic controller) {
], ],
), ),
) )
.toList(), ,
// Add image button // Add image button
GestureDetector( GestureDetector(
onTap: () => controller.pickImageFromCamera(), onTap: () => controller.pickImageFromCamera(controller.pultryImages),
child: Container( child: Container(
height: 80.h, height: 80.h,
width: 80.w, width: 80.w,

View File

@@ -14,7 +14,7 @@ Widget step3Page(CreateInspectionBottomSheetLogic controller) {
SizedBox(height: 35.h), SizedBox(height: 35.h),
Container( Container(
height: 350.h, height: 360.h,
clipBehavior: Clip.none, clipBehavior: Clip.none,
width: Get.width, width: Get.width,
child: farmInfoWidget( child: farmInfoWidget(
@@ -27,7 +27,7 @@ Widget step3Page(CreateInspectionBottomSheetLogic controller) {
SizedBox(height: 30.h), SizedBox(height: 30.h),
Container( Container(
height: 600.h, height: 610.h,
clipBehavior: Clip.none, clipBehavior: Clip.none,
width: Get.width, width: Get.width,
child: farmInfoWidget( child: farmInfoWidget(
@@ -51,7 +51,7 @@ Widget step3Page(CreateInspectionBottomSheetLogic controller) {
SizedBox(height: 24.h), SizedBox(height: 24.h),
Container( Container(
height: 320.h, height: 325.h,
clipBehavior: Clip.none, clipBehavior: Clip.none,
width: Get.width, width: Get.width,
child: farmInfoWidget( child: farmInfoWidget(
@@ -461,12 +461,11 @@ Column facilitiesAndSupport(CreateInspectionBottomSheetLogic controller) {
children: [ children: [
SizedBox(height: 1.h), SizedBox(height: 1.h),
RTextField( ResourceOverlayDropdown(
controller: controller.activeFacilityController, items: Resource.success([ 'دارد', 'ندارد']),
label: 'تسهیلات دریافتی فعال', onChanged: (item) => controller.setHasFacilities(item),
filled: true, itemBuilder: (item) => Text(item),
filledColor: AppColor.bgLight, labelBuilder: (selected) => Text(selected ?? ' تسهیلات دریافتی فعال'),
maxLines: 1,
), ),
RTextField( RTextField(

View File

@@ -16,7 +16,7 @@ Widget step4Page(CreateInspectionBottomSheetLogic controller) {
SizedBox(height: 35.h), SizedBox(height: 35.h),
Container( Container(
height: 430.h, height: 440.h,
clipBehavior: Clip.none, clipBehavior: Clip.none,
width: Get.width, width: Get.width,
child: farmInfoWidget( child: farmInfoWidget(
@@ -62,80 +62,60 @@ Column documents(CreateInspectionBottomSheetLogic controller) {
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
spacing: 8, spacing: 8,
children: [ children: [
...controller.hallImages.entries ...controller.hallImages.entries.map(
.map( (entry) => Stack(
(entry) => Stack( children: [
children: [ Container(
Container( height: 80.h,
height: 80.h, width: 80.w,
width: 80.w, decoration: BoxDecoration(
decoration: BoxDecoration( borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(8), border: Border.all(
border: Border.all( width: 1,
width: 1, color: AppColor.blackLightHover,
color: AppColor.blackLightHover,
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.file(
File(entry.key.path),
fit: BoxFit.cover,
),
),
), ),
// Delete button ),
Positioned( child: ClipRRect(
top: -4, borderRadius: BorderRadius.circular(8),
right: -4, child: Image.file(
child: GestureDetector( File(entry.key.path),
onTap: () => fit: BoxFit.cover,
controller.removeImage(entry.key),
child: Container(
width: 24,
height: 24,
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: Icon(
Icons.close,
color: Colors.white,
size: 16,
),
),
),
), ),
// Upload indicator ),
if (entry.value == false)
Positioned.fill(
child: Container(
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5),
borderRadius: BorderRadius.circular(8),
),
child: Center(
child: SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor:
AlwaysStoppedAnimation<Color>(
Colors.white,
),
),
),
),
),
),
],
), ),
) // Delete button
.toList(), Positioned(
top: 4,
left: 4,
child: GestureDetector(
onTap: () => controller.removeImage(
controller.hallImages,
entry.key,
),
child: Container(
width: 24,
height: 24,
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: Icon(
Icons.close,
color: Colors.white,
size: 16,
),
),
),
),
// Upload indicator
],
),
),
// Add image button // Add image button
GestureDetector( GestureDetector(
onTap: () => controller.pickImageFromCamera(), onTap: () =>
controller.pickImageFromCamera(controller.hallImages),
child: Container( child: Container(
height: 80.h, height: 80.h,
width: 80.w, width: 80.w,
@@ -192,80 +172,58 @@ Column documents(CreateInspectionBottomSheetLogic controller) {
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
spacing: 8, spacing: 8,
children: [ children: [
...controller.inputWarehouseImages.entries ...controller.inputWarehouseImages.entries.map(
.map( (entry) => Stack(
(entry) => Stack( children: [
children: [ Container(
Container( height: 80.h,
height: 80.h, width: 80.w,
width: 80.w, decoration: BoxDecoration(
decoration: BoxDecoration( borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(8), border: Border.all(
border: Border.all( width: 1,
width: 1, color: AppColor.blackLightHover,
color: AppColor.blackLightHover,
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.file(
File(entry.key.path),
fit: BoxFit.cover,
),
),
), ),
// Delete button ),
Positioned( child: ClipRRect(
top: -4, borderRadius: BorderRadius.circular(8),
right: -4, child: Image.file(
child: GestureDetector( File(entry.key.path),
onTap: () => controller fit: BoxFit.cover,
.removeInputWarehouseImage(entry.key),
child: Container(
width: 24,
height: 24,
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: Icon(
Icons.close,
color: Colors.white,
size: 16,
),
),
),
), ),
// Upload indicator ),
if (entry.value == false)
Positioned.fill(
child: Container(
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5),
borderRadius: BorderRadius.circular(8),
),
child: Center(
child: SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor:
AlwaysStoppedAnimation<Color>(
Colors.white,
),
),
),
),
),
),
],
), ),
) // Delete button
.toList(), Positioned(
top: 4,
left: 4,
child: GestureDetector(
onTap: () => controller.removeInputWarehouseImage(
entry.key,
),
child: Container(
width: 24,
height: 24,
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: Icon(
Icons.close,
color: Colors.white,
size: 16,
),
),
),
),
],
),
),
// Add image button // Add image button
GestureDetector( GestureDetector(
onTap: () => controller.pickImageFromCamera(), onTap: () => controller.pickImageFromCamera(
controller.inputWarehouseImages,
),
child: Container( child: Container(
height: 80.h, height: 80.h,
width: 80.w, width: 80.w,
@@ -310,80 +268,58 @@ Column documents(CreateInspectionBottomSheetLogic controller) {
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
spacing: 8, spacing: 8,
children: [ children: [
...controller.lossesImages.entries ...controller.lossesImages.entries.map(
.map( (entry) => Stack(
(entry) => Stack( children: [
children: [ Container(
Container( height: 80.h,
height: 80.h, width: 80.w,
width: 80.w, decoration: BoxDecoration(
decoration: BoxDecoration( borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(8), border: Border.all(
border: Border.all( width: 1,
width: 1, color: AppColor.blackLightHover,
color: AppColor.blackLightHover,
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.file(
File(entry.key.path),
fit: BoxFit.cover,
),
),
), ),
// Delete button ),
Positioned( child: ClipRRect(
top: -4, borderRadius: BorderRadius.circular(8),
right: -4, child: Image.file(
child: GestureDetector( File(entry.key.path),
onTap: () => controller fit: BoxFit.cover,
.removeInputWarehouseImage(entry.key),
child: Container(
width: 24,
height: 24,
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: Icon(
Icons.close,
color: Colors.white,
size: 16,
),
),
),
), ),
// Upload indicator ),
if (entry.value == false)
Positioned.fill(
child: Container(
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5),
borderRadius: BorderRadius.circular(8),
),
child: Center(
child: SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor:
AlwaysStoppedAnimation<Color>(
Colors.white,
),
),
),
),
),
),
],
), ),
) // Delete button
.toList(), Positioned(
top: 4,
left: 4,
child: GestureDetector(
onTap: () => controller.removeInputWarehouseImage(
entry.key,
),
child: Container(
width: 24,
height: 24,
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: Icon(
Icons.close,
color: Colors.white,
size: 16,
),
),
),
),
],
),
),
// Add image button // Add image button
GestureDetector( GestureDetector(
onTap: () => controller.pickImageFromCamera(), onTap: () => controller.pickImageFromCamera(
controller.lossesImages,
),
child: Container( child: Container(
height: 80.h, height: 80.h,
width: 80.w, width: 80.w,

View File

@@ -37,10 +37,11 @@ class DioRemote implements IHttpClient {
PrettyDioLogger( PrettyDioLogger(
request: true, request: true,
enabled: true, enabled: true,
error: false,
requestHeader: true, requestHeader: true,
responseHeader: true, responseHeader: true,
requestBody: true, requestBody: true,
responseBody: true, responseBody: false,
), ),
); );
} }