diff --git a/android/local.properties b/android/local.properties index b51c4ef..194f5e3 100644 --- a/android/local.properties +++ b/android/local.properties @@ -1,5 +1,5 @@ -sdk.dir=C:\\Users\\Housh11\\AppData\\Local\\Android\\sdk -flutter.sdk=C:\\src\\flutter +sdk.dir=/Users/mojtaba/Library/Android/sdk +flutter.sdk=/Users/mojtaba/develop/flutter flutter.buildMode=debug flutter.versionName=1.3.35 flutter.versionCode=32 \ No newline at end of file diff --git a/packages/chicken/lib/features/poultry_science/data/datasources/remote/poultry_science_remote_data_source.dart b/packages/chicken/lib/features/poultry_science/data/datasources/remote/poultry_science_remote_data_source.dart index 33ca503..200825a 100644 --- a/packages/chicken/lib/features/poultry_science/data/datasources/remote/poultry_science_remote_data_source.dart +++ b/packages/chicken/lib/features/poultry_science/data/datasources/remote/poultry_science_remote_data_source.dart @@ -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/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/request/submit_inspection/submit_inspection_response.dart'; import 'package:rasadyar_core/core.dart'; abstract class PoultryScienceRemoteDataSource { @@ -94,4 +95,14 @@ abstract class PoultryScienceRemoteDataSource { required String token, required List images, }); + + Future?> getSubmitInspectionList({ + required String token, + Map? queryParameters, + }); + + Future submitInspection({ + required String token, + required SubmitInspectionResponse request, + }); } diff --git a/packages/chicken/lib/features/poultry_science/data/datasources/remote/poultry_science_remote_data_source_impl.dart b/packages/chicken/lib/features/poultry_science/data/datasources/remote/poultry_science_remote_data_source_impl.dart index 6b8f532..230e349 100644 --- a/packages/chicken/lib/features/poultry_science/data/datasources/remote/poultry_science_remote_data_source_impl.dart +++ b/packages/chicken/lib/features/poultry_science/data/datasources/remote/poultry_science_remote_data_source_impl.dart @@ -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/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/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_core/core.dart'; @@ -261,5 +262,35 @@ class PoultryScienceRemoteDataSourceImpl return res.data; } + @override + Future?> getSubmitInspectionList({ + required String token, + Map? queryParameters, + }) async { + var res = await _httpClient.get( + '/poultry_science_report/', + headers: {'Authorization': 'Bearer $token'}, + queryParameters: queryParameters, + fromJson: (json) => PaginationModel.fromJson( + json, + (json) => + SubmitInspectionResponse.fromJson(json as Map), + ), + ); + return res.data; + } + + @override + Future submitInspection({ + required String token, + required SubmitInspectionResponse request, + }) async { + await _httpClient.post( + '/poultry_science_report/', + headers: {'Authorization': 'Bearer $token'}, + data: request.toJson(), + ); + } + //endregion } diff --git a/packages/chicken/lib/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart b/packages/chicken/lib/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart new file mode 100644 index 0000000..9cf0dac --- /dev/null +++ b/packages/chicken/lib/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart @@ -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 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 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, + ) + : null, + casualties: json['casualties'] != null + ? Casualties.fromJson(json['casualties'] as Map) + : null, + technicalOfficer: json['technical_officer'] != null + ? TechnicalOfficer.fromJson( + json['technical_officer'] as Map, + ) + : null, + inputStatus: json['input_status'] != null + ? InputStatus.fromJson(json['input_status'] as Map) + : null, + infrastructureEnergy: json['infrastructure_energy'] != null + ? InfrastructureEnergy.fromJson( + json['infrastructure_energy'] as Map, + ) + : null, + hr: json['hr'] != null + ? Hr.fromJson(json['hr'] as Map) + : null, + facilities: json['facilities'] != null + ? Facilities.fromJson(json['facilities'] as Map) + : 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? images; + String? healthStatus; + String? ventilationStatus; + String? bedCondition; + String? temperature; + String? drinkingWaterSource; + String? drinkingWaterQuality; + + Map 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 json) { + return GeneralConditionHall( + images: json['images'] != null + ? List.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? images; + + Map 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 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.from(json['images'] as List) + : null, + ); + } +} + +class TechnicalOfficer { + TechnicalOfficer({ + this.technicalHealthOfficer, + this.technicalEngineeringOfficer, + }); + + String? technicalHealthOfficer; + String? technicalEngineeringOfficer; + + Map toJson() { + return { + 'technical_health_officer': technicalHealthOfficer, + 'technical_engineering_officer': technicalEngineeringOfficer, + }; + } + + factory TechnicalOfficer.fromJson(Map 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? images; + + Map 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 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.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 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 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 toJson() { + return { + 'number_employed': numberEmployed, + 'number_indigenous': numberIndigenous, + 'number_non_indigenous': numberNonIndigenous, + 'contract_status': contractStatus, + 'trained': trained, + }; + } + + factory Hr.fromJson(Map 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 toJson() { + return { + 'has_facilities': hasFacilities, + 'type_of_facility': typeOfFacility, + 'amount': amount, + 'date': date, + 'repayment_status': repaymentStatus, + 'request_facilities': requestFacilities, + }; + } + + factory Facilities.fromJson(Map 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?, + ); + } +} diff --git a/packages/chicken/lib/features/poultry_science/data/repositories/poultry_science_repository.dart b/packages/chicken/lib/features/poultry_science/data/repositories/poultry_science_repository.dart index 743134e..913c6fe 100644 --- a/packages/chicken/lib/features/poultry_science/data/repositories/poultry_science_repository.dart +++ b/packages/chicken/lib/features/poultry_science/data/repositories/poultry_science_repository.dart @@ -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/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/request/submit_inspection/submit_inspection_response.dart'; import 'package:rasadyar_core/core.dart'; abstract class PoultryScienceRepository { @@ -94,4 +95,14 @@ abstract class PoultryScienceRepository { required String token, required List images, }); + + Future?> getSubmitInspectionList({ + required String token, + Map? queryParameters, + }); + + Future submitInspection({ + required String token, + required SubmitInspectionResponse request, + }); } diff --git a/packages/chicken/lib/features/poultry_science/data/repositories/poultry_science_repository_impl.dart b/packages/chicken/lib/features/poultry_science/data/repositories/poultry_science_repository_impl.dart index cba2334..c5b9e81 100644 --- a/packages/chicken/lib/features/poultry_science/data/repositories/poultry_science_repository_impl.dart +++ b/packages/chicken/lib/features/poultry_science/data/repositories/poultry_science_repository_impl.dart @@ -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/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/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/repositories/poultry_science_repository.dart'; import 'package:rasadyar_core/core.dart'; @@ -188,4 +189,23 @@ class PoultryScienceRepositoryImpl implements PoultryScienceRepository { }) async { return await _dataSource.uploadImages(token: token, images: images); } + + @override + Future?> getSubmitInspectionList({ + required String token, + Map? queryParameters, + }) async { + return await _dataSource.getSubmitInspectionList( + token: token, + queryParameters: queryParameters, + ); + } + + @override + Future submitInspection({ + required String token, + required SubmitInspectionResponse request, + }) async { + return await _dataSource.submitInspection(token: token, request: request); + } } diff --git a/packages/chicken/lib/features/poultry_science/presentation/pages/new_inspection/logic.dart b/packages/chicken/lib/features/poultry_science/presentation/pages/new_inspection/logic.dart new file mode 100644 index 0000000..f6704f3 --- /dev/null +++ b/packages/chicken/lib/features/poultry_science/presentation/pages/new_inspection/logic.dart @@ -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(); + + Rx>> submitInspectionList = + Resource>.loading().obs; + + PoultryScienceRootLogic rootLogic = Get.find(); + + final RxBool isLoadingMoreAllocationsMade = false.obs; + RxInt currentPage = 1.obs; + RxInt expandedIndex = RxInt(-1); + RxList pickedImages = [].obs; + final List _multiPartPickedImages = []; + + RxBool isOnUpload = false.obs; + + RxDouble presentUpload = 0.0.obs; + RxList routesName = RxList(); + RxInt selectedSegmentIndex = 0.obs; + + RxnString searchedValue = RxnString(); + Rx fromDateFilter = Jalali.now().obs; + Rx 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 getReport([bool isLoadingMore = false]) async { + if (isLoadingMore) { + isLoadingMoreAllocationsMade.value = true; + } else { + submitInspectionList.value = + Resource>.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>.empty(); + } else { + submitInspectionList.value = + Resource>.success( + PaginationModel( + count: res?.count ?? 0, + next: res?.next, + previous: res?.previous, + results: [ + ...(submitInspectionList.value.data?.results ?? []), + ...(res?.results ?? []), + ], + ), + ); + } + }, + ); + } + + Future 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 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; + } +} diff --git a/packages/chicken/lib/features/poultry_science/presentation/pages/new_inspection/view.dart b/packages/chicken/lib/features/poultry_science/presentation/pages/new_inspection/view.dart new file mode 100644 index 0000000..d8eb56c --- /dev/null +++ b/packages/chicken/lib/features/poultry_science/presentation/pages/new_inspection/view.dart @@ -0,0 +1,1035 @@ +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/presentation/utils/nested_keys_utils.dart'; +import 'package:rasadyar_chicken/presentation/widget/base_page/view.dart'; +import 'package:rasadyar_core/core.dart'; + +import 'logic.dart'; + +class NewInspectionPoultrySciencePage + extends GetView { + const NewInspectionPoultrySciencePage({super.key}); + + @override + Widget build(BuildContext context) { + return ChickenBasePage( + hasBack: true, + hasFilter: true, + hasSearch: true, + onFilterTap: () { + Get.bottomSheet(filterBottomSheet()); + }, + onRefresh: controller.onRefresh, + onSearchChanged: (data) => controller.setSearchValue(data), + backId: poultryFirstKey, + routesWidget: ContainerBreadcrumb(rxRoutes: controller.routesName), + child: Column(children: [reportWidget()]), + ); + } + + Widget reportWidget() { + return Expanded( + child: ObxValue((data) { + return RPaginatedListView( + listType: ListType.separated, + resource: data.value, + hasMore: data.value.data?.next != null, + padding: EdgeInsets.fromLTRB(8, 8, 8, 80), + itemBuilder: (context, index) { + var item = data.value.data!.results![index]; + return ObxValue((val) { + return ExpandableListItem2( + selected: val.value.isEqual(index), + onTap: () => controller.toggleExpanded(index), + index: index, + child: itemListWidgetReport(item), + secondChild: itemListExpandedWidgetReport(item), + labelColor: AppColor.greenLight, + labelIcon: Assets.vec.cubeSearchSvg.path, + ); + }, controller.expandedIndex); + }, + itemCount: data.value.data?.results?.length ?? 0, + separatorBuilder: (context, index) => SizedBox(height: 8.h), + onLoadMore: () async => controller.getReport(true), + ); + }, controller.submitInspectionList), + ); + } + + Widget itemListExpandedWidgetReport(SubmitInspectionResponse item) { + return Container( + padding: EdgeInsets.symmetric(horizontal: 8), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + child: Column( + spacing: 8, + children: [ + buildRow( + title: 'وضعیت بازرسی', + value: controller.getStatus(item), + titleStyle: AppFonts.yekan14.copyWith( + color: controller.getStatusColor(item), + ), + valueStyle: AppFonts.yekan14.copyWith( + color: controller.getStatusColor(item), + ), + ), + if (item.poultryHatchingId != null) + buildRow( + title: 'شناسه جوجه‌ریزی', + value: item.poultryHatchingId.toString(), + ), + if (item.role != null) + buildRow(title: 'نقش', value: item.role ?? '-'), + if (item.technicalOfficer?.technicalHealthOfficer != null) + buildRow( + title: 'کارشناس بهداشت', + value: item.technicalOfficer!.technicalHealthOfficer ?? '-', + ), + if (item.technicalOfficer?.technicalEngineeringOfficer != null) + buildRow( + title: 'کارشناس فنی', + value: item.technicalOfficer!.technicalEngineeringOfficer ?? '-', + ), + if (item.casualties?.normalLosses != null || + item.casualties?.abnormalLosses != null) + buildUnitRow( + title: 'تلفات عادی', + value: (item.casualties?.normalLosses ?? 0).toString(), + unit: '(قطعه)', + ), + if (item.casualties?.abnormalLosses != null) + buildUnitRow( + title: 'تلفات غیرعادی', + value: item.casualties!.abnormalLosses.toString(), + unit: '(قطعه)', + ), + if (item.lat != null && item.log != null) + buildRow(title: 'موقعیت', value: '${item.lat}, ${item.log}'), + if (item.inspectionNotes != null && item.inspectionNotes!.isNotEmpty) + buildRow( + title: 'یادداشت بازرسی', + value: item.inspectionNotes ?? '-', + ), + + RElevated( + text: 'جزییات', + isFullWidth: true, + width: 150.w, + height: 40.h, + onPressed: () { + showDetailsBottomSheet(item); + }, + textStyle: AppFonts.yekan16.copyWith(color: Colors.white), + backgroundColor: AppColor.blueNormal, + ), + ], + ), + ); + } + + Row itemListWidgetReport(SubmitInspectionResponse item) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + SizedBox(width: 20), + Expanded( + flex: 2, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 5, + children: [ + Text( + item.role ?? 'نقش نامشخص', + textAlign: TextAlign.start, + style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal), + ), + Text( + controller.getStatus(item), + textAlign: TextAlign.start, + style: AppFonts.yekan14.copyWith( + color: controller.getStatusColor(item), + ), + ), + ], + ), + ), + Expanded( + flex: 3, + child: Column( + spacing: 5, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + 'شناسه جوجه‌ریزی: ${item.poultryHatchingId ?? 'N/A'}', + textAlign: TextAlign.center, + style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal), + ), + if (item.technicalOfficer?.technicalHealthOfficer != null) + Text( + 'کارشناس: ${item.technicalOfficer!.technicalHealthOfficer}', + textAlign: TextAlign.center, + style: AppFonts.yekan12.copyWith(color: AppColor.bgDark), + ) + else + Text( + item.inspectionStatus ?? 'وضعیت نامشخص', + textAlign: TextAlign.center, + style: AppFonts.yekan12.copyWith(color: AppColor.bgDark), + ), + ], + ), + ), + Expanded( + flex: 1, + child: Assets.vec.scanSvg.svg( + width: 32.w, + height: 32.h, + colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn), + ), + ), + ], + ); + } + + void showDetailsBottomSheet(SubmitInspectionResponse item) { + Get.bottomSheet( + BaseBottomSheet( + height: Get.height * 0.8, + rootChild: DetailsBottomSheetWidget(item: item), + ), + ); + } + + Widget filterBottomSheet() { + return BaseBottomSheet( + height: 200, + child: Column( + spacing: 16, + children: [ + Text( + 'فیلترها', + style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal), + ), + Row( + spacing: 8, + children: [ + Expanded( + child: dateFilterWidget( + date: controller.fromDateFilter, + onChanged: (jalali) => + controller.fromDateFilter.value = jalali, + ), + ), + Expanded( + child: dateFilterWidget( + isFrom: false, + date: controller.toDateFilter, + onChanged: (jalali) => controller.toDateFilter.value = jalali, + ), + ), + ], + ), + RElevated( + text: 'اعمال فیلتر', + isFullWidth: true, + backgroundColor: AppColor.greenNormal, + onPressed: () { + controller.getReport(); + Get.back(); + }, + height: 40, + ), + ], + ), + ); + } +} + +class DetailsBottomSheetWidget extends StatefulWidget { + final SubmitInspectionResponse item; + + const DetailsBottomSheetWidget({super.key, required this.item}); + + @override + State createState() => + _DetailsBottomSheetWidgetState(); +} + +class _DetailsBottomSheetWidgetState extends State { + int selectedTabIndex = 0; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 4, + vertical: 10, + ), + child: Text( + 'جزییات', + style: AppFonts.yekan18Bold.copyWith( + color: AppColor.iconColor, + ), + ), + ), + ], + ), + Divider(color: AppColor.blackLightHover, height: 1, thickness: 1), + tabBarWidget( + [ + 'اطلاعات کلی', + 'شرایط سالن', + 'تلفات', + 'کارشناس', + 'نهاده', + 'زیرساخت', + 'نیروی انسانی', + 'تسهیلات', + ], + selectedTabIndex, + (index) => setState(() => selectedTabIndex = index), + ), + ], + ), + ), + Expanded( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: _buildTableContent(), + ), + ), + ), + ], + ); + } + + Widget _buildTableContent() { + switch (selectedTabIndex) { + case 0: + return generalInfoTable(); + case 1: + return generalConditionHallTable(); + case 2: + return casualtiesTable(); + case 3: + return technicalOfficerTable(); + case 4: + return inputStatusTable(); + case 5: + return infrastructureEnergyTable(); + case 6: + return hrTable(); + case 7: + return facilitiesTable(); + default: + return generalInfoTable(); + } + } + + Widget technicalOfficerTable() { + final officer = widget.item.technicalOfficer; + if (officer == null) { + return Center( + child: Padding( + padding: const EdgeInsets.all(20), + child: Text( + 'اطلاعاتی وجود ندارد', + style: AppFonts.yekan14.copyWith(color: AppColor.textColor), + ), + ), + ); + } + + return Column( + children: [ + SizedBox(height: 10), + Row( + children: [ + Text( + 'کارشناس فنی', + style: AppFonts.yekan16Bold.copyWith(color: AppColor.iconColor), + ), + ], + ), + SizedBox(height: 10), + Container( + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + child: Column( + children: [ + rTableRow( + title: 'کارشناس بهداشت', + value: officer.technicalHealthOfficer ?? '-', + ), + rTableRow( + title: 'کارشناس فنی', + value: officer.technicalEngineeringOfficer ?? '-', + ), + ], + ), + ), + ], + ); + } + + Widget generalInfoTable() { + return Column( + children: [ + SizedBox(height: 10), + Row( + children: [ + Text( + 'اطلاعات کلی', + style: AppFonts.yekan16Bold.copyWith(color: AppColor.iconColor), + ), + ], + ), + SizedBox(height: 10), + Container( + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + child: Column( + children: [ + rTableRow( + title: 'وضعیت بازرسی', + value: widget.item.inspectionStatus ?? '-', + ), + rTableRow( + title: 'یادداشت بازرسی', + value: widget.item.inspectionNotes ?? '-', + ), + rTableRow(title: 'نقش', value: widget.item.role ?? '-'), + if (widget.item.lat != null && widget.item.log != null) + rTableRow( + title: 'موقعیت', + value: '${widget.item.lat}, ${widget.item.log}', + ), + if (widget.item.poultryHatchingId != null) + rTableRow( + title: 'شناسه جوجه ریزی', + value: widget.item.poultryHatchingId.toString(), + ), + ], + ), + ), + ], + ); + } + + Widget generalConditionHallTable() { + final hall = widget.item.generalConditionHall; + if (hall == null) { + return Center( + child: Padding( + padding: const EdgeInsets.all(20), + child: Text( + 'اطلاعاتی وجود ندارد', + style: AppFonts.yekan14.copyWith(color: AppColor.textColor), + ), + ), + ); + } + + return Column( + children: [ + SizedBox(height: 10), + Row( + children: [ + Text( + 'شرایط عمومی سالن', + style: AppFonts.yekan16Bold.copyWith(color: AppColor.iconColor), + ), + ], + ), + SizedBox(height: 10), + Container( + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + child: Column( + children: [ + rTableRow(title: 'وضعیت سلامت', value: hall.healthStatus ?? '-'), + rTableRow( + title: 'وضعیت تهویه', + value: hall.ventilationStatus ?? '-', + ), + rTableRow(title: 'وضعیت بستر', value: hall.bedCondition ?? '-'), + rTableRow(title: 'دما', value: hall.temperature ?? '-'), + rTableRow( + title: 'منبع آب آشامیدنی', + value: hall.drinkingWaterSource ?? '-', + ), + rTableRow( + title: 'کیفیت آب آشامیدنی', + value: hall.drinkingWaterQuality ?? '-', + ), + ], + ), + ), + if (hall.images != null && hall.images!.isNotEmpty) ...[ + SizedBox(height: 16), + Text( + 'تصاویر', + style: AppFonts.yekan14Bold.copyWith(color: AppColor.textColor), + ), + SizedBox(height: 10), + SizedBox( + height: 100.h, + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: hall.images!.length, + separatorBuilder: (context, index) => SizedBox(width: 10), + itemBuilder: (context, index) => Container( + width: 80.w, + height: 80.h, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + image: DecorationImage( + fit: BoxFit.cover, + image: NetworkImage(hall.images![index]), + ), + ), + ), + ), + ), + ], + ], + ); + } + + Widget casualtiesTable() { + final casualties = widget.item.casualties; + if (casualties == null) { + return Center( + child: Padding( + padding: const EdgeInsets.all(20), + child: Text( + 'اطلاعاتی وجود ندارد', + style: AppFonts.yekan14.copyWith(color: AppColor.textColor), + ), + ), + ); + } + + return Column( + children: [ + SizedBox(height: 10), + Row( + children: [ + Text( + 'تلفات', + style: AppFonts.yekan16Bold.copyWith(color: AppColor.iconColor), + ), + ], + ), + SizedBox(height: 10), + Container( + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + child: Column( + children: [ + if (casualties.normalLosses != null) + rTableRow( + title: 'تلفات عادی', + value: casualties.normalLosses.toString(), + ), + if (casualties.abnormalLosses != null) + rTableRow( + title: 'تلفات غیرعادی', + value: casualties.abnormalLosses.toString(), + ), + rTableRow( + title: 'منبع جوجه ریزی', + value: casualties.sourceOfHatching ?? '-', + ), + rTableRow( + title: 'علت تلفات غیرعادی', + value: casualties.causeAbnormalLosses ?? '-', + ), + rTableRow( + title: 'نوع بیماری', + value: casualties.typeDisease ?? '-', + ), + rTableRow( + title: 'نمونه‌برداری انجام شده', + value: casualties.samplingDone == true ? 'بله' : 'خیر', + ), + rTableRow( + title: 'نوع نمونه‌برداری', + value: casualties.typeSampling ?? '-', + ), + ], + ), + ), + if (casualties.images != null && casualties.images!.isNotEmpty) ...[ + SizedBox(height: 16), + Text( + 'تصاویر', + style: AppFonts.yekan14Bold.copyWith(color: AppColor.textColor), + ), + SizedBox(height: 10), + SizedBox( + height: 100.h, + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: casualties.images!.length, + separatorBuilder: (context, index) => SizedBox(width: 10), + itemBuilder: (context, index) => Container( + width: 80.w, + height: 80.h, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + image: DecorationImage( + fit: BoxFit.cover, + image: NetworkImage(casualties.images![index]), + ), + ), + ), + ), + ), + ], + ], + ); + } + + Widget inputStatusTable() { + final inputStatus = widget.item.inputStatus; + if (inputStatus == null) { + return Center( + child: Padding( + padding: const EdgeInsets.all(20), + child: Text( + 'اطلاعاتی وجود ندارد', + style: AppFonts.yekan14.copyWith(color: AppColor.textColor), + ), + ), + ); + } + + return Column( + children: [ + SizedBox(height: 10), + Row( + children: [ + Text( + 'وضعیت نهاده', + style: AppFonts.yekan16Bold.copyWith(color: AppColor.iconColor), + ), + ], + ), + SizedBox(height: 10), + Container( + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + child: Column( + children: [ + rTableRow( + title: 'وضعیت نهاده', + value: inputStatus.inputStatus ?? '-', + ), + rTableRow( + title: 'نام شرکت', + value: inputStatus.companyName ?? '-', + ), + rTableRow( + title: 'کد پیگیری', + value: inputStatus.trackingCode ?? '-', + ), + rTableRow( + title: 'نوع دانه', + value: inputStatus.typeOfGrain ?? '-', + ), + rTableRow( + title: 'موجودی در انبار', + value: inputStatus.inventoryInWarehouse ?? '-', + ), + rTableRow( + title: 'موجودی تا بازدید', + value: inputStatus.inventoryUntilVisit ?? '-', + ), + rTableRow( + title: 'درجه دانه', + value: inputStatus.gradeGrain ?? '-', + ), + ], + ), + ), + if (inputStatus.images != null && inputStatus.images!.isNotEmpty) ...[ + SizedBox(height: 16), + Text( + 'تصاویر', + style: AppFonts.yekan14Bold.copyWith(color: AppColor.textColor), + ), + SizedBox(height: 10), + SizedBox( + height: 100.h, + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: inputStatus.images!.length, + separatorBuilder: (context, index) => SizedBox(width: 10), + itemBuilder: (context, index) => Container( + width: 80.w, + height: 80.h, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + image: DecorationImage( + fit: BoxFit.cover, + image: NetworkImage(inputStatus.images![index]), + ), + ), + ), + ), + ), + ], + ], + ); + } + + Widget infrastructureEnergyTable() { + final infra = widget.item.infrastructureEnergy; + if (infra == null) { + return Center( + child: Padding( + padding: const EdgeInsets.all(20), + child: Text( + 'اطلاعاتی وجود ندارد', + style: AppFonts.yekan14.copyWith(color: AppColor.textColor), + ), + ), + ); + } + + return Column( + children: [ + SizedBox(height: 10), + Row( + children: [ + Text( + 'زیرساخت و انرژی', + style: AppFonts.yekan16Bold.copyWith(color: AppColor.iconColor), + ), + ], + ), + SizedBox(height: 10), + Container( + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + child: Column( + children: [ + rTableRow( + title: 'نوع ژنراتور', + value: infra.generatorType ?? '-', + ), + rTableRow( + title: 'مدل ژنراتور', + value: infra.generatorModel ?? '-', + ), + rTableRow( + title: 'تعداد ژنراتور', + value: infra.generatorCount ?? '-', + ), + rTableRow( + title: 'ظرفیت ژنراتور', + value: infra.generatorCapacity ?? '-', + ), + rTableRow(title: 'نوع سوخت', value: infra.fuelType ?? '-'), + rTableRow( + title: 'عملکرد ژنراتور', + value: infra.generatorPerformance ?? '-', + ), + rTableRow( + title: 'موجودی سوخت اضطراری', + value: infra.emergencyFuelInventory ?? '-', + ), + rTableRow( + title: 'تاریخچه قطع برق', + value: infra.hasPowerCutHistory == true ? 'بله' : 'خیر', + ), + if (infra.hasPowerCutHistory == true) ...[ + rTableRow( + title: 'مدت قطع برق', + value: infra.powerCutDuration ?? '-', + ), + rTableRow( + title: 'ساعت قطع برق', + value: infra.powerCutHour ?? '-', + ), + ], + rTableRow( + title: 'یادداشت اضافی', + value: infra.additionalNotes ?? '-', + ), + ], + ), + ), + ], + ); + } + + Widget hrTable() { + final hr = widget.item.hr; + if (hr == null) { + return Center( + child: Padding( + padding: const EdgeInsets.all(20), + child: Text( + 'اطلاعاتی وجود ندارد', + style: AppFonts.yekan14.copyWith(color: AppColor.textColor), + ), + ), + ); + } + + return Column( + children: [ + SizedBox(height: 10), + Row( + children: [ + Text( + 'نیروی انسانی', + style: AppFonts.yekan16Bold.copyWith(color: AppColor.iconColor), + ), + ], + ), + SizedBox(height: 10), + Container( + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + child: Column( + children: [ + if (hr.numberEmployed != null) + rTableRow( + title: 'تعداد شاغلین', + value: hr.numberEmployed.toString(), + ), + if (hr.numberIndigenous != null) + rTableRow( + title: 'تعداد بومی', + value: hr.numberIndigenous.toString(), + ), + if (hr.numberNonIndigenous != null) + rTableRow( + title: 'تعداد غیربومی', + value: hr.numberNonIndigenous.toString(), + ), + rTableRow( + title: 'وضعیت قرارداد', + value: hr.contractStatus ?? '-', + ), + rTableRow( + title: 'آموزش دیده', + value: hr.trained == true ? 'بله' : 'خیر', + ), + ], + ), + ), + ], + ); + } + + Widget facilitiesTable() { + final facilities = widget.item.facilities; + if (facilities == null) { + return Center( + child: Padding( + padding: const EdgeInsets.all(20), + child: Text( + 'اطلاعاتی وجود ندارد', + style: AppFonts.yekan14.copyWith(color: AppColor.textColor), + ), + ), + ); + } + + return Column( + children: [ + SizedBox(height: 10), + Row( + children: [ + Text( + 'تسهیلات', + style: AppFonts.yekan16Bold.copyWith(color: AppColor.iconColor), + ), + ], + ), + SizedBox(height: 10), + Container( + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + ), + child: Column( + children: [ + rTableRow( + title: 'دارای تسهیلات', + value: facilities.hasFacilities == true ? 'بله' : 'خیر', + ), + rTableRow( + title: 'نوع تسهیلات', + value: facilities.typeOfFacility ?? '-', + ), + if (facilities.amount != null) + rTableRow(title: 'مبلغ', value: facilities.amount.toString()), + rTableRow(title: 'تاریخ', value: facilities.date ?? '-'), + rTableRow( + title: 'وضعیت بازپرداخت', + value: facilities.repaymentStatus ?? '-', + ), + rTableRow( + title: 'درخواست تسهیلات', + value: facilities.requestFacilities ?? '-', + ), + ], + ), + ), + ], + ); + } + + Widget tabBarWidget( + List tabs, + int selectedIndex, + Function(int) onTabSelected, + ) { + return SizedBox( + height: 38.h, + width: Get.width, + child: Stack( + fit: StackFit.expand, + children: [ + Positioned( + right: 0, + top: 0, + bottom: 0, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + reverse: true, + child: Row( + children: [ + ...tabs.map( + (tab) => GestureDetector( + onTap: () => onTabSelected(tabs.indexOf(tab)), + behavior: HitTestBehavior.opaque, + child: Container( + padding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 11, + ), + decoration: BoxDecoration( + border: tab == tabs[selectedIndex] + ? Border( + bottom: BorderSide( + color: AppColor.blueNormalOld, + width: 3, + ), + ) + : null, + ), + child: Text( + tab, + style: AppFonts.yekan12Bold.copyWith( + color: tab == tabs[selectedIndex] + ? AppColor.blueNormalOld + : AppColor.mediumGrey, + ), + ), + ), + ), + ), + ], + ), + ), + ), + Positioned( + bottom: 0, + left: 0, + right: 0, + child: Divider( + color: AppColor.blackLightHover, + height: 1, + thickness: 1, + ), + ), + ], + ), + ); + } + + Row rTableRow({String? title, String? value}) { + return Row( + children: [ + Expanded( + flex: 1, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 9, vertical: 11), + alignment: Alignment.centerRight, + decoration: BoxDecoration( + color: AppColor.bgLight2, + border: Border( + bottom: BorderSide(color: AppColor.blackLightHover, width: 1), + ), + ), + child: Text( + title ?? '', + style: AppFonts.yekan14Bold.copyWith(color: AppColor.iconColor), + ), + ), + ), + Expanded( + flex: 1, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 9, vertical: 11), + alignment: Alignment.centerRight, + decoration: BoxDecoration( + border: Border( + bottom: BorderSide(color: AppColor.blackLightHover, width: 1), + ), + ), + child: Text( + value ?? '', + style: AppFonts.yekan14Bold.copyWith(color: AppColor.textColor), + ), + ), + ), + ], + ); + } +} diff --git a/packages/chicken/lib/features/poultry_science/presentation/pages/poultry_action/logic.dart b/packages/chicken/lib/features/poultry_science/presentation/pages/poultry_action/logic.dart index ec8ab95..9db46d1 100644 --- a/packages/chicken/lib/features/poultry_science/presentation/pages/poultry_action/logic.dart +++ b/packages/chicken/lib/features/poultry_science/presentation/pages/poultry_action/logic.dart @@ -39,7 +39,7 @@ class PoultryActionLogic extends GetxController { PoultryActionItem( title: "بازرسی مزارع طیور", - route: ChickenRoutes.poultryFarmInspectionHome, + route: PoultryScienceRoutes.newInspectionPoultryScience, icon: Assets.vec.activeFramSvg.path, ), ].obs; diff --git a/packages/chicken/lib/features/poultry_science/presentation/routes/pages.dart b/packages/chicken/lib/features/poultry_science/presentation/routes/pages.dart index 79cdb3b..786422e 100644 --- a/packages/chicken/lib/features/poultry_science/presentation/routes/pages.dart +++ b/packages/chicken/lib/features/poultry_science/presentation/routes/pages.dart @@ -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(), diff --git a/packages/chicken/lib/features/poultry_science/presentation/routes/routes.dart b/packages/chicken/lib/features/poultry_science/presentation/routes/routes.dart index 1dafcd5..cea4063 100644 --- a/packages/chicken/lib/features/poultry_science/presentation/routes/routes.dart +++ b/packages/chicken/lib/features/poultry_science/presentation/routes/routes.dart @@ -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'; diff --git a/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/create_inspection_bottom_sheet.dart b/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/create_inspection_bottom_sheet.dart index 023a4fd..832c990 100644 --- a/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/create_inspection_bottom_sheet.dart +++ b/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/create_inspection_bottom_sheet.dart @@ -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( + 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( + 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, ), ), - ), + ), */ ], ), ), diff --git a/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/create_inspection_bottom_sheet_logic.dart b/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/create_inspection_bottom_sheet_logic.dart index 18d0640..9996740 100644 --- a/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/create_inspection_bottom_sheet_logic.dart +++ b/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/create_inspection_bottom_sheet_logic.dart @@ -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(); TokenStorageService tokenStorageService = Get.find(); - + var gService = Get.find(); 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 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 pickImageFromCamera() async { + Future pickImageFromCamera(RxMap 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?> uploadImageBatch(List images) async { + if (images.isEmpty) return []; + + List? 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 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 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 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, + ); + } } diff --git a/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/step1_page.dart b/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/step1_page.dart index c848934..8d3b239 100644 --- a/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/step1_page.dart +++ b/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/step1_page.dart @@ -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, diff --git a/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/step2_page.dart b/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/step2_page.dart index efca538..9cbc602 100644 --- a/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/step2_page.dart +++ b/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/step2_page.dart @@ -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, diff --git a/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/step3_page.dart b/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/step3_page.dart index 343c2d6..662424f 100644 --- a/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/step3_page.dart +++ b/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/step3_page.dart @@ -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( diff --git a/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/step4_page.dart b/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/step4_page.dart index 156329e..b22f760 100644 --- a/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/step4_page.dart +++ b/packages/chicken/lib/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/step4_page.dart @@ -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( - 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( - 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( - 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, diff --git a/packages/core/lib/infrastructure/remote/dio_remote.dart b/packages/core/lib/infrastructure/remote/dio_remote.dart index c1d6bd7..91660f5 100644 --- a/packages/core/lib/infrastructure/remote/dio_remote.dart +++ b/packages/core/lib/infrastructure/remote/dio_remote.dart @@ -37,10 +37,11 @@ class DioRemote implements IHttpClient { PrettyDioLogger( request: true, enabled: true, + error: false, requestHeader: true, responseHeader: true, requestBody: true, - responseBody: true, + responseBody: false, ), ); }