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

@@ -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(
title: "بازرسی مزارع طیور",
route: ChickenRoutes.poultryFarmInspectionHome,
route: PoultryScienceRoutes.newInspectionPoultryScience,
icon: Assets.vec.activeFramSvg.path,
),
].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/inspection/logic.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/view.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(
name: PoultryScienceRoutes.actionPoultryScience,
page: () => PoultryActionPage(),

View File

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

View File

@@ -16,7 +16,6 @@ class CreateInspectionBottomSheet
step2Page(controller),
step3Page(controller),
step4Page(controller),
step5Page(controller),
];
@override
@@ -47,20 +46,40 @@ class CreateInspectionBottomSheet
child: ObxValue((data) {
return RElevated(
height: 40.h,
enabled: data.value,
enabled:
data.value && !controller.isUploadingImages.value,
backgroundColor: AppColor.greenNormal,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.arrow_back_ios, color: Colors.white),
Text('ادامه'),
if (controller.isUploadingImages.value)
SizedBox(
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: () {
if (controller.activeStepperIndex.value < 4) {
if (controller.activeStepperIndex.value < 3) {
controller.activeStepperIndex.value++;
} else {
controller.submitInspection();
}
},
);
@@ -71,7 +90,9 @@ class CreateInspectionBottomSheet
borderColor: AppColor.error,
height: 40.h,
child: Text('برگشت'),
enabled: controller.activeStepperIndex.value > 0,
enabled:
controller.activeStepperIndex.value > 0 &&
!controller.isUploadingImages.value,
onPressed: () {
if (controller.activeStepperIndex.value > 0) {
controller.activeStepperIndex.value--;
@@ -83,6 +104,53 @@ class CreateInspectionBottomSheet
),
);
}, 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(
color: activeStep >= 4
? AppColor.greenNormalHover
@@ -207,7 +275,7 @@ class stepper extends StatelessWidget {
thickness: 8,
),
),
Container(
Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: activeStep >= 4
@@ -224,7 +292,7 @@ class stepper extends StatelessWidget {
color: activeStep >= 3 ? Colors.white : AppColor.iconColor,
),
),
),
), */
],
),
),

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.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/repositories/poultry_science_repository.dart';
import 'package:rasadyar_core/core.dart';
@@ -12,10 +13,9 @@ class CreateInspectionBottomSheetLogic extends GetxController
.get<PoultryScienceRepository>();
TokenStorageService tokenStorageService = Get.find<TokenStorageService>();
var gService = Get.find<GService>();
RxInt selectedSegmentIndex = 0.obs;
RxInt expandedIndex = RxInt(-1);
late TabController tabController;
@@ -28,7 +28,14 @@ class CreateInspectionBottomSheetLogic extends GetxController
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
@@ -155,6 +162,7 @@ class CreateInspectionBottomSheetLogic extends GetxController
RxBool trainingStatus = false.obs;
RxnString newBeneficiaryRequest = RxnString();
RxBool overdueStatus = false.obs;
RxBool hasFacilities = false.obs;
TextEditingController paymentYearController = TextEditingController();
Rx<Jalali> selectedPaymentYear = Jalali.now().obs;
@@ -213,11 +221,164 @@ class CreateInspectionBottomSheetLogic extends GetxController
@override
void onClose() {
// TODO: implement onClose
resetAllData();
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() {
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 =
hatchingModel?.poultry?.unitName ?? 'واحد مرغداری نامشخص';
@@ -296,6 +457,8 @@ class CreateInspectionBottomSheetLogic extends GetxController
void setSanitaryConditionOfTheHallIndex(int index, String item) {
sanitaryConditionOfTheHall.value = item;
submitInspectionResponse?.generalConditionHall?.healthStatus = item;
sanitaryConditionOfTheHallIndex.value =
index == sanitaryConditionOfTheHallIndex.value ? -1 : index;
}
@@ -307,6 +470,7 @@ class CreateInspectionBottomSheetLogic extends GetxController
void setVentilationStatusIndex(int index, String item) {
ventilationStatus.value = item;
submitInspectionResponse?.generalConditionHall?.ventilationStatus = item;
ventilationStatusIndex.value = index == ventilationStatusIndex.value
? -1
: index;
@@ -314,21 +478,25 @@ class CreateInspectionBottomSheetLogic extends GetxController
void setBeddingStatusIndex(int index, String item) {
beddingStatus.value = item;
submitInspectionResponse?.generalConditionHall?.bedCondition = item;
beddingStatusIndex.value = index == beddingStatusIndex.value ? -1 : index;
}
void setWaterQualityIndex(int index, String item) {
waterQuality.value = item;
submitInspectionResponse?.generalConditionHall?.drinkingWaterSource = item;
waterQualityIndex.value = index == waterQualityIndex.value ? -1 : index;
}
void setSampleTypeIndex(int index, String item) {
sampleType.value = item;
submitInspectionResponse?.casualties?.typeSampling = item;
sampleTypeIndex.value = index == sampleTypeIndex.value ? -1 : index;
}
void setGrainQualityInput(int index, String item) {
grainQualityInput.value = item;
submitInspectionResponse?.inputStatus?.gradeGrain = item;
grainQualityInputIndex.value = index == grainQualityInputIndex.value
? -1
: index;
@@ -336,13 +504,14 @@ class CreateInspectionBottomSheetLogic extends GetxController
void setGeneratorOperatingStatusIndex(int index, String item) {
generatorOperatingStatus.value = item;
submitInspectionResponse?.infrastructureEnergy?.generatorPerformance = item;
generatorOperatingStatusIndex.value =
index == generatorOperatingStatusIndex.value ? -1 : index;
}
void setWorkerContractStatusIndex(int index, String item) {
workerContractStatus.value = item;
submitInspectionResponse?.hr?.contractStatus = item;
workerContractStatusIndex.value = index == workerContractStatusIndex.value
? -1
: index;
@@ -374,6 +543,10 @@ class CreateInspectionBottomSheetLogic extends GetxController
final latLng = await determineCurrentLatLng();
currentLocation.value = latLng;
isLoadingLocation.value = false;
submitInspectionResponse?.lat = latLng.latitude.toString();
submitInspectionResponse?.log = latLng.longitude.toString();
setUpNextButtonListeners();
} catch (e) {
isLoadingLocation.value = false;
@@ -390,7 +563,7 @@ class CreateInspectionBottomSheetLogic extends GetxController
}
}
Future<void> pickImageFromCamera() async {
Future<void> pickImageFromCamera(RxMap<XFile, bool> images) async {
try {
final XFile? image = await imagePicker.pickImage(
source: ImageSource.camera,
@@ -400,7 +573,7 @@ class CreateInspectionBottomSheetLogic extends GetxController
);
if (image != null) {
pultryImages[image] = false;
images[image] = false;
//await uploadImage(image);
}
@@ -436,8 +609,137 @@ class CreateInspectionBottomSheetLogic extends GetxController
);
}
void removeImage(XFile image) {
pultryImages.remove(image);
Future<List<String>?> uploadImageBatch(List<XFile> images) async {
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) {
@@ -469,9 +771,10 @@ class CreateInspectionBottomSheetLogic extends GetxController
void setSamplingDone(String value) {
if (samplingDone.value && value != 'انجام شد') {
casualtiesInformationHeight.value =
casualtiesInformationHeight.value -80;
casualtiesInformationHeight.value - 80;
}
samplingDone.value = value == 'انجام شد';
submitInspectionResponse?.casualties?.samplingDone = samplingDone.value;
if (value == 'انجام شد') {
casualtiesInformationHeight.value =
casualtiesInformationHeight.value + 80;
@@ -480,30 +783,45 @@ class CreateInspectionBottomSheetLogic extends GetxController
void setInputStatus(String item) {
inputStatus.value = item;
submitInspectionResponse?.inputStatus?.inputStatus = item;
}
void setTypeOfGrain(String item) {
typeOfGrain.value = item;
submitInspectionResponse?.inputStatus?.typeOfGrain = item;
}
void setFuelType(String item) {
fuelType.value = item;
submitInspectionResponse?.infrastructureEnergy?.fuelType = item;
}
void setPowerCutHistory(String item) {
powerCutHistory.value = item == 'دارد';
submitInspectionResponse?.infrastructureEnergy?.hasPowerCutHistory =
powerCutHistory.value;
}
void setTrainingStatus(String item) {
trainingStatus.value = item == 'بله';
submitInspectionResponse?.hr?.trained = item == 'بله';
submitInspectionResponse?.hr?.trained = trainingStatus.value;
}
void setNewBeneficiaryRequest(String 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) {
overdueStatus.value = item == 'دارای معوقه';
submitInspectionResponse?.facilities?.repaymentStatus = item;
}
void showPaymentYearDatePicker() {
@@ -542,4 +860,117 @@ class CreateInspectionBottomSheetLogic extends GetxController
void removeLossesImage(XFile 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(
height: controller.tenantStatusController.text == 'دارد'
? 570.h
? 588.h
: 445.h,
clipBehavior: Clip.none,
width: Get.width,

View File

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

View File

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

View File

@@ -16,7 +16,7 @@ Widget step4Page(CreateInspectionBottomSheetLogic controller) {
SizedBox(height: 35.h),
Container(
height: 430.h,
height: 440.h,
clipBehavior: Clip.none,
width: Get.width,
child: farmInfoWidget(
@@ -62,80 +62,60 @@ Column documents(CreateInspectionBottomSheetLogic controller) {
mainAxisAlignment: MainAxisAlignment.start,
spacing: 8,
children: [
...controller.hallImages.entries
.map(
(entry) => Stack(
children: [
Container(
height: 80.h,
width: 80.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(
width: 1,
color: AppColor.blackLightHover,
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.file(
File(entry.key.path),
fit: BoxFit.cover,
),
),
...controller.hallImages.entries.map(
(entry) => Stack(
children: [
Container(
height: 80.h,
width: 80.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(
width: 1,
color: AppColor.blackLightHover,
),
// Delete button
Positioned(
top: -4,
right: -4,
child: GestureDetector(
onTap: () =>
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,
),
),
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.file(
File(entry.key.path),
fit: BoxFit.cover,
),
// 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,
),
),
),
),
),
),
],
),
),
)
.toList(),
// Delete button
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
GestureDetector(
onTap: () => controller.pickImageFromCamera(),
onTap: () =>
controller.pickImageFromCamera(controller.hallImages),
child: Container(
height: 80.h,
width: 80.w,
@@ -192,80 +172,58 @@ Column documents(CreateInspectionBottomSheetLogic controller) {
mainAxisAlignment: MainAxisAlignment.start,
spacing: 8,
children: [
...controller.inputWarehouseImages.entries
.map(
(entry) => Stack(
children: [
Container(
height: 80.h,
width: 80.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(
width: 1,
color: AppColor.blackLightHover,
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.file(
File(entry.key.path),
fit: BoxFit.cover,
),
),
...controller.inputWarehouseImages.entries.map(
(entry) => Stack(
children: [
Container(
height: 80.h,
width: 80.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(
width: 1,
color: AppColor.blackLightHover,
),
// Delete button
Positioned(
top: -4,
right: -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,
),
),
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.file(
File(entry.key.path),
fit: BoxFit.cover,
),
// 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,
),
),
),
),
),
),
],
),
),
)
.toList(),
// Delete button
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
GestureDetector(
onTap: () => controller.pickImageFromCamera(),
onTap: () => controller.pickImageFromCamera(
controller.inputWarehouseImages,
),
child: Container(
height: 80.h,
width: 80.w,
@@ -310,80 +268,58 @@ Column documents(CreateInspectionBottomSheetLogic controller) {
mainAxisAlignment: MainAxisAlignment.start,
spacing: 8,
children: [
...controller.lossesImages.entries
.map(
(entry) => Stack(
children: [
Container(
height: 80.h,
width: 80.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(
width: 1,
color: AppColor.blackLightHover,
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.file(
File(entry.key.path),
fit: BoxFit.cover,
),
),
...controller.lossesImages.entries.map(
(entry) => Stack(
children: [
Container(
height: 80.h,
width: 80.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(
width: 1,
color: AppColor.blackLightHover,
),
// Delete button
Positioned(
top: -4,
right: -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,
),
),
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.file(
File(entry.key.path),
fit: BoxFit.cover,
),
// 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,
),
),
),
),
),
),
],
),
),
)
.toList(),
// Delete button
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
GestureDetector(
onTap: () => controller.pickImageFromCamera(),
onTap: () => controller.pickImageFromCamera(
controller.lossesImages,
),
child: Container(
height: 80.h,
width: 80.w,