Merge branch with resolved conflicts - restructured features and added new modules

This commit is contained in:
2025-12-17 10:26:39 +03:30
484 changed files with 55236 additions and 4255 deletions

View File

@@ -0,0 +1,100 @@
<<<<<<<< HEAD:packages/chicken/lib/features/poultry_science/active_hatching/logic.dart
import 'package:rasadyar_chicken/data/models/response/hatching/hatching_models.dart';
import 'package:rasadyar_chicken/features/poultry_science/root/logic.dart';
========
import 'package:rasadyar_chicken/data/di/chicken_di.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/hatching/hatching_models.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/repositories/poultry_science_repository.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/root/logic.dart';
>>>>>>>> develop:packages/chicken/lib/features/poultry_science/presentation/pages/active_hatching/logic.dart
import 'package:rasadyar_core/core.dart';
class ActiveHatchingLogic extends GetxController {
PoultryScienceRootLogic rootLogic = Get.find<PoultryScienceRootLogic>();
BaseLogic baseLogic = Get.find<BaseLogic>();
late PoultryScienceRepository poultryScienceRepository;
Rx<Resource<PaginationModel<HatchingModel>>> activeHatchingList =
Resource<PaginationModel<HatchingModel>>.loading().obs;
final RxBool isLoadingMoreList = false.obs;
RxInt currentPage = 1.obs;
RxInt expandedIndex = RxInt(-1);
List<String> routesName = ['اقدام', 'جوجه ریزی فعال'];
Rx<Jalali> fromDateFilter = Jalali.now().obs;
Rx<Jalali> toDateFilter = Jalali.now().obs;
RxnString searchedValue = RxnString();
@override
void onInit() {
super.onInit();
poultryScienceRepository = diChicken.get<PoultryScienceRepository>();
}
@override
void onReady() {
super.onReady();
getHatchingList();
}
@override
void onClose() {
super.onClose();
baseLogic.clearSearch();
}
Future<void> getHatchingList([bool isLoadingMore = false]) async {
if (isLoadingMore) {
isLoadingMoreList.value = true;
} else {
activeHatchingList.value =
Resource<PaginationModel<HatchingModel>>.loading();
}
if (searchedValue.value != null &&
searchedValue.value!.trim().isNotEmpty &&
currentPage.value > 1) {
currentPage.value = 1;
}
safeCall(
call: () async => await poultryScienceRepository.getHatchingPoultry(
token: rootLogic.tokenService.accessToken.value!,
queryParameters: buildQueryParams(
queryParams: {'type': 'hatching'},
role: 'PoultryScience',
pageSize: 50,
page: currentPage.value,
),
),
onSuccess: (res) {
if ((res?.count ?? 0) == 0) {
activeHatchingList.value =
Resource<PaginationModel<HatchingModel>>.empty();
} else {
activeHatchingList.value =
Resource<PaginationModel<HatchingModel>>.success(
PaginationModel<HatchingModel>(
count: res?.count ?? 0,
next: res?.next,
previous: res?.previous,
results: [
...(activeHatchingList.value.data?.results ?? []),
...(res?.results ?? []),
],
),
);
}
},
);
}
void toggleExpanded(int index) {
expandedIndex.value = expandedIndex.value == index ? -1 : index;
}
Future<void> onRefresh() async {
currentPage.value = 1;
await getHatchingList();
}
}

View File

@@ -0,0 +1,239 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/hatching/hatching_models.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/active_hatching/logic.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/create_inspection_bottom_sheet.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/create_inspection_bottom_sheet_logic.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';
class ActiveHatchingPage extends GetView<ActiveHatchingLogic> {
const ActiveHatchingPage({super.key});
@override
Widget build(BuildContext context) {
return ChickenBasePage(
hasSearch: true,
hasFilter: false,
backId: poultryScienceActionKey,
routes: controller.routesName,
onSearchChanged: (data) {
controller.searchedValue.value = data;
controller.getHatchingList();
},
child: hatchingWidget(),
/*widgets: [
hatchingWidget()
],*/
);
}
Widget hatchingWidget() {
return 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: itemListWidget(item),
secondChild: itemListExpandedWidget(item),
labelColor: AppColor.blueLight,
labelIcon: Assets.vec.activeFramSvg.path,
);
}, controller.expandedIndex);
},
itemCount: data.value.data?.results?.length ?? 0,
separatorBuilder: (context, index) => SizedBox(height: 8.h),
onLoadMore: () async => controller.getHatchingList(true),
);
}, controller.activeHatchingList);
}
Container itemListExpandedWidget(HatchingModel item) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Column(
spacing: 8,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
item.poultry?.user?.fullname ?? 'N/A',
textAlign: TextAlign.center,
style: AppFonts.yekan16.copyWith(color: AppColor.greenDark),
),
Spacer(),
Visibility(
child: Text(
item.violation == true ? 'پیگیری' : 'عادی',
textAlign: TextAlign.center,
style: AppFonts.yekan10.copyWith(color: AppColor.redDark),
),
),
],
),
Container(
height: 32,
padding: EdgeInsets.symmetric(horizontal: 8),
decoration: ShapeDecoration(
color: AppColor.blueLight,
shape: RoundedRectangleBorder(
side: BorderSide(width: 1, color: AppColor.blueLightHover),
borderRadius: BorderRadius.circular(8),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'نژاد:${item.breed?.first.breed ?? 'N/A'}',
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
),
Text(
' سن${item.age} (روز)',
style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
),
Text(
' دوره جوجه ریزی:${item.period}',
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
),
],
),
),
buildRow(
title: 'شماره مجوز جوجه ریزی',
value: item.licenceNumber ?? 'N/A',
),
buildUnitRow(
title: 'حجم جوجه ریزی',
value: item.quantity.separatedByCommaFa,
unit: '(قطعه)',
),
buildUnitRow(
title: 'مانده در سالن',
value: item.leftOver.separatedByCommaFa,
unit: '(قطعه)',
),
buildUnitRow(
title: 'تلفات',
value: item.losses.separatedByCommaFa,
unit: '(قطعه)',
),
buildRow(
title: 'دامپزشک فارم',
value:
'${item.vetFarm?.vetFarmFullName}(${item.vetFarm?.vetFarmMobile})',
),
buildRow(
title: 'شرح بازرسی',
value: item.reportInfo?.image == false
? 'ارسال تصویر جوجه ریزی فارم '
: 'تکمیل شده',
titleStyle: AppFonts.yekan14.copyWith(
color: (item.reportInfo?.image ?? false)
? AppColor.greenNormal
: AppColor.redDark,
),
valueStyle: AppFonts.yekan14.copyWith(
color: (item.reportInfo?.image ?? false)
? AppColor.greenNormal
: AppColor.redDark,
),
),
RElevated(
height: 40.h,
isFullWidth: true,
onPressed: () {
Get.find<CreateInspectionBottomSheetLogic>().setHatchingModel(
item,
);
Get.bottomSheet(
CreateInspectionBottomSheet(),
isScrollControlled: true,
ignoreSafeArea: false,
).then((value) {
if (Get.isRegistered<CreateInspectionBottomSheetLogic>()) {
Get.find<CreateInspectionBottomSheetLogic>().clearForm();
}
});
},
child: Text('ثبت بازرسی'),
),
],
),
);
}
Widget itemListWidget(HatchingModel item) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
SizedBox(width: 20),
Expanded(
flex: 2,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 3,
children: [
Text(
item.poultry?.user?.fullname ?? 'N/A',
textAlign: TextAlign.start,
style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal),
),
Text(
item.poultry?.user?.mobile ?? 'N/A',
textAlign: TextAlign.center,
style: AppFonts.yekan14.copyWith(color: AppColor.bgDark),
),
],
),
),
Expanded(
flex: 3,
child: Column(
spacing: 3,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
item.poultry?.unitName ?? 'N/Aaq',
textAlign: TextAlign.start,
style: AppFonts.yekan12.copyWith(color: AppColor.bgDark),
),
Text(
item.poultry?.licenceNumber ?? 'N/A',
textAlign: TextAlign.left,
style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal),
),
],
),
),
Expanded(
flex: 1,
child: Assets.vec.scanSvg.svg(
width: 32.w,
height: 32.h,
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
),
),
],
);
}
}

View File

@@ -0,0 +1,127 @@
import 'package:flutter/material.dart';
<<<<<<<< HEAD:packages/chicken/lib/features/poultry_science/farm/logic.dart
import 'package:rasadyar_chicken/data/models/response/poultry_farm/poultry_farm.dart';
import 'package:rasadyar_chicken/features/poultry_science/home/logic.dart';
import 'package:rasadyar_chicken/features/poultry_science/root/logic.dart';
========
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_farm/poultry_farm.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/home/logic.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/root/logic.dart';
>>>>>>>> develop:packages/chicken/lib/features/poultry_science/presentation/pages/farm/logic.dart
import 'package:rasadyar_core/core.dart';
class FarmLogic extends GetxController {
List<String> routes = ['اقدام', 'فارم ها'];
PoultryScienceRootLogic rootLogic = Get.find<PoultryScienceRootLogic>();
BasePageLogic baseLogic = Get.find<BasePageLogic>();
final PoultryScienceHomeLogic _homeLogic =
Get.find<PoultryScienceHomeLogic>();
RxList<InformationTagData> tagInfo = <InformationTagData>[
InformationTagData(
labelTitle: 'کل فارم ها',
isLoading: true,
labelVecIcon: Assets.vec.cubeScanSvg.path,
iconColor: AppColor.blueNormalOld,
valueBgColor: Colors.white,
labelGradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [AppColor.blueLight, Colors.white],
),
),
InformationTagData(
labelTitle: 'حجم جوجه ریزی',
unit: 'قطعه',
isLoading: true,
labelVecIcon: Assets.vec.cubeCardSvg.path,
blendMode: BlendMode.dst,
valueBgColor: Colors.white,
labelGradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [AppColor.greenLightHover, Colors.white],
),
),
].obs;
Rx<Resource<PaginationModel<PoultryFarm>>> farmList =
Resource<PaginationModel<PoultryFarm>>.loading().obs;
RxInt currentPage = 1.obs;
final RxBool isLoadingMoreList = false.obs;
RxInt expandedIndex = RxInt(-1);
Rx<Jalali> fromDateFilter = Jalali.now().obs;
Rx<Jalali> toDateFilter = Jalali.now().obs;
RxnString searchedValue = RxnString();
@override
void onReady() {
super.onReady();
tagInfo[0] = tagInfo[0].copyWith(
isLoading: false,
value: _homeLogic.tagInfo['first']!.first.value,
);
tagInfo[1] = tagInfo[1].copyWith(
isLoading: false,
value: _homeLogic.tagInfo['second']!.first.value,
);
getFarmList();
}
@override
void onClose() {
super.onClose();
baseLogic.clearSearch();
}
Future<void> getFarmList([bool isLoadingMore = false]) async {
if (isLoadingMore) {
isLoadingMoreList.value = true;
} else {
farmList.value = Resource<PaginationModel<PoultryFarm>>.loading();
}
await safeCall(
call: () async =>
await rootLogic.poultryRepository.getPoultryScienceFarmList(
token: rootLogic.tokenService.accessToken.value!,
queryParameters: buildQueryParams(
queryParams: {'type': 'farm'},
role: 'PoultryScience',
pageSize: 50,
search: 'filter',
value: '',
page: currentPage.value,
),
),
onSuccess: (res) {
if ((res?.count ?? 0) == 0) {
farmList.value = Resource<PaginationModel<PoultryFarm>>.empty();
} else {
farmList.value = Resource<PaginationModel<PoultryFarm>>.success(
PaginationModel<PoultryFarm>(
count: res?.count ?? 0,
next: res?.next,
previous: res?.previous,
results: [
...(farmList.value.data?.results ?? []),
...?res?.results,
],
),
);
}
},
onError: (error, stackTrace) {},
);
}
void toggleExpanded(int index) {
expandedIndex.value = expandedIndex.value == index ? -1 : index;
}
Future<void> onRefresh() async {
currentPage.value = 1;
farmList.value = Resource<PaginationModel<PoultryFarm>>.loading();
await getFarmList();
}
}

View File

@@ -0,0 +1,261 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_farm/poultry_farm.dart';
import 'package:rasadyar_chicken/presentation/utils/nested_keys_utils.dart';
import 'package:rasadyar_chicken/presentation/widget/base_page/view.dart';
import 'package:rasadyar_chicken/presentation/widget/filter_bottom_sheet.dart';
import 'package:rasadyar_core/core.dart';
import 'logic.dart';
class FarmPage extends GetView<FarmLogic> {
const FarmPage({super.key});
@override
Widget build(BuildContext context) {
return ChickenBasePage(
hasFilter: false,
hasSearch: true,
onRefresh: controller.onRefresh,
onFilterTap: () {
Get.bottomSheet(
isScrollControlled: true,
backgroundColor: Colors.transparent,
filterBottomSheet(),
);
},
onSearchChanged: (data) {
controller.searchedValue.value = data;
controller.getFarmList();
},
routes: controller.routes,
backId: poultryScienceActionKey,
child: Column(children: [firstTagInformation(), farmListWidget()]),
);
}
Widget firstTagInformation() {
return Padding(
padding: const EdgeInsets.fromLTRB(0, 8, 0, 13),
child: ObxValue((data) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w),
child: Row(
spacing: 8,
children: List.generate(
data.length,
(index) => Expanded(child: InformationTag(data: data[index])),
),
),
);
}, controller.tagInfo),
);
}
Widget farmListWidget() {
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: itemListWidget(item),
secondChild: itemListExpandedWidget(item),
labelColor: AppColor.blueLight,
labelIcon: Assets.vec.cubeScanSvg.path,
);
}, controller.expandedIndex);
},
itemCount: data.value.data?.results?.length ?? 0,
separatorBuilder: (context, index) => SizedBox(height: 8.h),
onLoadMore: () async => controller.getFarmList(true),
);
}, controller.farmList),
);
}
Container itemListExpandedWidget(PoultryFarm item) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Column(
spacing: 8,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
item.unitName ?? 'N/A',
textAlign: TextAlign.center,
style: AppFonts.yekan16.copyWith(color: AppColor.greenDark),
),
Spacer(),
Visibility(
child: Text(
'${item.address?.province?.name} / ${item.address?.city?.name}',
textAlign: TextAlign.center,
style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
),
),
],
),
Container(
height: 32,
padding: EdgeInsets.symmetric(horizontal: 8),
decoration: ShapeDecoration(
color: AppColor.blueLight,
shape: RoundedRectangleBorder(
side: BorderSide(width: 1, color: AppColor.blueLightHover),
borderRadius: BorderRadius.circular(8),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'تعاونی : ${item.cityOperator ?? 'ندارد'}',
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
),
Text(
' تعداد سالن : ${item.numberOfHalls}',
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
),
],
),
),
buildRow(
title: 'مالک/ تلفن',
value: '${item.user?.fullname} (${item.user?.mobile})',
),
buildRow(title: 'شناسه یکتا', value: item.breedingUniqueId ?? 'N/A'),
buildRow(
title: 'کد اپیدمیولوژیک',
value: item.epidemiologicalCode ?? 'N/A',
),
buildRow(
title: 'کد بهداشتی',
value: item.healthCertificateNumber ?? 'N/A',
),
buildRow(
title: 'دامپزشک فارم',
value: '${item.vetFarm?.fullName} (${item.vetFarm?.mobile ?? '-'})',
),
buildUnitRow(
title: 'ظرفیت فارم',
value: item.totalCapacity.separatedByCommaFa,
unit: '(قطعه)',
),
buildRow(
title: 'جوجه ریزی فعال (تعداد دوره) ',
value:
'${(item.hatchingInfo?.activeHatching ?? false) ? 'دارد' : 'ندارد'} (${item.hatchingInfo?.period ?? 0})',
),
/* buildRow(
title: 'شرح بازرسی',
value: item.reportInfo?.image == false ? 'ارسال تصویر جوجه ریزی فارم ' : 'تکمیل شده',
titleStyle: AppFonts.yekan14.copyWith(
color: (item.reportInfo?.image ?? false) ? AppColor.greenNormal : AppColor.redDark,
),
valueStyle: AppFonts.yekan14.copyWith(
color: (item.reportInfo?.image ?? false) ? AppColor.greenNormal : AppColor.redDark,
),
),*/
/* Visibility(
visible: (item.reportInfo?.image == false),
child: RElevated(
text: 'ثبت بازرسی',
isFullWidth: true,
width: 150.w,
height: 40.h,
onPressed: () {
cameraBottomSheet(item.id!);
},
textStyle: AppFonts.yekan20.copyWith(color: Colors.white),
backgroundColor: AppColor.greenNormal,
),
),*/
],
),
);
}
Widget itemListWidget(PoultryFarm item) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(width: 20),
Expanded(
flex: 2,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
spacing: 3,
children: [
Text(
item.unitName ?? 'N/A',
textAlign: TextAlign.start,
style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal),
),
Text(
item.user?.mobile ?? 'N/A',
textAlign: TextAlign.center,
style: AppFonts.yekan14.copyWith(color: AppColor.bgDark),
),
],
),
),
Expanded(
flex: 3,
child: Column(
spacing: 3,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'شناسه یکتا: ${item.breedingUniqueId}',
textAlign: TextAlign.start,
style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal),
),
Text(
'${item.address?.province?.name}/${item.address?.city?.name}',
textAlign: TextAlign.left,
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),
),
),
],
);
}
Widget filterBottomSheet() => filterBottomSheetWidget(
fromDate: controller.fromDateFilter,
onChangedFromDate: (jalali) => controller.fromDateFilter.value = jalali,
toDate: controller.toDateFilter,
onChangedToDate: (jalali) => controller.toDateFilter.value = jalali,
onSubmit: () => controller.getFarmList(),
);
}

View File

@@ -0,0 +1,153 @@
<<<<<<<< HEAD:packages/chicken/lib/features/poultry_science/genocide/logic.dart
import 'package:rasadyar_chicken/data/models/response/poultry_order/poultry_order.dart';
import 'package:rasadyar_chicken/features/poultry_science/killing_registration/logic.dart';
import 'package:rasadyar_chicken/features/poultry_science/root/logic.dart';
========
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_order/poultry_order.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/killing_registration/logic.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/root/logic.dart';
>>>>>>>> develop:packages/chicken/lib/features/poultry_science/presentation/pages/genocide/logic.dart
import 'package:rasadyar_core/core.dart';
class GenocideLogic extends GetxController {
List<String> routesName = ['اقدام', 'درخواست کشتارها'];
var tokenService = Get.find<TokenStorageService>();
BaseLogic baseLogic = Get.find<BaseLogic>();
var gService = Get.find<GService>();
var rootLogic = Get.find<PoultryScienceRootLogic>();
var killRegistration = Get.find<KillingRegistrationLogic>();
Rx<Resource<PaginationModel<PoultryOrder>>> poultryOrderList =
Resource<PaginationModel<PoultryOrder>>.loading().obs;
RxInt expandedIndex = RxInt(-1);
final RxInt currentPage = 1.obs;
final RxBool isLoadingMore = false.obs;
final RxBool isLoadingDelete = false.obs;
Rx<Jalali> fromDateFilter = Jalali.now().obs;
Rx<Jalali> toDateFilter = Jalali.now().obs;
RxnString searchedValue = RxnString();
/* final RxBool isLoadingMoreAllocationsMade = false.obs;
final RxBool isOnLoadingSubmitOrEdit = false.obs;*/
@override
void onReady() {
super.onReady();
getPoultryOrderList();
}
@override
void onClose() {
super.onClose();
baseLogic.clearSearch();
}
Future<void> getPoultryOrderList([bool loadingMore = false]) async {
if (loadingMore) {
isLoadingMore.value = true;
} else {
poultryOrderList.value =
Resource<PaginationModel<PoultryOrder>>.loading();
}
if (searchedValue.value != null &&
searchedValue.value!.trim().isNotEmpty &&
currentPage.value > 1) {
currentPage.value = 1;
}
await safeCall(
call: () async => await rootLogic.poultryRepository.getPoultryOderList(
token: rootLogic.tokenService.accessToken.value!,
queryParameters: buildQueryParams(
pageSize: 20,
page: currentPage.value,
search: 'filter',
role: gService.getRole(Module.chicken),
value: searchedValue.value,
fromDate: fromDateFilter.value.toDateTime(),
toDate: toDateFilter.value.toDateTime(),
queryParams: {'today': null},
),
),
onSuccess: (res) async {
await Future.delayed(Duration(milliseconds: 500));
if ((res?.count ?? 0) == 0) {
poultryOrderList.value =
Resource<PaginationModel<PoultryOrder>>.empty();
} else {
if (loadingMore) {
poultryOrderList.value =
Resource<PaginationModel<PoultryOrder>>.success(
PaginationModel<PoultryOrder>(
count: res?.count ?? 0,
next: res?.next,
previous: res?.previous,
results: [
...(poultryOrderList.value.data?.results ?? []),
...(res?.results ?? []),
],
),
);
} else {
poultryOrderList.value =
Resource<PaginationModel<PoultryOrder>>.success(res!);
}
}
},
);
}
Future<void> deletePoultryOrder(int id) async {
toggleExpanded(-1);
await safeCall(
call: () async => await rootLogic.poultryRepository.deletePoultryOder(
token: rootLogic.tokenService.accessToken.value!,
orderId: id.toString(),
),
onSuccess: (_) {
defaultShowSuccessMessage('درخواست با موفقیت حذف شد');
},
);
}
void toggleExpanded(int index) {
expandedIndex.value = expandedIndex.value == index ? -1 : index;
}
String getRequestType(PoultryOrder item) {
if (item.market ?? false) {
return 'پنل معاملات';
} else if (item.union ?? false) {
return 'اتحادیه';
} else {
return 'خرید مستقیم';
}
}
String getKillType(PoultryOrder item) {
if (item.export ?? false) {
return 'صادرات';
} else if (item.freezing ?? false) {
return 'انجماد';
} else {
return 'عادی';
}
}
String getState(PoultryOrder item) {
if (item.stateProcess == 'pending') {
return 'در انتظار تایید';
} else {
return 'تایید شده';
}
}
Future<void> onRefresh() async {
currentPage.value = 1;
await getPoultryOrderList();
}
}

View File

@@ -0,0 +1,313 @@
import 'package:flutter/material.dart';
<<<<<<<< HEAD:packages/chicken/lib/features/poultry_science/genocide/view.dart
import 'package:rasadyar_chicken/data/models/response/poultry_order/poultry_order.dart';
import 'package:rasadyar_chicken/features/poultry_science/killing_registration/view.dart';
========
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_order/poultry_order.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/killing_registration/view.dart';
>>>>>>>> develop:packages/chicken/lib/features/poultry_science/presentation/pages/genocide/view.dart
import 'package:rasadyar_chicken/presentation/utils/nested_keys_utils.dart';
import 'package:rasadyar_chicken/presentation/widget/base_page/view.dart';
import 'package:rasadyar_chicken/presentation/widget/filter_bottom_sheet.dart';
import 'package:rasadyar_core/core.dart';
import 'logic.dart';
class GenocidePage extends GetView<GenocideLogic> {
const GenocidePage({super.key});
@override
Widget build(BuildContext context) {
return ChickenBasePage(
routes: controller.routesName,
hasSearch: true,
hasFilter: true,
onRefresh: controller.onRefresh,
onSearchChanged: (data) {
controller.searchedValue.value = data;
controller.getPoultryOrderList();
},
backId: poultryScienceActionKey,
onFilterTap: () {
Get.bottomSheet(
isScrollControlled: true,
backgroundColor: Colors.transparent,
filterBottomSheet(),
);
},
child: Stack(
fit: StackFit.expand,
children: [
Positioned.fill(child: poultryOrderListWidget()),
Positioned(
bottom: 130,
right: 16,
child: RFab.add(
onPressed: () {
Get.bottomSheet(
isScrollControlled: true,
backgroundColor: Colors.transparent,
killRegistrationBottomSheet(),
).whenComplete(() {
controller.killRegistration.clearAllFields();
controller.killRegistration.onReady();
});
},
),
),
],
),
);
}
//region List and Items
Widget poultryOrderListWidget() {
return 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: itemListWidget(item),
secondChild: itemListExpandedWidget(item),
labelColor: AppColor.blueLight,
labelIcon: Assets.vec.timerSvg.path,
labelIconColor: AppColor.yellowNormal2,
);
}, controller.expandedIndex);
},
itemCount: data.value.data?.results?.length ?? 0,
separatorBuilder: (context, index) => SizedBox(height: 8.h),
onLoadMore: () async => controller.getPoultryOrderList(true),
);
}, controller.poultryOrderList);
}
Container itemListExpandedWidget(PoultryOrder item) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Column(
spacing: 8,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
item.poultry?.unitName ?? 'N/A',
textAlign: TextAlign.center,
style: AppFonts.yekan16.copyWith(color: AppColor.greenDark),
),
Spacer(),
Visibility(
child: Text(
'${item.poultry?.address?.province?.name} / ${item.poultry?.address?.city?.name}',
textAlign: TextAlign.center,
style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
),
),
],
),
Container(
height: 32,
padding: EdgeInsets.symmetric(horizontal: 8),
decoration: ShapeDecoration(
color: AppColor.blueLight,
shape: RoundedRectangleBorder(
side: BorderSide(width: 1, color: AppColor.blueLightHover),
borderRadius: BorderRadius.circular(8),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'سن مرغ:${item.hatching?.age ?? '-'} (روز)',
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
),
Text(
item.sendDate?.formattedJalaliDate ?? '-',
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
),
Text(
'تعداد:${item.quantity.separatedByComma} (قطعه)',
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
),
],
),
),
buildRow(title: 'کد سفارش', value: '${item.orderCode} '),
buildRow(
title: 'نوع فروش',
value: (item.freeSaleInProvince ?? false) ? 'آزاد' : 'دولتی ',
),
buildRow(title: 'نوع کشتار ', value: controller.getKillType(item)),
buildRow(title: 'درخواست', value: controller.getRequestType(item)),
buildRow(
title: 'میانگین وزنی',
value: '${(item.indexWeight)} (کیلوگرم)',
),
buildRow(
title: 'قیمت مرغدار',
value: '${item.amount.separatedByComma} (ریال)',
),
buildRow(
title: 'مانده در سالن ',
value: '${item.hatching?.leftOver.separatedByComma ?? 0} (قطعه)',
),
buildRow(title: ' وضعیت', value: controller.getState(item)),
Visibility(
visible: item.stateProcess == 'pending',
child: ObxValue((data) {
return ROutlinedElevatedIcon(
height: 40.h,
width: Get.width,
text: 'حذف',
icon: Assets.vec.trashSvg.svg(
width: 16.w,
height: 16.h,
colorFilter: ColorFilter.mode(
AppColor.error,
BlendMode.srcIn,
),
),
textStyle: AppFonts.yekan16Bold.copyWith(color: AppColor.error),
borderColor: AppColor.error,
foregroundColor: AppColor.error,
pressedBackgroundColor: AppColor.error,
onPressed: data.value
? null
: () => _buildDeleteDialog(
onConfirm: () async {
Get.back();
await controller.deletePoultryOrder(item.id!);
controller.getPoultryOrderList();
},
),
);
}, controller.isLoadingDelete),
),
],
),
);
}
Widget itemListWidget(PoultryOrder item) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(width: 20),
Expanded(
flex: 2,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
spacing: 3,
children: [
Text(
item.poultry?.unitName ?? 'N/A',
textAlign: TextAlign.start,
style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal),
),
Text(
item.sendDate?.formattedJalaliDate ?? '-',
textAlign: TextAlign.center,
style: AppFonts.yekan14.copyWith(color: AppColor.bgDark),
),
],
),
),
Expanded(
flex: 3,
child: Column(
spacing: 3,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'کد سفارش : ${item.orderCode ?? '-'}',
textAlign: TextAlign.start,
style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal),
),
Text(
'تعداد:${item.quantity.separatedByComma} (قطعه)',
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
),
],
),
),
Expanded(
flex: 1,
child: Assets.vec.scanSvg.svg(
width: 32.w,
height: 32.h,
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
),
),
],
);
}
//endregion
//region other widgets
void _buildDeleteDialog({required VoidCallback onConfirm}) {
Get.defaultDialog(
title: 'حذف درخواست کشتار',
middleText: 'آیا از حذف این درخواست کشتار مطمئن هستید؟',
confirm: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: AppColor.error,
foregroundColor: Colors.white,
),
onPressed: onConfirm,
child: Text('بله'),
),
cancel: ElevatedButton(
onPressed: () {
Get.back();
},
child: Text('خیر'),
),
);
}
Widget filterBottomSheet() => filterBottomSheetWidget(
fromDate: controller.fromDateFilter,
onChangedFromDate: (jalali) => controller.fromDateFilter.value = jalali,
toDate: controller.toDateFilter,
onChangedToDate: (jalali) => controller.toDateFilter.value = jalali,
onSubmit: () => controller.getPoultryOrderList(),
);
//endregion
//region kill registration bottom sheet
Widget killRegistrationBottomSheet() {
return BaseBottomSheet(
height: Get.height * 0.9,
bgColor: Color(0x66E4E4E4),
child: KillingRegistrationPage(),
);
}
//endregion
}

View File

@@ -0,0 +1,196 @@
import 'package:flutter/material.dart';
<<<<<<<< HEAD:packages/chicken/lib/features/poultry_science/home/logic.dart
import 'package:rasadyar_chicken/data/models/response/poultry_science/home_poultry_science/home_poultry_science_model.dart';
import 'package:rasadyar_chicken/features/poultry_science/root/logic.dart';
========
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/home_poultry_science/home_poultry_science_model.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/root/logic.dart';
>>>>>>>> develop:packages/chicken/lib/features/poultry_science/presentation/pages/home/logic.dart
import 'package:rasadyar_core/core.dart';
import 'package:rasadyar_core/presentation/widget/custom/information_card_widget.dart';
class PoultryScienceHomeLogic extends GetxController {
PoultryScienceRootLogic rootLogic = Get.find<PoultryScienceRootLogic>();
Rxn<HomePoultryScienceModel> homeInformation = Rxn();
RxBool isExpanded = false.obs;
RxMap<String, List<InformationTagData>> tagInfo = RxMap({
'first': [
InformationTagData(
labelTitle: 'کل فارم ها',
isLoading: true,
labelVecIcon: Assets.vec.cubeScanSvg.path,
iconColor: AppColor.blueFlashing,
blendMode: BlendMode.srcOut,
valueBgColor: Colors.white,
labelGradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [AppColor.blueLight, Colors.white],
),
),
InformationTagData(
labelTitle: 'تعداد جوجه ریزی',
isLoading: true,
labelVecIcon: Assets.vec.cubeCardSvg.path,
blendMode: BlendMode.dst,
valueBgColor: Colors.white,
labelGradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [AppColor.greenLightHover, Colors.white],
),
),
],
'second': [
InformationTagData(
labelTitle: 'حجم جوجه ریزی',
unit: 'قطعه',
isLoading: true,
labelVecIcon: Assets.vec.hashtagSvg.path,
iconColor: const Color(0xFF6C5D60),
labelBgColor: const Color(0xFFDDC0C7),
valueBgColor: const Color(0xFFEDDCE0),
),
InformationTagData(
labelTitle: 'مانده در سالن',
unit: 'قطعه',
isLoading: true,
labelVecIcon: Assets.vec.homeHashtagSvg.path,
labelBgColor: const Color(0xFFAFCBFF),
valueBgColor: const Color(0xFFCEDFFF),
),
],
'third': [
InformationTagData(
labelTitle: 'تلفات',
unit: 'قطعه',
isLoading: true,
labelVecIcon: Assets.vec.boxRemoveSvg.path,
iconColor: const Color(0xFF426060),
labelBgColor: const Color(0xFFA5D1D2),
valueBgColor: const Color(0xFFC7DFE0),
),
InformationTagData(
labelTitle: 'حجم کشتار شده',
unit: 'قطعه',
isLoading: true,
labelVecIcon: Assets.vec.closeSquareFilledSvg.path,
blendMode: BlendMode.dst,
labelBgColor: Color(0xFFC8B8D1),
valueBgColor: Color(0xFFDAD4DD),
),
],
});
RxList<InformationCardData> ageCardData = [
InformationCardData(
labelTitle: 'بیشترین سن جوجه ریزی',
isLoading: true,
unit: 'روز',
labelVecIcon: Assets.vec.homeTrendUpSvg.path,
iconColor: const Color.fromRGBO(85, 97, 93, 1),
cardBgColor: const Color(0xFFE6FAF5),
labelBgColor: const Color(0xFFB0EFDF),
),
InformationCardData(
labelTitle: 'کمترین سن جوجه ریزی',
isLoading: true,
unit: 'روز',
labelVecIcon: Assets.vec.homeTrendDownSvg.path,
iconColor: const Color(0xFF6F6164),
cardBgColor: const Color(0xFFEDDCE0),
labelBgColor: const Color(0xFFE0BCC5),
),
].obs;
@override
void onReady() {
super.onReady();
getHomePoultryHatching();
}
Future<void> getHomePoultryHatching() async {
await safeCall<HomePoultryScienceModel?>(
call: () async => await rootLogic.poultryRepository.getHomePoultry(
token: rootLogic.tokenService.accessToken.value!,
type: 'home',
),
onSuccess: (result) {
if (result != null) {
homeInformation.value = result;
tagInfo['first'] = tagInfo['first']!.map((tag) {
if (tag.labelTitle == 'کل فارم ها') {
return tag.copyWith(
value: result.farmCount?.separatedByCommaFa ?? '0',
isLoading: false,
);
}
if (tag.labelTitle == 'تعداد جوجه ریزی') {
return tag.copyWith(
value: result.hatchingCount?.separatedByCommaFa ?? '0',
isLoading: false,
);
}
return tag;
}).toList();
// second
tagInfo['second'] = tagInfo['second']!.map((tag) {
switch (tag.labelTitle) {
case 'حجم جوجه ریزی':
return tag.copyWith(
value: result.hatchingQuantity?.separatedByCommaFa ?? '0',
isLoading: false,
);
case 'مانده در سالن':
return tag.copyWith(
value: result.hatchingLeftOver?.separatedByCommaFa ?? '0',
isLoading: false,
);
default:
return tag;
}
}).toList();
// third
tagInfo['third'] = tagInfo['third']!.map((tag) {
switch (tag.labelTitle) {
case 'تلفات':
return tag.copyWith(
value: result.hatchingLosses?.separatedByCommaFa ?? '0',
isLoading: false,
);
case 'حجم کشتار شده':
return tag.copyWith(
value:
result.hatchingKilledQuantity?.separatedByCommaFa ?? '0',
isLoading: false,
);
default:
return tag;
}
}).toList();
ageCardData.value = ageCardData.map((element) {
switch (element.labelTitle) {
case 'کمترین سن جوجه ریزی':
return element.copyWith(
value: result.hatchingMinAge?.separatedByCommaFa ?? '0',
isLoading: false,
);
case 'بیشترین سن جوجه ریزی':
return element.copyWith(
value: result.hatchingMaxAge?.separatedByCommaFa ?? '0',
isLoading: false,
);
default:
return element;
}
}).toList();
}
},
onError: (error, stackTrace) {},
);
}
}

View File

@@ -0,0 +1,448 @@
import 'package:flutter/cupertino.dart' hide LinearGradient;
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/routes/routes.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 'package:rasadyar_core/presentation/widget/custom/information_card_widget.dart';
import 'logic.dart';
class PoultryScienceHomePage extends GetView<PoultryScienceHomeLogic> {
const PoultryScienceHomePage({super.key});
@override
Widget build(BuildContext context) {
return ChickenBasePage(
isBase: true,
hasNotification: true,
hasNews: true,
scrollable: true,
child: Column(
children: [
SizedBox(height: 18.h),
InkWell(
onTap: () {
controller.isExpanded.value = !controller.isExpanded.value;
},
child: Stack(
clipBehavior: Clip.none,
children: [
Container(
margin: EdgeInsetsGeometry.all(6),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(
width: 0.50,
color: const Color(0xFFA9A9A9),
),
),
child: ObxValue((data) {
return AnimatedSize(
duration: Duration(milliseconds: 300),
child: data.value
? mainItemWidget()
: mainItemWidgetExpanded(),
);
}, controller.isExpanded),
),
Positioned(
top: -10,
right: 20,
child: Container(
height: 32.h,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(
width: 0.50,
color: const Color(0xFFA9A9A9),
),
),
padding: EdgeInsets.symmetric(horizontal: 8),
child: Row(
spacing: 8,
children: [
Assets.vec.chicken2Svg.svg(
width: 16.w,
height: 16.h,
colorFilter: ColorFilter.mode(
AppColor.blueDark,
BlendMode.srcIn,
),
),
Text(
'اطلاعات فارم‌ها',
textAlign: TextAlign.right,
style: AppFonts.yekan16Bold.copyWith(
color: AppColor.iconColor,
),
),
],
),
),
),
],
),
),
SizedBox(height: 10),
widelyWidget(),
SizedBox(height: 20),
],
),
);
}
Padding mainItemWidget() {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 8),
firstTagInformation(),
secondTagInformation(),
],
),
);
}
Padding mainItemWidgetExpanded() {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 8),
/* Row(
spacing: 8,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
width: 40,
height: 40,
decoration: ShapeDecoration(
shape: RoundedRectangleBorder(
side: BorderSide(width: 0.25, color: const Color(0xFFB0B0B0)),
borderRadius: BorderRadius.circular(4),
),
),
child: Assets.images.liveChicken.image(
width: 40.w,
height: 40.h,
fit: BoxFit.cover,
),
),
Text(
'فارم ها',
textAlign: TextAlign.right,
style: AppFonts.yekan16.copyWith(color: AppColor.darkGreyDarkActive),
),
Spacer(),
AnimatedRotation(
turns: 180,
duration: Duration(milliseconds: 3000),
child: Icon(CupertinoIcons.chevron_up, size: 18),
),
],
),*/
SizedBox(height: 8),
firstTagInformation(),
Row(
children: [
Text(
'اطلاعات جوجه‌ریزی',
textAlign: TextAlign.right,
style: AppFonts.yekan16,
),
],
),
secondTagInformation(),
thirdTagInformation(),
ageCardInformation(),
],
),
);
}
Widget firstTagInformation() {
return Padding(
padding: const EdgeInsets.fromLTRB(0, 8, 0, 13),
child: ObxValue((data) {
List<InformationTagData>? items = data['first']!;
return Row(
spacing: 8,
children: List.generate(
items.length,
(index) => Expanded(child: InformationTag(data: items[index])),
),
);
}, controller.tagInfo),
);
}
Widget secondTagInformation() {
return Padding(
padding: const EdgeInsets.fromLTRB(0, 8, 0, 13),
child: ObxValue((data) {
List<InformationTagData>? items = data['second']!;
return Row(
spacing: 8,
children: List.generate(
items.length,
(index) => Expanded(child: InformationTag(data: items[index])),
),
);
}, controller.tagInfo),
);
}
Widget thirdTagInformation() {
return Padding(
padding: const EdgeInsets.fromLTRB(0, 8, 0, 13),
child: ObxValue((data) {
List<InformationTagData>? items = data['third']!;
return Row(
spacing: 8,
children: List.generate(
items.length,
(index) => Expanded(child: InformationTag(data: items[index])),
),
);
}, controller.tagInfo),
);
}
Widget ageCardInformation() {
return Padding(
padding: EdgeInsets.fromLTRB(30.w, 8, 30.w, 13),
child: ObxValue((data) {
return Row(
spacing: 8,
children: List.generate(
data.length,
(index) => Expanded(child: InformationCard(data: data[index])),
),
);
}, controller.ageCardData),
);
}
//region Widely Used Widget
Widget widelyWidget() {
return Container(
margin: EdgeInsetsGeometry.all(6),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 0.50, color: const Color(0xFFA9A9A9)),
),
child: Stack(
clipBehavior: Clip.none,
children: [
Padding(
padding: EdgeInsets.fromLTRB(12.w, 24.h, 12.w, 16.h),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
widelyUsed(
title: 'بازرسی',
iconPath: Assets.vec.cubeSearchSvg.path,
isOnEdit: false,
cardColor: AppColor.greenLightActive,
labelColor: AppColor.greenNormal,
textColor: AppColor.textColor,
onTap: () async {
controller.rootLogic.currentPage.value = 0;
Get.toNamed(
PoultryScienceRoutes.inspectionPoultryScience,
id: poultryFirstKey,
);
},
),
widelyUsed(
title: 'ثبت کشتار',
iconPath: Assets.vec.noteRemoveSvg.path,
isOnEdit: false,
cardColor: AppColor.blueLightActive,
labelColor: AppColor.blueNormalOld,
textColor: AppColor.textColor,
onTap: () async {
controller.rootLogic.currentPage.value = 0;
Get.toNamed(
PoultryScienceRoutes.genocidePoultryScience,
id: poultryFirstKey,
);
},
),
widelyUsed(
title: 'فارم ها',
iconPath: Assets.vec.cubeScanSvg.path,
cardColor: Color(0xFFFFCFA3),
labelColor: Color(0xFFF68D2B),
textColor: AppColor.textColor,
isOnEdit: false,
onTap: () async {
controller.rootLogic.currentPage.value = 0;
Get.toNamed(
PoultryScienceRoutes.farmPoultryScience,
id: poultryFirstKey,
);
},
),
widelyUsed(
title: 'جوجه‌ریزی فعال',
iconPath: Assets.vec.boxTickSvg.path,
isOnEdit: false,
cardColor: Color(0xFFD9BEFF),
labelColor: Color(0xFF9757FF),
textColor: AppColor.textColor,
onTap: () async {
controller.rootLogic.currentPage.value = 0;
Get.toNamed(
PoultryScienceRoutes.activeHatchingPoultryScience,
id: poultryFirstKey,
);
},
),
],
),
),
Positioned(
top: -17,
right: 11,
child: Container(
padding: EdgeInsets.all(4),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 0.50, color: const Color(0xFFA9A9A9)),
),
child: Text(
'پر کاربردها',
textAlign: TextAlign.right,
style: AppFonts.yekan16,
),
),
),
],
),
);
}
Widget widelyUsed({
required String title,
required String iconPath,
required VoidCallback onTap,
required bool isOnEdit,
Color? cardColor,
Color? labelColor,
Color? textColor,
}) {
return GestureDetector(
onTap: !isOnEdit ? onTap : null,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
spacing: 4,
children: [
Stack(
clipBehavior: Clip.none,
children: [
Container(
width: 48,
height: 48,
padding: EdgeInsets.all(4),
decoration: ShapeDecoration(
color: cardColor ?? Color(0xFFBECDFF),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Container(
width: 40,
height: 40,
decoration: ShapeDecoration(
color: labelColor ?? AppColor.blueNormal,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: SvgGenImage.vec(iconPath).svg(
width: 24,
height: 24,
colorFilter: ColorFilter.mode(
Colors.white,
BlendMode.srcIn,
),
fit: BoxFit.cover,
),
),
),
Visibility(
visible: isOnEdit,
child: Container(
width: 48,
height: 48,
padding: EdgeInsets.all(4),
decoration: ShapeDecoration(
color: Colors.white60,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
),
Visibility(
visible: isOnEdit,
child: Positioned(
top: -15,
left: -12,
child: SizedBox(
width: 32.w,
height: 32.h,
child: GestureDetector(
onTap: () {},
behavior: HitTestBehavior.translucent,
child: Center(
child: Container(
width: 16,
height: 16,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
alignment: Alignment.center,
child: Icon(
CupertinoIcons.minus,
color: AppColor.error,
size: 15,
),
),
),
),
),
),
),
],
),
Text(
title,
style: AppFonts.yekan10Bold.copyWith(
color: textColor ?? AppColor.blueNormal,
),
),
],
),
);
}
//endregion
}

View File

@@ -0,0 +1,294 @@
import 'package:flutter/material.dart';
<<<<<<<< HEAD:packages/chicken/lib/features/poultry_science/inspection/logic.dart
import 'package:rasadyar_chicken/data/models/response/hatching/hatching_models.dart';
import 'package:rasadyar_chicken/data/models/response/hatching_report/hatching_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/root/logic.dart';
========
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/hatching/hatching_models.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/hatching_report/hatching_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/root/logic.dart';
>>>>>>>> develop:packages/chicken/lib/features/poultry_science/presentation/pages/inspection/logic.dart
import 'package:rasadyar_core/core.dart';
class InspectionPoultryScienceLogic extends GetxController {
BaseLogic baseLogic = Get.find<BaseLogic>();
Rx<Resource<PaginationModel<HatchingModel>>> hatchingList =
Resource<PaginationModel<HatchingModel>>.loading().obs;
Rx<Resource<PaginationModel<HatchingReport>>> hatchingReportList =
Resource<PaginationModel<HatchingReport>>.loading().obs;
PoultryScienceRootLogic rootLogic = Get.find<PoultryScienceRootLogic>();
Rx<LatLng> currentLocation = LatLng(
34.798315281272544,
48.51479142983491,
).obs;
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();
getHatchingList();
getHatchingReport();
checkLocationPermission(request: true);
ever(pickedImages, (callback) {
_multiPartPickedImages.clear();
for (var element in pickedImages) {
_multiPartPickedImages.add(
MultipartFile.fromFileSync(element.path, filename: element.name),
);
}
});
}
@override
void onClose() {
super.onClose();
baseLogic.clearSearch();
}
Future<void> getHatchingList([bool isLoadingMore = false]) async {
if (isLoadingMore) {
isLoadingMoreAllocationsMade.value = true;
} else {
hatchingList.value = Resource<PaginationModel<HatchingModel>>.loading();
}
if (searchedValue.value != null &&
searchedValue.value!.trim().isNotEmpty &&
currentPage.value > 1) {
currentPage.value = 1;
}
safeCall(
call: () async => await rootLogic.poultryRepository.getHatchingPoultry(
token: rootLogic.tokenService.accessToken.value!,
queryParameters: buildQueryParams(
queryParams: {'type': 'hatching', 'report': true},
role: 'PoultryScience',
search: 'filter',
value: searchedValue.value,
fromDate: fromDateFilter.value.toDateTime(),
toDate: toDateFilter.value.toDateTime(),
pageSize: 50,
page: currentPage.value,
),
),
onSuccess: (res) {
if ((res?.count ?? 0) == 0) {
hatchingList.value = Resource<PaginationModel<HatchingModel>>.empty();
} else {
hatchingList.value = Resource<PaginationModel<HatchingModel>>.success(
PaginationModel<HatchingModel>(
count: res?.count ?? 0,
next: res?.next,
previous: res?.previous,
results: [
...(hatchingList.value.data?.results ?? []),
...(res?.results ?? []),
],
),
);
}
},
);
}
Future<void> getHatchingReport([bool isLoadingMore = false]) async {
if (isLoadingMore) {
isLoadingMoreAllocationsMade.value = true;
} else {
hatchingReportList.value =
Resource<PaginationModel<HatchingReport>>.loading();
}
if (searchedValue.value != null &&
searchedValue.value!.trim().isNotEmpty &&
currentPage.value > 1) {
currentPage.value = 1;
}
safeCall(
call: () async =>
await rootLogic.poultryRepository.getHatchingPoultryReport(
token: rootLogic.tokenService.accessToken.value!,
queryParameters: buildQueryParams(
role: 'PoultryScience',
pageSize: 50,
search: 'filter',
value: searchedValue.value,
fromDate: fromDateFilter.value.toDateTime(),
toDate: toDateFilter.value.toDateTime(),
page: currentPage.value,
),
),
onSuccess: (res) {
if ((res?.count ?? 0) == 0) {
hatchingReportList.value =
Resource<PaginationModel<HatchingReport>>.empty();
} else {
hatchingReportList.value =
Resource<PaginationModel<HatchingReport>>.success(
PaginationModel<HatchingReport>(
count: res?.count ?? 0,
next: res?.next,
previous: res?.previous,
results: [
...(hatchingReportList.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 clearImages() {
pickedImages.clear();
}
Future<void> submitInspectionReport({required int id}) async {
isOnUpload.value = true;
final tmpFiles = await Future.wait(
pickedImages.map(
(element) =>
MultipartFile.fromFile(element.path, filename: element.name),
),
);
var data = FormData.fromMap({
'file': tmpFiles,
'hatching_id': id.toString(),
'lat': currentLocation.value.latitude.toString(),
'log': currentLocation.value.longitude.toString(),
});
safeCall(
call: () async =>
await rootLogic.poultryRepository.submitPoultryScienceReport(
token: rootLogic.tokenService.accessToken.value!,
data: data,
onSendProgress: (sent, total) {
presentUpload.value = calculateUploadProgress(
sent: sent,
total: total,
);
},
),
onSuccess: (res) {
closeBottomSheet();
clearImages();
getHatchingList();
getHatchingReport();
isOnUpload.value = false;
},
onError: (error, stackTrace) async {
clearImages();
isOnUpload.value = false;
await Future.delayed(
const Duration(seconds: 4),
).then((value) => closeBottomSheet());
},
showError: true,
);
}
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;
}
String getStatus(HatchingReport item) {
if (item.state == 'accepted') {
return 'تکمیل شده';
} else if (item.state == 'rejected') {
return 'رد شده';
} else {
return 'در حال بررسی';
}
}
Color getStatusColor(HatchingReport item) {
if (item.state == 'accepted') {
return AppColor.greenNormal;
} else if (item.state == 'rejected') {
return AppColor.redNormal;
} else {
return AppColor.yellowNormal;
}
}
void setSearchValue(String? data) {
dLog('Search Value: $data');
searchedValue.value = data?.trim();
final isReporter = selectedSegmentIndex.value == 1;
if (isReporter) {
getHatchingReport();
} else {
getHatchingList();
}
}
Future<void> onRefresh() async {
currentPage.value = 1;
await Future.wait([getHatchingList(), getHatchingReport()]);
}
}

View File

@@ -0,0 +1,699 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/hatching/hatching_models.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/hatching_report/hatching_report.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 InspectionPoultrySciencePage extends GetView<InspectionPoultryScienceLogic> {
const InspectionPoultrySciencePage({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: poultryScienceActionKey,
routesWidget: ContainerBreadcrumb(rxRoutes: controller.routesName),
child: Column(
children: [
SizedBox(height: 50, child: segmentWidget()),
ObxValue((data) {
return data.value == 0 ? hatchingWidget() : reportWidget();
}, controller.selectedSegmentIndex),
],
),
);
}
Widget hatchingWidget() {
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: itemListWidget(item),
secondChild: itemListExpandedWidget(item),
labelColor: AppColor.blueLight,
labelIcon: item.reportInfo?.image == false
? Assets.vec.timerSvg.path
: Assets.vec.checkSquareSvg.path,
labelIconColor: item.reportInfo?.image == false
? AppColor.yellowNormal2
: AppColor.mediumGreyDarkHover,
);
}, controller.expandedIndex);
},
itemCount: data.value.data?.results?.length ?? 0,
separatorBuilder: (context, index) => SizedBox(height: 8.h),
onLoadMore: () async => controller.getHatchingList(true),
);
}, controller.hatchingList),
);
}
Container itemListExpandedWidget(HatchingModel item) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(8)),
child: Column(
spacing: 8,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
item.poultry?.user?.fullname ?? 'N/A',
textAlign: TextAlign.center,
style: AppFonts.yekan16.copyWith(color: AppColor.greenDark),
),
Spacer(),
Visibility(
child: Text(
item.violation == true ? 'پیگیری' : 'عادی',
textAlign: TextAlign.center,
style: AppFonts.yekan10.copyWith(color: AppColor.redDark),
),
),
],
),
Container(
height: 32,
padding: EdgeInsets.symmetric(horizontal: 8),
decoration: ShapeDecoration(
color: AppColor.blueLight,
shape: RoundedRectangleBorder(
side: BorderSide(width: 1, color: AppColor.blueLightHover),
borderRadius: BorderRadius.circular(8),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'نژاد:${item.breed?.first.breed ?? 'N/A'}',
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
),
Text(
' سن ${item.age} (روزه)',
style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
),
Text(
' دوره جوجه ریزی:${item.period}',
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
),
],
),
),
buildRow(title: 'شماره مجوز جوجه ریزی', value: item.licenceNumber ?? 'N/A'),
buildUnitRow(
title: 'حجم جوجه ریزی',
value: item.quantity.separatedByCommaFa,
unit: '(قطعه)',
),
buildUnitRow(
title: 'مانده در سالن',
value: item.leftOver.separatedByCommaFa,
unit: '(قطعه)',
),
buildUnitRow(title: 'تلفات', value: item.losses.separatedByCommaFa, unit: '(قطعه)'),
buildRow(
title: 'دامپزشک فارم',
value: '${item.vetFarm?.vetFarmFullName}(${item.vetFarm?.vetFarmMobile})',
),
buildRow(
title: 'شرح بازرسی',
value: item.reportInfo?.image == false ? 'ارسال تصویر جوجه ریزی فارم ' : 'تکمیل شده',
titleStyle: AppFonts.yekan14.copyWith(
color: (item.reportInfo?.image ?? false) ? AppColor.greenNormal : AppColor.redDark,
),
valueStyle: AppFonts.yekan14.copyWith(
color: (item.reportInfo?.image ?? false) ? AppColor.greenNormal : AppColor.redDark,
),
),
Visibility(
visible: (item.reportInfo?.image == false),
child: RElevated(
text: 'ثبت بازرسی',
isFullWidth: true,
width: 150.w,
height: 40.h,
onPressed: () {
cameraBottomSheet(item.id!);
},
textStyle: AppFonts.yekan20.copyWith(color: Colors.white),
backgroundColor: AppColor.greenNormal,
),
),
],
),
);
}
void cameraBottomSheet(int id) {
Get.bottomSheet(
isDismissible: false,
isScrollControlled: false,
BaseBottomSheet(
height: 350.h,
child: Column(
children: [
ObxValue((data) {
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
childAspectRatio: 1,
),
shrinkWrap: true,
itemCount: controller.pickedImages.length + 1,
itemBuilder: (context, index) {
if (index + 1 < 7 && index == data.length) {
return GestureDetector(
onTap: () async {
await controller.pickImages();
},
child: Container(
width: 80.h,
height: 80.h,
decoration: BoxDecoration(
color: AppColor.lightGreyNormal,
borderRadius: BorderRadius.circular(8.r),
),
child: Center(
child: Icon(
Icons.add_a_photo,
color: AppColor.lightGreyDarker,
size: 32.h,
),
),
),
);
} else {
return Container(
width: 80.h,
height: 80.h,
decoration: BoxDecoration(
color: AppColor.lightGreyNormal,
borderRadius: BorderRadius.circular(8.r),
),
child: Stack(
children: [
Positioned.fill(
child: Image.file(File(data[index].path), fit: BoxFit.cover),
),
Positioned(
top: 4,
left: 4,
child: GestureDetector(
onTap: () {
controller.removeImage(index);
},
child: Container(
width: 24.w,
height: 24.h,
clipBehavior: Clip.antiAlias,
padding: EdgeInsets.all(4),
decoration: ShapeDecoration(
color: Colors.white.withValues(alpha: 0.80),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
),
child: Assets.vec.trashSvg.svg(
width: 8.w,
height: 8.h,
colorFilter: ColorFilter.mode(
AppColor.redNormal,
BlendMode.srcIn,
),
),
),
),
),
],
),
);
}
},
);
}, controller.pickedImages),
SizedBox(height: 35.h),
Text(
'حداقل ۲ تصویر برای ثبت بازرسی لازم است',
style: AppFonts.yekan12.copyWith(color: AppColor.textColor),
),
SizedBox(height: 8.h),
Row(
spacing: 16,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Obx(() {
return RElevated(
height: 40.h,
text: 'ارسال',
backgroundColor: AppColor.greenNormal,
progress: controller.presentUpload.value,
isLoading: controller.isOnUpload.value,
enabled: controller.pickedImages.length >= 2,
onPressed: () async {
controller.submitInspectionReport(id: id);
},
);
}),
ObxValue((data) {
return RElevated(
height: 40.h,
text: 'انصراف',
backgroundColor: AppColor.redNormal,
enabled: !data.value,
onPressed: () {
if (!data.value) {
controller.clearImages();
Get.back();
}
},
);
}, controller.isOnUpload),
],
),
SizedBox(height: 8.h),
],
),
),
).whenComplete(() {
controller.pickedImages.clear();
});
}
Padding segmentWidget() {
return Padding(
padding: const EdgeInsets.fromLTRB(8, 0, 8, 8),
child: Row(
children: [
Expanded(
child: RSegment(
children: ['بازرسی', 'بایگانی'],
selectedIndex: 0,
borderColor: const Color(0xFFB4B4B4),
selectedBorderColor: AppColor.blueNormal,
selectedBackgroundColor: AppColor.blueLight,
onSegmentSelected: (index) => controller.selectedSegmentIndex.value = index,
backgroundColor: AppColor.whiteGreyNormal,
),
),
],
),
);
}
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: item.state == 'rejected' ? AppColor.redLight : 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.getHatchingReport(true),
);
}, controller.hatchingReportList),
);
}
Widget itemListExpandedWidgetReport(HatchingReport item) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(8)),
child: Column(
spacing: 8,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
item.hatching?.poultry?.user?.fullname ?? 'N/A',
textAlign: TextAlign.center,
style: AppFonts.yekan16.copyWith(color: AppColor.greenDark),
),
Spacer(),
Visibility(
child: Text(
item.hatching?.violation == true ? 'پیگیری' : 'عادی',
textAlign: TextAlign.center,
style: AppFonts.yekan10.copyWith(color: AppColor.redDark),
),
),
],
),
Container(
height: 32,
padding: EdgeInsets.symmetric(horizontal: 8),
decoration: ShapeDecoration(
color: AppColor.blueLight,
shape: RoundedRectangleBorder(
side: BorderSide(width: 1, color: AppColor.blueLightHover),
borderRadius: BorderRadius.circular(8),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
spacing: 3,
children: [
Text('نژاد:', style: AppFonts.yekan14.copyWith(color: AppColor.textColor)),
Text(
item.hatching?.chickenBreed ?? 'N/A',
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
),
],
),
Text(
' سن ${item.hatching?.chickenAge} (روزه)',
style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
),
Text(
' دوره جوجه ریزی:${item.hatching?.period}',
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
),
],
),
),
buildRow(title: 'شماره مجوز جوجه ریزی', value: item.hatching?.licenceNumber ?? 'N/A'),
buildUnitRow(
title: 'حجم جوجه ریزی',
value: item.hatching?.quantity.separatedByCommaFa ?? 'N/A',
unit: '(قطعه)',
),
buildUnitRow(
title: 'مانده در سالن',
value: item.hatching?.leftOver.separatedByCommaFa ?? 'N/A',
unit: '(قطعه)',
),
buildUnitRow(
title: 'تلفات',
value: item.hatching?.losses.separatedByCommaFa ?? 'N/A',
unit: '(قطعه)',
),
buildRow(
title: 'دامپزشک فارم',
value:
'${item.hatching?.vetFarm?.vetFarmFullName}(${item.hatching?.vetFarm?.vetFarmMobile})',
),
buildRow(
title: 'شرح بازرسی',
value: controller.getStatus(item),
titleStyle: AppFonts.yekan14.copyWith(color: controller.getStatusColor(item)),
valueStyle: AppFonts.yekan14.copyWith(color: controller.getStatusColor(item)),
),
if (item.state == 'accepted') ...{
Visibility(
visible: item.realQuantityAi != null,
child: buildRow(
title: 'تعداد تاییده هوش مصنوعی',
value: item.realQuantityAi.separatedByComma,
),
),
Visibility(
visible: item.realQuantity != null,
child: buildRow(
title: 'تعداد تاییده',
value: item.realQuantity.separatedByComma,
),
),
},
if (item.state == 'rejected') ...{
Visibility(
visible: item.messageAi != null,
child: buildRow(title: 'پیام هوش مصنوعی', value: item.messageAi ?? '-'),
),
Visibility(
visible: item.message != null,
child: buildRow(title: 'پیام', value: item.message ?? '-'),
),
Visibility(
visible: item.messageRegistererFullname != null,
child: buildRow(
title: 'ثبت کننده گزارش',
value: item.messageRegistererFullname ?? '-',
),
),
Visibility(
visible: item.messageRegistererMobile != null,
child: buildRow(
title: 'موبایل کننده گزارش',
value: item.messageRegistererMobile ?? '-',
),
),
Visibility(
visible: item.messageRegistererRole != null,
child: buildRow(title: 'نقش کننده گزارش', value: item.messageRegistererRole ?? '-'),
),
},
SizedBox(
height: 140.h,
child: GridView.builder(
shrinkWrap: true,
itemCount: item.image?.length ?? 0,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
),
itemBuilder: (context, index) => Container(
height: 100.h,
clipBehavior: Clip.hardEdge,
child: Image.network(
item.image?[index] ?? '',
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Padding(
padding: EdgeInsetsGeometry.all(80),
child: CircularProgressIndicator(
color: AppColor.blueDark,
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
(loadingProgress.expectedTotalBytes ?? 1)
: null,
),
);
},
),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8.r)),
),
),
),
],
),
);
}
Widget itemListWidget(HatchingModel 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.poultry?.user?.fullname ?? 'N/A',
textAlign: TextAlign.start,
style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal),
),
Text(
item.poultry?.user?.mobile ?? 'N/A',
textAlign: TextAlign.center,
style: AppFonts.yekan14.copyWith(color: AppColor.bgDark),
),
],
),
),
Expanded(
flex: 3,
child: Column(
spacing: 5,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
item.poultry?.unitName ?? 'N/Aaq',
textAlign: TextAlign.start,
style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal),
),
Text(
item.poultry?.licenceNumber ?? 'N/A',
textAlign: TextAlign.left,
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),
),
),
],
);
}
Row itemListWidgetReport(HatchingReport 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.hatching?.poultry?.user?.fullname ?? 'N/A',
textAlign: TextAlign.start,
style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal),
),
Text(
item.hatching?.poultry?.user?.mobile ?? 'N/A',
textAlign: TextAlign.center,
style: AppFonts.yekan14.copyWith(color: AppColor.bgDark),
),
],
),
),
Expanded(
flex: 3,
child: Column(
spacing: 5,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
item.hatching?.poultry?.unitName ?? 'N/Aaq',
textAlign: TextAlign.start,
style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal),
),
Text(
item.hatching?.licenceNumber ?? 'N/A',
textAlign: TextAlign.left,
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),
),
),
],
);
}
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: () {
final isReporter = controller.selectedSegmentIndex.value == 0;
if (isReporter) {
controller.getHatchingReport();
} else {
controller.getHatchingList();
}
Get.back();
},
height: 40,
),
],
),
);
}
}

View File

@@ -0,0 +1,462 @@
import 'package:flutter/material.dart';
<<<<<<<< HEAD:packages/chicken/lib/features/poultry_science/killing_registration/logic.dart
import 'package:rasadyar_chicken/data/models/poultry_export/poultry_export.dart';
import 'package:rasadyar_chicken/data/models/request/kill_registration/kill_registration.dart';
import 'package:rasadyar_chicken/data/models/response/all_poultry/all_poultry.dart';
import 'package:rasadyar_chicken/data/models/response/approved_price/approved_price.dart';
import 'package:rasadyar_chicken/data/models/response/kill_house_poultry/kill_house_poultry.dart';
import 'package:rasadyar_chicken/data/models/response/kill_request_poultry/kill_request_poultry.dart';
import 'package:rasadyar_chicken/data/models/response/poultry_hatching/poultry_hatching.dart';
import 'package:rasadyar_chicken/data/models/response/sell_for_freezing/sell_for_freezing.dart';
import 'package:rasadyar_chicken/features/poultry_science/genocide/logic.dart';
import 'package:rasadyar_chicken/features/poultry_science/root/logic.dart';
========
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_export/poultry_export.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/all_poultry/all_poultry.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/approved_price/approved_price.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/kill_house_poultry/kill_house_poultry.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/kill_request_poultry/kill_request_poultry.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_hatching/poultry_hatching.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/sell_for_freezing/sell_for_freezing.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/genocide/logic.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/root/logic.dart';
>>>>>>>> develop:packages/chicken/lib/features/poultry_science/presentation/pages/killing_registration/logic.dart
import 'package:rasadyar_core/core.dart';
class KillingRegistrationLogic extends GetxController {
List<String> routes = ['اقدام', 'درخواست کشتارها', 'ثبت کشتار'];
var tokenService = Get.find<TokenStorageService>();
var gService = Get.find<GService>();
var rootLogic = Get.find<PoultryScienceRootLogic>();
GlobalKey<FormState> formKey = GlobalKey<FormState>();
Rxn<ApprovedPrice> approvedPrice = Rxn<ApprovedPrice>();
Rx<Jalali> killsDate = Jalali.now().obs;
Rxn<SellForFreezing> sellForFreezing = Rxn<SellForFreezing>();
Rxn<PoultryExport> poultryExport = Rxn<PoultryExport>();
TextEditingController quantityKillsController = TextEditingController();
RxBool quantityKillsIsCompleted = false.obs;
TextEditingController quantityLoseController = TextEditingController(
text: 0.toString(),
);
TextEditingController averageWeightKillsController = TextEditingController();
RxBool averageWeightKillsIsCompleted = false.obs;
TextEditingController priceFreeSaleController = TextEditingController();
RxInt generatedApprovedPrice = 0.obs;
RxBool isOnSubmitLoading = false.obs;
RxBool isExportSelected = false.obs;
RxBool isFreezedSelected = false.obs;
RxBool isMarketSelected = false.obs;
RxBool isFreeSale = false.obs;
//step 1
Rx<Resource<List<AllPoultry>>> allPoultryList =
Resource<List<AllPoultry>>.loading().obs;
Rxn<AllPoultry> selectedPoultry = Rxn();
//step 2
Rx<Resource<List<KillRequestPoultry>>> poultryList =
Resource<List<KillRequestPoultry>>.success([]).obs;
Rxn<KillRequestPoultry> selectedKillRequestPoultry = Rxn();
//step 3
Rx<Resource<List<PoultryHatching>>> poultryHatchingList =
Resource<List<PoultryHatching>>.success([]).obs;
Rxn<PoultryHatching> selectedPoultryHatching = Rxn();
//step 5
Rx<Resource<List<KillHousePoultry>>> killHouseList =
Resource<List<KillHousePoultry>>.success([]).obs;
Rxn<KillHousePoultry> selectedKillHouse = Rxn();
@override
void onReady() {
super.onReady();
getApprovedPrice();
getAllPoultryList();
getSellForFreezing();
getPoultryExport();
priceListener();
ever(selectedKillRequestPoultry, (callback) {
if (callback?.provinceAllowChooseKillHouse?.allowState ?? false) {
getKillHouseList();
}
});
everAll(
[
selectedPoultry,
selectedKillRequestPoultry,
selectedPoultryHatching,
selectedKillHouse,
],
(callback) {
checkSubmitButton();
},
);
}
@override
void onClose() {
super.onClose();
quantityKillsController.dispose();
quantityLoseController.dispose();
averageWeightKillsController.dispose();
priceFreeSaleController.dispose();
clearSelectedStep1();
clearSelectedStep2();
selectedKillHouse.value = null;
killHouseList.value = Resource<List<KillHousePoultry>>.success([]);
}
void priceListener() {
quantityKillsController.addListener(() {
quantityKillsIsCompleted.value = quantityKillsController.text
.trim()
.isNotEmpty;
if (averageWeightKillsController.text.isNotEmpty &&
quantityKillsController.text.trim().isNotEmpty) {
generatedApprovedPrice.value = calculateApprovedPrice().toInt();
priceFreeSaleController.text =
generatedApprovedPrice.value.separatedByComma;
checkSubmitButton();
} else {
generatedApprovedPrice.value = 0;
priceFreeSaleController.text = '0';
checkSubmitButton();
}
});
averageWeightKillsController.addListener(() {
averageWeightKillsIsCompleted.value = averageWeightKillsController.text
.trim()
.isNotEmpty;
if (averageWeightKillsController.text.trim().isNotEmpty &&
quantityKillsController.text.trim().isNotEmpty) {
generatedApprovedPrice.value = calculateApprovedPrice().toInt();
priceFreeSaleController.text =
generatedApprovedPrice.value.separatedByComma;
checkSubmitButton();
} else {
generatedApprovedPrice.value = 0;
priceFreeSaleController.text = '0';
checkSubmitButton();
}
});
priceFreeSaleController.addListener(() {
final text = priceFreeSaleController.text;
if (text.isNotEmpty) {
generatedApprovedPrice.value = int.parse(text.replaceAll(',', ''));
} else {
generatedApprovedPrice.value = 0;
}
checkSubmitButton();
});
}
void clearSelectedStep1() {
selectedPoultry.value = null;
selectedKillRequestPoultry.value = null;
selectedPoultryHatching.value = null;
poultryList.value = Resource<List<KillRequestPoultry>>.success([]);
poultryHatchingList.value = Resource<List<PoultryHatching>>.success([]);
}
void clearSelectedStep2() {
selectedKillRequestPoultry.value = null;
selectedPoultryHatching.value = null;
poultryHatchingList.value = Resource<List<PoultryHatching>>.success([]);
}
Future<void> getApprovedPrice() async {
await safeCall(
call: () async => await rootLogic.poultryRepository.getApprovedPrice(
token: tokenService.accessToken.value ?? '',
),
onSuccess: (result) {
if (result != null) {
approvedPrice.value = result;
}
},
onError: (error, stackTrace) {},
);
}
Future<void> getAllPoultryList() async {
await safeCall(
call: () async => await rootLogic.poultryRepository.getAllPoultry(
token: tokenService.accessToken.value ?? '',
queryParameters: buildRawQueryParams(
role: gService.getRole(Module.chicken),
),
),
onSuccess: (result) {
if (result != null) {
allPoultryList.value = Resource<List<AllPoultry>>.success(result);
}
},
onError: (error, stackTrace) {
allPoultryList.value = Resource<List<AllPoultry>>.error(
'$error -- $stackTrace',
);
},
);
}
Future<void> getSellForFreezing() async {
await safeCall(
call: () async => await rootLogic.poultryRepository.getSellForFreezing(
token: tokenService.accessToken.value ?? '',
),
onSuccess: (result) {
if (result != null) {
sellForFreezing.value = result;
}
},
onError: (error, stackTrace) {},
);
}
Future<void> getPoultryExport() async {
await safeCall(
call: () async => await rootLogic.poultryRepository.getPoultryExport(
token: tokenService.accessToken.value ?? '',
),
onSuccess: (result) {
if (result != null) {
poultryExport.value = result;
}
},
onError: (error, stackTrace) {},
);
}
Future<void> getUserPoultryList() async {
poultryList.value = Resource<List<KillRequestPoultry>>.loading();
await safeCall(
call: () async => await rootLogic.poultryRepository.getUserPoultry(
token: tokenService.accessToken.value ?? '',
queryParameters: buildQueryParams(
value: selectedPoultry.value?.user?.mobile,
queryParams: {'type': 'filter'},
),
),
onSuccess: (result) {
if (result != null) {
poultryList.value = Resource<List<KillRequestPoultry>>.success(
result,
);
}
},
onError: (error, stackTrace) {
poultryList.value = Resource<List<KillRequestPoultry>>.error(
'$error -- $stackTrace',
);
},
);
}
Future<void> getPoultryHatchingList() async {
poultryHatchingList.value = Resource<List<PoultryHatching>>.loading();
await safeCall(
call: () async => await rootLogic.poultryRepository.getPoultryHatching(
token: tokenService.accessToken.value ?? '',
queryParameters: buildRawQueryParams(
queryParams: {'key': selectedKillRequestPoultry.value?.key},
),
),
onSuccess: (result) {
if (result != null) {
poultryHatchingList.value = Resource<List<PoultryHatching>>.success(
result,
);
}
},
onError: (error, stackTrace) {
poultryHatchingList.value = Resource<List<PoultryHatching>>.error(
'$error -- $stackTrace',
);
},
);
}
Future<void> getKillHouseList() async {
killHouseList.value = Resource<List<KillHousePoultry>>.loading();
await safeCall(
call: () async => await rootLogic.poultryRepository.getKillHouseList(
token: tokenService.accessToken.value ?? '',
queryParameters: buildRawQueryParams(
queryParams: {
'show_poultry': '',
'date': DateTime.now().formattedDashedGregorian,
},
),
),
onSuccess: (result) {
if (result != null) {
killHouseList.value = Resource<List<KillHousePoultry>>.success(
result,
);
}
},
onError: (error, stackTrace) {
killHouseList.value = Resource<List<KillHousePoultry>>.error(
'$error -- $stackTrace',
);
},
);
}
double calculateApprovedPrice() {
final inputWeight = double.parse(averageWeightKillsController.text) * 1000;
final lowestWeight = approvedPrice.value?.lowestWeight ?? 0;
final highestWeight = approvedPrice.value?.highestWeight ?? 0;
final lowestPrice = approvedPrice.value?.lowestPrice ?? 0;
final highestPrice = approvedPrice.value?.highestPrice ?? 0;
if (inputWeight <= lowestWeight) {
return lowestPrice;
} else if (inputWeight >= highestWeight) {
return highestPrice;
} else {
final diffWeight = highestWeight - lowestWeight;
final diffPrice = highestPrice - lowestPrice;
final fraction = diffPrice / diffWeight;
final diffFromMinWeight = inputWeight - lowestWeight;
return diffFromMinWeight * fraction + lowestPrice;
}
}
void changeSaleType() {
isFreeSale.value = !isFreeSale.value;
generatedApprovedPrice.value = calculateApprovedPrice().toInt();
}
void checkSubmitButton() {
isOnSubmitLoading.value =
selectedPoultry.value != null &&
selectedKillRequestPoultry.value != null &&
selectedPoultryHatching.value != null &&
quantityKillsController.text.isNotEmpty &&
averageWeightKillsController.text.isNotEmpty &&
((selectedKillRequestPoultry
.value
?.provinceAllowChooseKillHouse
?.mandatory ??
false)
? selectedKillHouse.value != null
: true);
}
Future<void> submitKillRegistration() async {
isOnSubmitLoading.value = false;
if (!formKey.currentState!.validate()) {
return;
}
dLog(double.parse(averageWeightKillsController.text));
KillRegistrationRequest registrationRequest = KillRegistrationRequest(
indexWeight: double.parse(averageWeightKillsController.text),
amount: generatedApprovedPrice.value,
approvedPrice: approvedPrice.value?.approved ?? false,
auctionList: [],
cash: true,
chickenBreed: selectedPoultryHatching.value?.chickenBreed,
confirmPoultryMobile: selectedPoultry.value?.user?.mobile,
credit: false,
export: isExportSelected.value,
financialOperation: "outside-system",
freeSaleInProvince: isMarketSelected.value,
freezing: isFreezedSelected.value,
killHouseList: [
if (selectedKillHouse.value != null)
'${selectedKillHouse.value?.name}(${selectedKillHouse.value?.fullname})',
],
killReqKey: selectedKillHouse.value?.killReqKey,
losses: quantityLoseController.text,
market: isMarketSelected.value,
operatorKey: "",
poultryHatchingKey: selectedPoultryHatching.value?.key,
poultryKey: selectedPoultry.value?.key,
quantity: int.parse(quantityKillsController.text.clearComma),
role: gService.getRole(Module.chicken),
sendDate: killsDate.value.toDateTime().formattedDashedGregorian,
);
await safeCall(
call: () async =>
await rootLogic.poultryRepository.submitKillRegistration(
token: tokenService.accessToken.value ?? '',
request: registrationRequest,
),
onSuccess: (result) async {
defaultShowSuccessMessage(
'ثبت با موفقیت انجام شد',
durationInSeconds: 2,
onDismissed: () async {
Get.find<GenocideLogic>().getPoultryOrderList();
Future.delayed(Duration(milliseconds: 300), () {
Get.back();
});
Get.find<GenocideLogic>().getPoultryOrderList();
},
);
},
onError: (error, stackTrace) {},
);
}
void clearAllFields() {
// ریست فرم
formKey.currentState?.reset();
// ریست Rxn ها
approvedPrice.value = null;
sellForFreezing.value = null;
poultryExport.value = null;
selectedPoultry.value = null;
selectedKillRequestPoultry.value = null;
selectedPoultryHatching.value = null;
selectedKillHouse.value = null;
// ریست Rx ها
killsDate.value = Jalali.now();
generatedApprovedPrice.value = 0;
isOnSubmitLoading.value = false;
isExportSelected.value = false;
isFreezedSelected.value = false;
isMarketSelected.value = false;
isFreeSale.value = false;
// ریست TextEditingController ها
quantityKillsController.clear();
quantityLoseController.text = '0';
averageWeightKillsController.clear();
priceFreeSaleController.clear();
// ریست RxBool ها
quantityKillsIsCompleted.value = false;
averageWeightKillsIsCompleted.value = false;
// ریست Resource لیست‌ها
allPoultryList.value = Resource<List<AllPoultry>>.loading();
poultryList.value = Resource<List<KillRequestPoultry>>.success([]);
poultryHatchingList.value = Resource<List<PoultryHatching>>.success([]);
killHouseList.value = Resource<List<KillHousePoultry>>.success([]);
}
}

View File

@@ -0,0 +1,587 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/all_poultry/all_poultry.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/kill_house_poultry/kill_house_poultry.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/kill_request_poultry/kill_request_poultry.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_hatching/poultry_hatching.dart';
import 'package:rasadyar_core/core.dart';
import 'logic.dart';
class KillingRegistrationPage extends GetView<KillingRegistrationLogic> {
const KillingRegistrationPage({super.key});
@override
Widget build(BuildContext context) {
return Form(
key: controller.formKey,
child: Column(
children: [
poultryFarmWidget(),
poultryUserListWidget(),
poultryHatchingWidget(),
ObxValue((data) {
return Visibility(
visible: controller.selectedPoultryHatching.value != null,
child: Column(
children: [
informationWidget(),
killDateWidget(
date: controller.killsDate,
onChanged: (Jalali jalali) {
controller.killsDate.value = jalali;
},
),
quantityKillsWidget(),
],
),
);
}, controller.selectedPoultryHatching),
ObxValue((data) {
return Visibility(
visible: data.value,
child: Column(children: [averageWeightKillsWidget()]),
);
}, controller.quantityKillsIsCompleted),
ObxValue((data) {
return Visibility(
visible: data.value,
child: Column(
children: [
saleTypeWidget(),
priceWidget(),
buyerListWidget(),
slaughterhouseSelectedWidget(),
submitButtonWidget(),
],
),
);
}, controller.averageWeightKillsIsCompleted),
],
),
);
}
Widget poultryFarmWidget() {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 10.h),
child: ObxValue((data) {
return ResourceOverlayDropdown<AllPoultry>(
items: data.value,
background: Colors.white,
onChanged: (value) {
controller.clearSelectedStep1();
controller.selectedPoultry.value = value;
controller.getUserPoultryList();
},
selectedItem: controller.selectedPoultry.value,
itemBuilder: (item) => labelPoultryWidget(item),
labelBuilder: (item) => labelPoultryWidget(item),
);
}, controller.allPoultryList),
);
}
Widget labelPoultryWidget(AllPoultry? item) {
if (item == null) {
return Row(
children: [
Assets.vec.farmSvg.svg(
width: 28.w,
height: 28.h,
colorFilter: const ColorFilter.mode(AppColor.darkGreyDark, BlendMode.srcIn),
),
SizedBox(width: 4.w),
Text('انتخاب مرغداری', style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDark)),
],
);
} else {
return Text('${item.unitName} (${item.address?.city?.name})', maxLines: 2);
}
}
Widget poultryUserListWidget() {
return ObxValue((data) {
if (data.value == null) {
return SizedBox.shrink();
}
return Padding(
padding: EdgeInsets.symmetric(horizontal: 8.w),
child: ObxValue((data) {
return ResourceOverlayDropdown<KillRequestPoultry>(
items: data.value,
background: Colors.white,
onChanged: (value) {
controller.clearSelectedStep2();
controller.selectedKillRequestPoultry.value = value;
controller.getPoultryHatchingList();
},
selectedItem: controller.selectedKillRequestPoultry.value,
itemBuilder: (item) => labelPoultryUser(item),
labelBuilder: (item) => labelPoultryUser(item),
);
}, controller.poultryList),
);
}, controller.selectedPoultry);
}
Widget labelPoultryUser(KillRequestPoultry? item) {
if (item == null) {
return Row(
children: [
Assets.vec.chickenHouseSvg.svg(
width: 28.w,
height: 28.h,
colorFilter: const ColorFilter.mode(AppColor.darkGreyDark, BlendMode.srcIn),
),
SizedBox(width: 6.w),
Text('محل پرورش', style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDark)),
],
);
} else {
return Text(item.unitName ?? '-');
}
}
Widget poultryHatchingWidget() {
return ObxValue((data) {
if (data.value == null) {
return SizedBox.shrink();
}
return Padding(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 10.h),
child: ObxValue((data) {
return ResourceOverlayDropdown<PoultryHatching>(
items: data.value,
background: Colors.white,
onChanged: (value) {
controller.selectedPoultryHatching.value = value;
},
selectedItem: controller.selectedPoultryHatching.value,
itemBuilder: (item) => labelPoultryHatching(item),
labelBuilder: (item) => labelPoultryHatching(item),
);
}, controller.poultryHatchingList),
);
}, controller.selectedKillRequestPoultry);
}
Widget labelPoultryHatching(PoultryHatching? item) {
if (item == null) {
return Row(
children: [
Assets.vec.calendarSvg.svg(
width: 28.w,
height: 28.h,
colorFilter: const ColorFilter.mode(AppColor.darkGreyDark, BlendMode.srcIn),
),
SizedBox(width: 6.w),
Text('دوره جوجه ریزی', style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDark)),
],
);
} else {
return Text(
' دوره ${item.period} سالن ${item.hall} نژاد ${item.chickenBreed} باقیمانده ${item.leftOver} قطعه ',
);
}
}
Widget informationWidget() {
return Container(
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(8)),
margin: EdgeInsets.fromLTRB(8.w, 0, 8.w, 10.h),
padding: EdgeInsets.all(7),
child: ObxValue(
(data) => Column(
spacing: 8,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Text(
'اطلاعات مرغداری',
textAlign: TextAlign.right,
style: AppFonts.yekan14Bold.copyWith(color: AppColor.blueNormal),
),
),
Container(
decoration: ShapeDecoration(
color: const Color(0xFFEAEFFF),
shape: RoundedRectangleBorder(
side: BorderSide(width: 1, color: const Color(0xFFE0E7FF)),
borderRadius: BorderRadius.circular(8),
),
),
padding: EdgeInsets.all(4),
child: buildUnitRow(
title: 'تعداد جوجه ریزی',
value: data.value?.quantity.separatedByCommaFa,
unit: 'قطعه',
),
),
buildUnitRow(
title: 'تلفات',
value: data.value?.losses.separatedByCommaFa,
unit: 'قطعه',
padding: EdgeInsetsGeometry.symmetric(horizontal: 4),
),
buildUnitRow(
title: 'باقیمانده',
value: data.value?.leftOver.separatedByCommaFa,
unit: 'قطعه',
padding: EdgeInsetsGeometry.symmetric(horizontal: 4),
),
buildUnitRow(
title: 'سن جوجه',
value: data.value?.chickenAge.separatedByCommaFa,
unit: 'روز',
padding: EdgeInsetsGeometry.symmetric(horizontal: 4),
),
buildUnitRow(
title: 'مجوز فروش آزاد',
value:
data.value?.freeGovernmentalInfo?.totalFreeCommitmentQuantity.separatedByCommaFa,
unit: 'قطعه',
padding: EdgeInsetsGeometry.symmetric(horizontal: 4),
),
buildUnitRow(
title: 'مانده فروش آزاد',
value: data
.value
?.freeGovernmentalInfo
?.leftTotalFreeCommitmentQuantity
.separatedByCommaFa,
unit: 'قطعه',
padding: EdgeInsetsGeometry.symmetric(horizontal: 4),
),
buildUnitRow(
title: 'تلفن مرغدار',
value: data.value?.poultry?.userprofile?.mobile,
unit: '',
padding: EdgeInsetsGeometry.symmetric(horizontal: 4),
),
],
),
controller.selectedPoultryHatching,
),
);
}
Widget killDateWidget({required Rx<Jalali> date, required Function(Jalali jalali) onChanged}) {
return GestureDetector(
onTap: () {
Get.bottomSheet(modalDatePicker(onDateSelected: (value) => onChanged(value)));
},
child: Container(
height: 40,
margin: EdgeInsets.symmetric(horizontal: 8.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 1, color: AppColor.darkGreyLight),
),
padding: EdgeInsets.symmetric(horizontal: 11, vertical: 4),
child: Row(
spacing: 8,
children: [
Assets.vec.calendarSvg.svg(
width: 24,
height: 24,
colorFilter: const ColorFilter.mode(AppColor.bgDark, BlendMode.srcIn),
),
Text('تاریخ کشتار', style: AppFonts.yekan14.copyWith(color: AppColor.bgDark)),
Spacer(),
ObxValue((data) {
return Text(
date.value.formatCompactDate(),
textAlign: TextAlign.center,
style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDark),
);
}, date),
],
),
),
);
}
Widget quantityKillsWidget() {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 10.h),
child: RTextField(
label: 'تعداد کشتار (قطعه)',
filled: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'تعداد کشتار را وارد کنید';
}
final count = double.tryParse(value.replaceAll(',', ''));
if (controller.isFreeSale.value) {
if (count! >
(controller
.selectedPoultryHatching
.value
?.freeGovernmentalInfo
?.leftTotalFreeCommitmentQuantity ??
0)) {
return 'مجوز فروش آزاد شما کافی نیست';
}
} else {
if (count! > (controller.selectedPoultryHatching.value?.leftOver ?? 0)) {
return 'تعداد کشتار نباید بیشتر از باقیمانده جوجه ریزی باشد';
}
}
return null;
},
textInputAction: TextInputAction.next,
filledColor: Colors.white,
keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly, SeparatorInputFormatter()],
controller: controller.quantityKillsController,
),
);
}
Widget averageWeightKillsWidget() {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 8.w),
child: RTextField(
label: 'میانگین وزن (کیلوگرم)',
filled: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'میانگین وزن را وارد کنید';
}
final weight = double.tryParse(value.replaceAll(',', ''));
if (weight == null || weight <= 0) {
return 'میانگین وزن باید عددی بزرگتر از صفر باشد';
} else if (weight >
(controller.selectedPoultryHatching.value?.managementHatchingAgeRange?.toWeight ??
10000) ||
weight <
(controller
.selectedPoultryHatching
.value
?.managementHatchingAgeRange
?.fromWeight ??
-10000)) {
return 'میانگین وزن باید بین ${controller.selectedPoultryHatching.value?.managementHatchingAgeRange?.fromWeight} تا ${controller.selectedPoultryHatching.value?.managementHatchingAgeRange?.toWeight} کیلوگرم باشد';
}
return null;
},
filledColor: Colors.white,
keyboardType: TextInputType.number,
inputFormatters: [FirstDigitDecimalFormatter()],
controller: controller.averageWeightKillsController,
),
);
}
Widget saleTypeWidget() {
return Container(
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(8)),
margin: EdgeInsets.symmetric(horizontal: 8.w, vertical: 10.h),
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 4.h),
child: ObxValue((data) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 4,
children: [
Text(
'نوع فروش',
textAlign: TextAlign.center,
style: AppFonts.yekan14Bold.copyWith(color: AppColor.blueNormal),
),
RadioGroup(
groupValue: data.value ? 1 : 0,
onChanged: (value) {
controller.changeSaleType();
},
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Radio(value: 0, activeColor: AppColor.blueNormal),
Text('دولتی', style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDark)),
Spacer(),
Radio(value: 1, activeColor: AppColor.blueNormal),
Text('آزاد', style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDark)),
],
),
),
],
);
}, controller.isFreeSale),
);
}
Widget priceWidget() {
return ObxValue((data) {
if (!data.value) {
return Container(
height: 40.h,
margin: EdgeInsets.symmetric(horizontal: 8.w),
decoration: BoxDecoration(
color: AppColor.greenLight,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 1.w, color: AppColor.whiteNormalHover),
),
padding: EdgeInsets.symmetric(horizontal: 11.w, vertical: 4.h),
child: Row(
spacing: 8,
children: [
Text('قیمت مصوب', style: AppFonts.yekan14.copyWith(color: AppColor.textColor)),
Spacer(),
ObxValue((data) {
return Text(
' ${data.value.separatedByCommaFa} ریال',
textAlign: TextAlign.center,
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
);
}, controller.generatedApprovedPrice),
],
),
);
} else {
return Padding(
padding: EdgeInsets.fromLTRB(8.w, 0, 8.w, 0),
child: RTextField(
label: 'قیمت پیشنهادی (ریال)',
validator: (value) {
if (value == null || value.isEmpty) {
return 'قیمت پیشنهادی را وارد کنید';
}
final price = double.tryParse(value.replaceAll(',', ''));
if (price == null || price <= 0) {
return 'قیمت پیشنهادی باید عددی بزرگتر از صفر باشد';
}
return null;
},
filled: true,
borderColor: AppColor.whiteNormalHover,
filledColor: AppColor.accent1,
keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly, SeparatorInputFormatter()],
controller: controller.priceFreeSaleController,
),
);
}
}, controller.isFreeSale);
}
Widget buyerListWidget() {
return Padding(
padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 8.w),
child: ObxValue((data) {
return Visibility(
visible: data.value?.provinceAllowChooseKillHouse?.allowState ?? false,
child: ObxValue((data) {
return ResourceOverlayDropdown<KillHousePoultry>(
items: data.value,
background: Colors.white,
onChanged: (value) {
controller.selectedKillHouse.value = value;
},
selectedItem: controller.selectedKillHouse.value,
itemBuilder: (item) => Text(buildKillHouseLabel(item)),
labelBuilder: (item) => Text(buildKillHouseLabel(item)),
);
}, controller.killHouseList),
);
}, controller.selectedKillRequestPoultry),
);
}
String buildKillHouseLabel(KillHousePoultry? item) {
if (item == null) {
return 'خریدار/ظرفیت باقیمانده';
} else {
return '${item.name} / ${item.quantitySum} قطعه ';
}
}
Widget slaughterhouseSelectedWidget() {
return Container(
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(8)),
margin: EdgeInsets.symmetric(horizontal: 8.w),
padding: EdgeInsets.symmetric(horizontal: 8.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 8.h),
Text('عملیات کشتار', style: AppFonts.yekan14Bold.copyWith(color: AppColor.blueNormal)),
SizedBox(height: 8.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ObxValue((data) {
return Visibility(
visible: data.value?.permission ?? false,
child: ObxValue(
(data) => TitleCheckBox(
title: ' انجماد',
onChanged: (_) {
controller.isFreezedSelected.value = !controller.isFreezedSelected.value;
},
isSelected: data.value,
),
controller.isFreezedSelected,
),
);
}, controller.sellForFreezing),
ObxValue((data) {
return TitleCheckBox(
title: 'پنل معاملات',
onChanged: (_) {
controller.isMarketSelected.value = !controller.isMarketSelected.value;
},
isSelected: data.value,
);
}, controller.isMarketSelected),
ObxValue((data) {
return Visibility(
visible: data.value?.allow ?? false,
child: ObxValue((data) {
return TitleCheckBox(
title: 'صادرات',
isSelected: data.value,
onChanged: (_) {
controller.isExportSelected.value = !controller.isExportSelected.value;
},
);
}, controller.isExportSelected),
);
}, controller.poultryExport),
],
),
SizedBox(height: 8.h),
],
),
);
}
Widget submitButtonWidget() {
return ObxValue((data) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 12.h),
child: RElevated(
enabled: data.value,
height: 45.h,
isFullWidth: true,
disabledBackgroundColor: AppColor.greenDarkHover,
backgroundColor: AppColor.greenNormal,
textStyle: AppFonts.yekan16Bold.copyWith(color: Colors.white),
onPressed: () {
controller.submitKillRegistration();
},
text: 'ثبت کشتار',
),
);
}, controller.isOnSubmitLoading);
}
}

View File

@@ -0,0 +1,159 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.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<PoultryScienceReport>>> submitInspectionList =
Resource<PaginationModel<PoultryScienceReport>>.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<PoultryScienceReport>>.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<PoultryScienceReport>>.empty();
} else {
submitInspectionList.value =
Resource<PaginationModel<PoultryScienceReport>>.success(
PaginationModel<PoultryScienceReport>(
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(PoultryScienceReport item) {
final status = item.reportInformation?.inspectionStatus ?? item.state;
if (status == null || status.isEmpty) {
return 'در حال بررسی';
}
return status;
}
Color getStatusColor(PoultryScienceReport item) {
final status = item.reportInformation?.inspectionStatus ?? item.state;
if (status == null || status.isEmpty) {
return AppColor.yellowNormal;
}
// می‌توانید منطق رنگ را بر اساس inspectionStatus تنظیم کنید
return AppColor.greenNormal;
}
}

View File

@@ -0,0 +1,46 @@
import 'package:rasadyar_chicken/features/poultry_science/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/presentation/routes/routes.dart';
import 'package:rasadyar_core/core.dart';
class PoultryActionItem {
final String title;
final String route;
final String icon;
PoultryActionItem({
required this.title,
required this.route,
required this.icon,
});
}
class PoultryActionLogic extends GetxController {
RxList<PoultryActionItem> items = [
PoultryActionItem(
title: "بازرسی",
route: PoultryScienceRoutes.inspectionPoultryScience,
icon: Assets.vec.chickenInspectionSvg.path,
),
PoultryActionItem(
title: "ثبت کشتار",
route: PoultryScienceRoutes.genocidePoultryScience,
icon: Assets.vec.registerKillSvg.path,
),
PoultryActionItem(
title: "فارم ها",
route: PoultryScienceRoutes.farmPoultryScience,
icon: Assets.vec.farmsSvg.path,
),
PoultryActionItem(
title: "جوجه ریزی فعال",
route: PoultryScienceRoutes.activeHatchingPoultryScience,
icon: Assets.vec.activeFramSvg.path,
),
PoultryActionItem(
title: "بازرسی مزارع طیور",
route: PoultryScienceRoutes.newInspectionPoultryScience,
icon: Assets.vec.activeFramSvg.path,
),
].obs;
}

View File

@@ -0,0 +1,46 @@
import 'package:flutter/material.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 PoultryActionPage extends GetView<PoultryActionLogic> {
PoultryActionPage({super.key});
@override
Widget build(BuildContext context) {
return ChickenBasePage(
isBase: true,
hasNews: true,
hasNotification: true,
child: gridWidget(),
);
}
Widget gridWidget() {
return ObxValue((data) {
return GridView.builder(
physics: BouncingScrollPhysics(),
padding: EdgeInsets.symmetric(vertical: 18.h, horizontal: 32.w),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 24.h,
crossAxisSpacing: 24.w,
),
itemCount: data.length,
hitTestBehavior: HitTestBehavior.opaque,
itemBuilder: (BuildContext context, int index) {
var item = data[index];
return GlassMorphismCardIcon(
title: item.title,
vecIcon: item.icon,
onTap: () async {
Get.toNamed(item.route, id: poultryScienceActionKey);
},
);
},
);
}, controller.items);
}
}

View File

@@ -0,0 +1,110 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:rasadyar_chicken/data/di/chicken_di.dart';
<<<<<<<< HEAD:packages/chicken/lib/features/poultry_science/root/logic.dart
import 'package:rasadyar_chicken/data/repositories/poultry_science/poultry_science_repository.dart';
import 'package:rasadyar_chicken/features/common/profile/view.dart';
import 'package:rasadyar_chicken/features/poultry_science/home/view.dart';
========
import 'package:rasadyar_chicken/features/poultry_science/data/repositories/poultry_science_repository.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/home/view.dart';
import 'package:rasadyar_chicken/features/common/presentation/page/profile/view.dart';
>>>>>>>> develop:packages/chicken/lib/features/poultry_science/presentation/pages/root/logic.dart
import 'package:rasadyar_chicken/presentation/routes/pages.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/presentation/utils/nested_keys_utils.dart';
import 'package:rasadyar_chicken/presentation/utils/utils.dart';
import 'package:rasadyar_core/core.dart';
enum ErrorLocationType { serviceDisabled, permissionDenied, none }
class PoultryScienceRootLogic extends GetxController {
var tokenService = Get.find<TokenStorageService>();
late PoultryScienceRepository poultryRepository;
RxList<ErrorLocationType> errorLocationType = RxList();
RxMap<int, dynamic> homeExpandedList = RxMap();
DateTime? _lastBackPressed;
RxInt currentPage = 1.obs;
final pages = [
Navigator(
key: Get.nestedKey(poultryScienceActionKey),
onGenerateRoute: (settings) {
final page = ChickenPages.pages.firstWhere(
(e) => e.name == settings.name,
orElse: () => ChickenPages.pages.firstWhere(
(e) => e.name == PoultryScienceRoutes.actionPoultryScience,
),
);
return buildRouteFromGetPage(page);
},
),
PoultryScienceHomePage(),
ProfilePage(),
];
@override
void onInit() {
super.onInit();
poultryRepository = diChicken.get<PoultryScienceRepository>();
}
void toggleExpanded(int index) {
if (homeExpandedList.keys.contains(index)) {
homeExpandedList.remove(index);
} else {
homeExpandedList[index] = false;
}
}
void rootErrorHandler(DioException error) {
handleGeneric(error, () {
tokenService.deleteModuleTokens(Module.chicken);
});
}
void changePage(int index) {
currentPage.value = index;
}
int getNestedKey() {
switch (currentPage.value) {
case 0:
return poultryFirstKey;
case 1:
return poultrySecondKey;
case 2:
return poultryThirdKey;
default:
return poultryFirstKey;
}
}
void popBackTaped() async {
final nestedKeyId = getNestedKey();
GlobalKey<NavigatorState>? currentNestedKey = Get.nestedKey(nestedKeyId);
if (currentNestedKey?.currentState?.canPop() == true) {
Get.back(id: nestedKeyId);
} else {
final now = DateTime.now();
if (_lastBackPressed == null ||
now.difference(_lastBackPressed!) > Duration(seconds: 2)) {
_lastBackPressed = now;
Get.snackbar(
'خروج از برنامه',
'برای خروج دوباره بازگشت را بزنید',
snackPosition: SnackPosition.TOP,
duration: Duration(seconds: 2),
backgroundColor: AppColor.warning,
);
} else {
await SystemNavigator.pop();
}
}
}
}

View File

@@ -0,0 +1,71 @@
import 'package:flutter/material.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 PoultryScienceRootPage extends GetView<PoultryScienceRootLogic> {
const PoultryScienceRootPage({super.key});
@override
Widget build(BuildContext context) {
return ChickenBasePage(
isFullScreen: true,
onPopScopTaped: controller.popBackTaped,
child: ObxValue((data) {
return Stack(
children: [
IndexedStack(children: controller.pages, index: data.value),
Positioned(
right: 0,
left: 0,
bottom: 0,
child: RBottomNavigation(
mainAxisAlignment: MainAxisAlignment.spaceAround,
items: [
RBottomNavigationItem(
label: 'عملیات',
icon: Assets.vec.settingSvg.path,
isSelected: controller.currentPage.value == 0,
onTap: () {
Get.nestedKey(
poultryScienceActionKey,
)?.currentState?.popUntil((route) => route.isFirst);
controller.changePage(0);
},
),
RBottomNavigationItem(
label: 'خانه',
icon: Assets.vec.homeSvg.path,
isSelected: controller.currentPage.value == 1,
onTap: () {
Get.nestedKey(
poultryScienceActionKey,
)?.currentState?.popUntil((route) => route.isFirst);
controller.changePage(1);
},
),
RBottomNavigationItem(
label: 'پروفایل',
icon: Assets.vec.profileCircleSvg.path,
isSelected: controller.currentPage.value == 2,
onTap: () {
Get.nestedKey(
poultryScienceActionKey,
)?.currentState?.popUntil((route) => route.isFirst);
controller.changePage(2);
},
),
],
),
),
],
);
}, controller.currentPage),
);
}
}

View File

@@ -0,0 +1,127 @@
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/active_hatching/logic.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/active_hatching/view.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/farm/logic.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/farm/view.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/genocide/logic.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/genocide/view.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/home/logic.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/inspection/logic.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/inspection/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';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/poultry_action/view.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/root/logic.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/pages/root/view.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/create_inspection_bottom_sheet_logic.dart';
import 'package:rasadyar_chicken/presentation/routes/global_binding.dart';
import 'package:rasadyar_core/core.dart';
class PoultrySciencePages {
PoultrySciencePages._();
static List<GetPage> get pages => [
GetPage(
name: PoultryScienceRoutes.initPoultryScience,
page: () => PoultryScienceRootPage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => PoultryScienceRootLogic());
Get.lazyPut(() => PoultryScienceHomeLogic());
Get.lazyPut(() => PoultryActionLogic());
}),
],
),
GetPage(
name: PoultryScienceRoutes.inspectionPoultryScience,
page: () => InspectionPoultrySciencePage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => InspectionPoultryScienceLogic());
}),
],
),
GetPage(
name: PoultryScienceRoutes.newInspectionPoultryScience,
page: () => NewInspectionPoultrySciencePage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => NewInspectionPoultryScienceLogic());
}),
],
),
GetPage(
name: PoultryScienceRoutes.actionPoultryScience,
page: () => PoultryActionPage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => PoultryActionLogic());
}),
],
),
GetPage(
name: PoultryScienceRoutes.farmPoultryScience,
page: () => FarmPage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => FarmLogic());
Get.lazyPut(() => PoultryScienceHomeLogic());
Get.lazyPut(() => PoultryScienceRootLogic());
}),
],
),
GetPage(
name: PoultryScienceRoutes.activeHatchingPoultryScience,
page: () => ActiveHatchingPage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => ActiveHatchingLogic());
Get.lazyPut(() => CreateInspectionBottomSheetLogic(), fenix: true);
Get.lazyPut(() => PoultryScienceRootLogic());
}),
],
),
GetPage(
name: PoultryScienceRoutes.genocidePoultryScience,
page: () => GenocidePage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => GenocideLogic());
Get.lazyPut(() => PoultryScienceRootLogic());
Get.lazyPut(() => KillingRegistrationLogic(), fenix: true);
}),
],
),
GetPage(
name: PoultryScienceRoutes.killingRegistrationPoultryScience,
page: () => KillingRegistrationPage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => KillingRegistrationLogic());
Get.lazyPut(() => GenocideLogic());
Get.lazyPut(() => PoultryScienceRootLogic());
}),
],
),
];
}

View File

@@ -0,0 +1,14 @@
sealed class PoultryScienceRoutes {
PoultryScienceRoutes._();
static const _base = '/chicken/poultryScience';
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';
static const killingRegistrationPoultryScience =
'$genocidePoultryScience/KillingRegistration';
}

View File

@@ -0,0 +1,60 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/create_inspection_bottom_sheet_logic.dart';
import 'package:rasadyar_core/core.dart';
Widget farmInfoWidget({
required CreateInspectionBottomSheetLogic controller,
required String title,
required Widget child,
EdgeInsets? padding,
}) {
return Stack(
clipBehavior: Clip.none,
children: [
Positioned.fill(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 0.50, color: AppColor.mediumGrey),
),
padding:
padding ?? EdgeInsets.symmetric(horizontal: 12.w, vertical: 11.h),
child: child,
),
),
Positioned(
top: -17,
right: 7,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 5.h),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 0.50, color: AppColor.mediumGrey),
),
child: Text(
title,
style: AppFonts.yekan14.copyWith(color: AppColor.iconColor),
),
),
),
],
);
}
Widget cardInfo({required Widget child, EdgeInsets? padding}) {
return Container(
width: Get.width,
padding: padding ?? EdgeInsets.symmetric(horizontal: 12.w, vertical: 14.h),
decoration: BoxDecoration(
color: AppColor.bgLight,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 1, color: AppColor.blackLight),
),
child: child,
);
}

View File

@@ -0,0 +1,302 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/create_inspection_bottom_sheet_logic.dart';
import 'package:rasadyar_core/core.dart';
import 'step1_page.dart';
import 'step2_page.dart';
import 'step3_page.dart';
import 'step4_page.dart';
import 'step5_page.dart';
class CreateInspectionBottomSheet
extends GetWidget<CreateInspectionBottomSheetLogic> {
CreateInspectionBottomSheet({super.key});
List<Widget> get pages => [
step1Page(controller),
step2Page(controller),
step3Page(controller),
step4Page(controller),
step5Page(controller),
];
@override
Widget build(BuildContext context) {
return BaseBottomSheet(
height: Get.height,
rootChild: Column(
children: [
ObxValue((data) {
return stepper(activeStep: data.value);
}, controller.activeStepperIndex),
Expanded(
child: PageView.builder(
physics: const NeverScrollableScrollPhysics(),
reverse: true,
controller: controller.pageController,
itemBuilder: (context, index) => pages[index],
),
),
ObxValue((data) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
spacing: 16,
children: [
Expanded(
child: ObxValue((data) {
return RElevated(
height: 40.h,
enabled:
data.value && !controller.isUploadingImages.value,
backgroundColor: AppColor.greenNormal,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
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 < 4)
? 'ادامه'
: 'ثبت',
),
],
),
onPressed: () {
if (controller.activeStepperIndex.value < 4) {
controller.activeStepperIndex.value++;
} else {
controller.submitInspection();
}
},
);
}, controller.nextStepButtonEnabled),
),
Expanded(
child: ROutlinedElevated(
borderColor: AppColor.error,
height: 40.h,
child: Text('برگشت'),
enabled:
controller.activeStepperIndex.value > 0 &&
!controller.isUploadingImages.value,
onPressed: () {
if (controller.activeStepperIndex.value > 0) {
controller.activeStepperIndex.value--;
}
},
),
),
],
),
);
}, 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();
}),
],
),
);
}
}
class stepper extends StatelessWidget {
const stepper({super.key, required this.activeStep});
final int activeStep;
@override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.ltr,
child: SizedBox(
height: 24,
width: Get.width,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: activeStep >= 0
? AppColor.greenNormalHover
: AppColor.whiteNormalActive,
shape: BoxShape.circle,
),
width: 24.w,
height: 24.h,
child: Text(
'1',
textAlign: TextAlign.center,
style: AppFonts.yekan16.copyWith(
color: activeStep >= 0 ? Colors.white : AppColor.iconColor,
),
),
),
Expanded(
child: Divider(
color: activeStep >= 1
? AppColor.greenNormalHover
: AppColor.whiteNormalActive,
thickness: 8,
),
),
Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: activeStep >= 1
? AppColor.greenNormalHover
: AppColor.whiteNormalActive,
shape: BoxShape.circle,
),
width: 24.w,
height: 24.h,
child: Text(
'2',
textAlign: TextAlign.center,
style: AppFonts.yekan16.copyWith(
color: activeStep >= 1 ? Colors.white : AppColor.iconColor,
),
),
),
Expanded(
child: Divider(
color: activeStep >= 2
? AppColor.greenNormalHover
: AppColor.whiteNormalActive,
thickness: 8,
),
),
Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: activeStep >= 2
? AppColor.greenNormalHover
: AppColor.whiteNormalActive,
shape: BoxShape.circle,
),
width: 24.w,
height: 24.h,
child: Text(
'3',
textAlign: TextAlign.center,
style: AppFonts.yekan16.copyWith(
color: activeStep >= 2 ? Colors.white : AppColor.iconColor,
),
),
),
Expanded(
child: Divider(
color: activeStep >= 3
? AppColor.greenNormalHover
: AppColor.whiteNormalActive,
thickness: 8,
),
),
Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: activeStep >= 3
? AppColor.greenNormalHover
: AppColor.whiteNormalActive,
shape: BoxShape.circle,
),
width: 24.w,
height: 24.h,
child: Text(
'4',
textAlign: TextAlign.center,
style: AppFonts.yekan16.copyWith(
color: activeStep >= 3 ? Colors.white : AppColor.iconColor,
),
),
),
Expanded(
child: Divider(
color: activeStep >= 4
? AppColor.greenNormalHover
: AppColor.whiteNormalActive,
thickness: 8,
),
),
Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: activeStep >= 4
? AppColor.greenNormalHover
: AppColor.whiteNormalActive,
shape: BoxShape.circle,
),
width: 24.w,
height: 24.h,
child: Text(
'5',
textAlign: TextAlign.center,
style: AppFonts.yekan16.copyWith(
color: activeStep >= 3 ? Colors.white : AppColor.iconColor,
),
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,985 @@
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';
class CreateInspectionBottomSheetLogic extends GetxController
with GetSingleTickerProviderStateMixin {
HatchingModel? hatchingModel;
PoultryScienceRepository repository = diChicken
.get<PoultryScienceRepository>();
TokenStorageService tokenStorageService = Get.find<TokenStorageService>();
var gService = Get.find<GService>();
RxInt selectedSegmentIndex = 0.obs;
RxInt expandedIndex = RxInt(-1);
late TabController tabController;
RxInt selectedTabIndex = 0.obs;
RxInt activeStepperIndex = 0.obs;
PageController pageController = PageController(initialPage: 0);
RxBool nextStepButtonEnabled = false.obs;
RxInt casualtiesInformationHeight = 315.obs;
SubmitInspectionResponse? submitInspectionResponse;
// Upload states
RxBool isUploadingImages = false.obs;
RxString uploadStatusMessage = ''.obs;
RxDouble uploadProgress = 0.0.obs;
//step1
TextEditingController unitNameController = TextEditingController();
TextEditingController breedingUniqueIdController = TextEditingController();
TextEditingController healthLicenseController = TextEditingController();
TextEditingController tenantStatusController = TextEditingController();
TextEditingController tenantNameController = TextEditingController();
TextEditingController tenantNationalIdController = TextEditingController();
TextEditingController tenantPhoneNumberController = TextEditingController();
TextEditingController ownerNameController = TextEditingController();
TextEditingController ownerNationalCodeController = TextEditingController();
TextEditingController ownerPhoneNumberController = TextEditingController();
TextEditingController totalCapacityController = TextEditingController();
//--------------------------------
TextEditingController hatchingDateController = TextEditingController();
TextEditingController visitDateController = TextEditingController();
TextEditingController hatchingCountController = TextEditingController();
TextEditingController hatchingAverageWeightController =
TextEditingController();
TextEditingController hatchingBreedController = TextEditingController();
//step2
Resource<List<String>> causeOfUnusualCasualtiesList = Resource.success([
'بیماری',
'قطعی برق',
'استرس گرمایی',
'مشکلات دان',
'کیفیت جوجه',
'سایر (شرح…)',
]);
RxnString causeOfUnusualCasualties = RxnString();
RxBool isOthercauseOfUnusualCasualties = false.obs;
TextEditingController otherCauseOfUnusualCasualtiesController =
TextEditingController();
Resource<List<String>> typeOfDiseaseList = Resource.success([
'آنفلوانزا',
'نیوکاسل',
'IB',
'عفونت‌های باکتریایی',
'مشکلات گوارشی',
'سایر (شرح)',
]);
RxnString typeOfDisease = RxnString();
RxBool isOtherTypeOfDiseaseSelected = false.obs;
TextEditingController otherTypeOfDiseaseController = TextEditingController();
RxList<String> pultryImagesUrls = RxList<String>();
RImagePickerController pultryImagesController = RImagePickerController();
RxList<XFile> pultryImages = RxList<XFile>();
TextEditingController hatchingTemperatureController = TextEditingController();
TextEditingController waterHardnessController = TextEditingController();
TextEditingController normalLossesController = TextEditingController();
TextEditingController abnormalLossesController = TextEditingController();
TextEditingController sourceOfHatchingController = TextEditingController();
RxBool samplingDone = false.obs;
TextEditingController technicalHealthOfficerNameController =
TextEditingController();
TextEditingController technicalEngineeringOfficerNameController =
TextEditingController();
RxInt sanitaryConditionOfTheHallIndex = (-1).obs;
RxInt ventilationStatusIndex = (-1).obs;
RxnString sanitaryConditionOfTheHall = RxnString();
RxnString ventilationStatus = RxnString();
RxInt beddingStatusIndex = (-1).obs;
RxnString beddingStatus = RxnString();
RxInt waterQualityIndex = (-1).obs;
RxnString waterQuality = RxnString();
RxInt fuelTypeIndex = (-1).obs;
RxInt sampleTypeIndex = (-1).obs;
RxnString sampleType = RxnString();
//step3
Resource<List<String>> inputStatusList = Resource.success([
'وابسته',
'مستقل',
]);
RxnString inputStatus = RxnString();
TextEditingController companyNameController = TextEditingController();
TextEditingController trackingCodeController = TextEditingController();
Resource<List<String>> typeOfGrainList = Resource.success([
'آردی',
'پلت',
'کرامبل و اکسترود ',
'مش ',
'پوره و وال',
]);
RxnString typeOfGrain = RxnString();
TextEditingController inputInventoryInWarehouseController =
TextEditingController();
TextEditingController inputInventoryUntilVisitController =
TextEditingController();
TextEditingController generatorTypeController = TextEditingController();
TextEditingController generatorModelController = TextEditingController();
TextEditingController generatorCountController = TextEditingController();
TextEditingController generatorCapacityController = TextEditingController();
TextEditingController emergencyFuelInventoryController =
TextEditingController();
TextEditingController powerCutDurationController = TextEditingController();
TextEditingController powerCutHourController = TextEditingController();
TextEditingController additionalNotesController = TextEditingController();
RxnString fuelType = RxnString();
RxBool powerCutHistory = false.obs;
RxInt grainQualityInputIndex = (-1).obs;
RxnString grainQualityInput = RxnString();
RxInt generatorOperatingStatusIndex = (-1).obs;
RxnString generatorOperatingStatus = RxnString();
RxInt workerContractStatusIndex = (-1).obs;
RxnString workerContractStatus = RxnString();
RxInt newBeneficiaryRequestIndex = (-1).obs;
RxBool trainingStatus = false.obs;
RxnString newBeneficiaryRequest = RxnString();
RxBool overdueStatus = false.obs;
RxBool hasFacilities = false.obs;
TextEditingController paymentYearController = TextEditingController();
Rx<Jalali> selectedPaymentYear = Jalali.now().obs;
//step3 - worker fields
TextEditingController employedWorkersCountController =
TextEditingController();
TextEditingController nativeWorkersCountController = TextEditingController();
TextEditingController nonNativeWorkersCountController =
TextEditingController();
TextEditingController activeFacilityController = TextEditingController();
TextEditingController facilityTypeController = TextEditingController();
TextEditingController facilityAmountController = TextEditingController();
//step4
TextEditingController facilityYearController = TextEditingController();
Rx<Jalali> selectedFacilityYear = Jalali.now().obs;
RxInt inspectorConclusionIndex = (-1).obs;
RxnString inspectorConclusion = RxnString();
TextEditingController inspectorConclusionDescriptionController =
TextEditingController();
RxList<String> hallImagesUrls = RxList<String>();
RImagePickerController hallImagesController = RImagePickerController();
RxList<XFile> hallImages = RxList<XFile>();
RxList<String> inputWarehouseImagesUrls = RxList<String>();
RImagePickerController inputWarehouseImagesController =
RImagePickerController();
RxList<XFile> inputWarehouseImages = RxList<XFile>();
RxList<String> lossesImagesUrls = RxList<String>();
RImagePickerController lossesImagesController = RImagePickerController();
RxList<XFile> lossesImages = RxList<XFile>();
//location
Rxn<LatLng> currentLocation = Rxn<LatLng>();
RxBool isLoadingLocation = false.obs;
@override
void onReady() {
super.onReady();
activeStepperIndex.listen((value) {
pageController.animateToPage(
value,
duration: Duration(milliseconds: 300),
curve: Curves.linear,
);
});
setUpPultryImagesListener();
setUpHallImagesListener();
setUpInputWarehouseImagesListener();
setUpLossesImagesListener();
initData();
setUpNextButtonListeners();
}
@override
void 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
pultryImagesController.disposeCameraController();
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() {
// Reset critical values before initializing
currentLocation.value = null;
nextStepButtonEnabled.value = false;
activeStepperIndex.value = 0;
selectedTabIndex.value = 0;
isLoadingLocation.value = false;
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 ?? 'واحد مرغداری نامشخص';
breedingUniqueIdController.text =
hatchingModel?.poultry?.breedingUniqueId ?? 'کد یکتا/شناسه واحد نامشخص';
healthLicenseController.text =
hatchingModel?.poultry?.healthCertificateNumber ??
'پروانه بهداشتی ندارد ';
tenantStatusController.text = (hatchingModel?.poultry?.hasTenant == true)
? 'دارد'
: 'ندارد';
if (hatchingModel?.poultry?.hasTenant == true) {
tenantNameController.text =
hatchingModel?.tenantFullname ?? 'نام مستاجر نامشخص';
tenantNationalIdController.text =
hatchingModel?.tenantNationalCode ?? 'کد ملی مستاجر نامشخص';
tenantPhoneNumberController.text =
hatchingModel?.tenantMobile ?? 'شماره تماس مستاجر نامشخص';
}
ownerNameController.text =
hatchingModel?.poultry?.user?.fullname ?? 'مالک واحد مرغداری نامشخص';
ownerNationalCodeController.text =
hatchingModel?.chainCompany?.user?.nationalId ??
'کد ملی مالک واحد مرغداری نامشخص';
ownerPhoneNumberController.text =
hatchingModel?.poultry?.user?.mobile ??
'شماره تماس مالک واحد مرغداری نامشخص';
totalCapacityController.text =
hatchingModel?.poultry?.totalCapacity?.toString() ??
'ظرفیت اسمی سالن‌ها نامشخص';
hatchingDateController.text =
hatchingModel?.date?.toJalali.formatCompactDate() ??
'تاریخ جوجه ریزی نامشخص';
hatchingCountController.text =
hatchingModel?.quantity?.separatedByComma ?? 'تعداد جوجه ریزی نامشخص';
hatchingBreedController.text =
hatchingModel?.chickenBreed ?? 'نوع نژاد نامشخص';
visitDateController.text = Jalali.now().formatCompactDate();
}
void toggleExpanded(int index) {
expandedIndex.value = expandedIndex.value == index ? -1 : index;
}
void setHatchingModel(HatchingModel hatchingModel) {
// Reset all data before setting new hatching model
resetAllData();
this.hatchingModel = hatchingModel;
initData();
}
void changeSegmentIndex(int index) {
if (index == selectedSegmentIndex.value) {
return;
}
expandedIndex.value = -1;
selectedSegmentIndex.value = index;
}
void changeTab(int index) {
if (index == selectedTabIndex.value) {
return;
}
selectedTabIndex.value = index;
}
void clearForm() {
unitNameController.clear();
activeStepperIndex.value = 0;
selectedTabIndex.value = 0;
}
void setSanitaryConditionOfTheHallIndex(int index, String item) {
sanitaryConditionOfTheHall.value = item;
submitInspectionResponse?.generalConditionHall?.healthStatus = item;
sanitaryConditionOfTheHallIndex.value =
index == sanitaryConditionOfTheHallIndex.value ? -1 : index;
}
void setFuelTypeIndex(int index, String item) {
fuelType.value = item;
fuelTypeIndex.value = index == fuelTypeIndex.value ? -1 : index;
}
void setVentilationStatusIndex(int index, String item) {
ventilationStatus.value = item;
submitInspectionResponse?.generalConditionHall?.ventilationStatus = item;
ventilationStatusIndex.value = index == ventilationStatusIndex.value
? -1
: index;
}
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;
}
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;
}
void setNewBeneficiaryRequestIndex(int index) {
newBeneficiaryRequestIndex.value = index == newBeneficiaryRequestIndex.value
? -1
: index;
}
void setInspectorConclusionIndex(int index, String item) {
inspectorConclusion.value = item;
submitInspectionResponse?.inspectionStatus = item;
inspectorConclusionIndex.value = index == inspectorConclusionIndex.value
? -1
: index;
}
Future<void> getCurrentLocation() async {
isLoadingLocation.value = true;
try {
final hasPermission = await checkLocationPermission(request: true);
if (!hasPermission) {
throw Exception('دسترسی به موقعیت مکانی داده نشده است');
}
// Get current location
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;
currentLocation.value = null;
nextStepButtonEnabled.value = false;
}
}
void setUpNextButtonListeners() {
if (activeStepperIndex.value == 0) {
nextStepButtonEnabled.value = currentLocation.value != null;
} else {
nextStepButtonEnabled.value = false;
}
}
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 +
pultryImagesController.capturedImages.length;
if (totalImages == 0) {
isUploadingImages.value = false;
return true;
}
// آپلود عکس‌های سالن
if (hallImages.isNotEmpty) {
uploadStatusMessage.value = 'در حال آپلود عکس‌های سالن...';
final hallImageFiles = hallImages.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.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.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 (pultryImagesController.capturedImages.isNotEmpty) {
uploadStatusMessage.value = 'در حال آپلود عکس‌های مرغداری...';
final poultryImageFiles = pultryImagesController.capturedImages
.toList();
final poultryUrls = await uploadImageBatch(poultryImageFiles);
if (poultryUrls != null && poultryUrls.isNotEmpty) {
// اگر فیلد جداگانه‌ای برای عکس‌های مرغداری نداریم، به generalConditionHall اضافه می‌کنیم
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(RxList<XFile> images, String imagePath) {
images.removeWhere((element) => element.path == imagePath);
}
void setTypeOfDiseaseIndex(String item) {
if (isOtherTypeOfDiseaseSelected.value && item != 'سایر (شرح)') {
casualtiesInformationHeight.value =
casualtiesInformationHeight.value - 50;
}
typeOfDisease.value = item;
isOtherTypeOfDiseaseSelected.value = item == 'سایر (شرح)';
if (isOtherTypeOfDiseaseSelected.value && item == 'سایر (شرح)') {
casualtiesInformationHeight.value =
casualtiesInformationHeight.value + 50;
}
}
void setCauseOfUnusualCasualtiesIndex(String item) {
if (isOthercauseOfUnusualCasualties.value && item != 'سایر (شرح…)') {
casualtiesInformationHeight.value =
casualtiesInformationHeight.value - 50;
}
causeOfUnusualCasualties.value = item;
isOthercauseOfUnusualCasualties.value = item == 'سایر (شرح…)';
if (item == 'سایر (شرح…)') {
casualtiesInformationHeight.value =
casualtiesInformationHeight.value + 50;
}
}
void setSamplingDone(String value) {
if (samplingDone.value && value != 'انجام شد') {
casualtiesInformationHeight.value =
casualtiesInformationHeight.value - 80;
}
samplingDone.value = value == 'انجام شد';
submitInspectionResponse?.casualties?.samplingDone = samplingDone.value;
if (value == 'انجام شد') {
casualtiesInformationHeight.value =
casualtiesInformationHeight.value + 80;
}
}
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() {
Get.bottomSheet(
modalDatePicker(
initialDate: selectedPaymentYear.value,
onDateSelected: (value) {
selectedPaymentYear.value = value;
paymentYearController.text = value.formatCompactDate();
},
),
);
}
void showFacilityYearDatePicker() {
Get.bottomSheet(
modalDatePicker(
initialDate: selectedFacilityYear.value,
onDateSelected: (value) {
selectedFacilityYear.value = value;
facilityYearController.text = value.formatCompactDate();
Get.back();
},
),
);
}
void removeHallImage(XFile key) {
hallImages.remove(key);
}
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.tryParse(
abnormalLossesController.text.clearComma,
);
submitInspectionResponse?.casualties?.normalLosses = int.tryParse(
normalLossesController.text.clearComma,
);
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.tryParse(
employedWorkersCountController.text.clearComma,
);
submitInspectionResponse?.hr?.numberIndigenous = int.tryParse(
nativeWorkersCountController.text.clearComma,
);
submitInspectionResponse?.hr?.numberNonIndigenous = int.tryParse(
nonNativeWorkersCountController.text.clearComma,
);
submitInspectionResponse?.inspectionNotes =
inspectorConclusionDescriptionController.text;
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,
);
}
void setUpPultryImagesListener() {
pultryImagesController.addListener(() {
if (pultryImagesController.capturedImages.isNotEmpty) {
WidgetsBinding.instance.addPostFrameCallback((_) {
pultryImages.assignAll(pultryImagesController.capturedImages);
});
}
});
}
void setUpHallImagesListener() {
hallImagesController.addListener(() {
if (hallImagesController.capturedImages.isNotEmpty) {
WidgetsBinding.instance.addPostFrameCallback((_) {
hallImages.assignAll(hallImagesController.capturedImages);
});
}
});
}
void setUpInputWarehouseImagesListener() {
inputWarehouseImagesController.addListener(() {
if (inputWarehouseImagesController.capturedImages.isNotEmpty) {
WidgetsBinding.instance.addPostFrameCallback((_) {
inputWarehouseImages.assignAll(
inputWarehouseImagesController.capturedImages,
);
});
}
});
}
void setUpLossesImagesListener() {
lossesImagesController.addListener(() {
if (lossesImagesController.capturedImages.isNotEmpty) {
WidgetsBinding.instance.addPostFrameCallback((_) {
lossesImages.assignAll(lossesImagesController.capturedImages);
});
}
});
}
}

View File

@@ -0,0 +1,249 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/card_info.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/create_inspection_bottom_sheet_logic.dart';
import 'package:rasadyar_core/core.dart';
Widget step1Page(CreateInspectionBottomSheetLogic controller) {
return SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Column(
children: [
SizedBox(height: 25.h),
Container(
height: controller.tenantStatusController.text == 'دارد'
? 605.h
: 470.h,
clipBehavior: Clip.none,
width: Get.width,
child: farmInfoWidget(
controller: controller,
title: 'اطلاعات پایه واحد',
child: basicUnitInformation(controller),
),
),
SizedBox(height: 30.h),
Container(
height: 280.h,
clipBehavior: Clip.none,
width: Get.width,
child: farmInfoWidget(
controller: controller,
title: 'اطلاعات جوجه ریزی',
child: hatchingInformation(controller),
),
),
SizedBox(height: 16.h),
],
),
);
}
Column basicUnitInformation(CreateInspectionBottomSheetLogic controller) {
return Column(
spacing: 10,
children: [
SizedBox(height: 1.h),
RTextField(
controller: controller.unitNameController,
label: 'نام واحد مرغداری',
filled: true,
filledColor: AppColor.bgLight,
readonly: true,
),
RTextField(
controller: controller.breedingUniqueIdController,
label: 'کد یکتا / شناسه واحد',
filled: true,
readonly: true,
filledColor: AppColor.bgLight,
),
RTextField(
controller: controller.healthLicenseController,
label: 'پروانه بهداشتی',
filled: true,
filledColor: AppColor.bgLight,
),
RTextField(
controller: controller.tenantStatusController,
label: 'وضعیت مستاجر',
filled: true,
filledColor: AppColor.bgLight,
),
Visibility(
child: controller.tenantStatusController.text == 'دارد'
? Column(
spacing: 10,
children: [
RTextField(
controller: controller.tenantNameController,
label: 'نام مستاجر',
filled: true,
filledColor: AppColor.bgLight,
),
RTextField(
controller: controller.tenantNationalIdController,
label: 'کد ملی مستاجر',
filled: true,
filledColor: AppColor.bgLight,
),
RTextField(
controller: controller.tenantPhoneNumberController,
label: 'شماره تماس مستاجر',
filled: true,
filledColor: AppColor.bgLight,
),
],
)
: SizedBox.shrink(),
),
RTextField(
controller: controller.ownerNationalCodeController,
label: 'کد ملی بهره‌بردار',
filled: true,
readonly: true,
filledColor: AppColor.bgLight,
),
RTextField(
controller: controller.ownerPhoneNumberController,
label: 'شماره تماس بهره‌بردار',
filled: true,
readonly: true,
filledColor: AppColor.bgLight,
),
RTextField(
controller: controller.totalCapacityController,
label: 'ظرفیت اسمی سالن‌ها',
filled: true,
filledColor: AppColor.bgLight,
),
Obx(
() => RElevated(
isFullWidth: true,
height: 40.h,
enabled: !controller.isLoadingLocation.value,
backgroundColor: AppColor.greenNormal,
onPressed: controller.isLoadingLocation.value
? null
: () => controller.getCurrentLocation(),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 4,
children: [
if (controller.isLoadingLocation.value)
SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
)
else
Icon(Icons.my_location_rounded, color: Colors.white, size: 24),
Text(
controller.currentLocation.value == null
? 'دریافت موقعیت جغرافیایی'
: 'موقعیت جغرافیایی دریافت شد',
style: AppFonts.yekan14.copyWith(color: Colors.white),
),
],
),
),
),
ObxValue((data) {
return Visibility(
visible: data.value != null,
child: Row(
spacing: 8,
children: [
Icon(Icons.gps_fixed, color: AppColor.greenNormal, size: 24),
Text(
'موقعیت جغرافیایی دریافت شد',
style: AppFonts.yekan12.copyWith(color: AppColor.greenNormal),
),
],
),
);
}, controller.currentLocation),
],
);
}
Column hatchingInformation(CreateInspectionBottomSheetLogic controller) {
return Column(
spacing: 10,
children: [
SizedBox(height: 1.h),
RTextField(
controller: controller.hatchingDateController,
label: 'تاریخ جوجه ریزی',
filled: true,
filledColor: AppColor.bgLight,
readonly: true,
),
RTextField(
controller: controller.visitDateController,
label: 'تاریخ بازدید',
filled: true,
filledColor: AppColor.bgLight,
readonly: true,
),
RTextField(
controller: controller.hatchingCountController,
label: 'تعداد جوجه‌ریزی اولیه',
filled: true,
filledColor: AppColor.bgLight,
readonly: true,
),
/* ResourceOverlayDropdown(
items: Resource.success(['سرابی', 'پژدر', 'روتوایلر', 'دیگر']),
itemBuilder: (item) => Text(item),
labelBuilder: (selected) => Text(selected ?? 'نوع نژاد'),
), */
/* RTextField(
controller: controller.unitNameController,
label: 'گرید جوجه',
filled: true,
filledColor: AppColor.bgLight,
readonly: true,
), */
RTextField(
controller: controller.hatchingBreedController,
label: 'نوع نژاد',
filled: true,
filledColor: AppColor.bgLight,
),
RTextField(
controller: controller.hatchingAverageWeightController,
label: 'میانگین وزن جوجه',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
),
],
);
}

View File

@@ -0,0 +1,647 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/card_info.dart';
import 'package:rasadyar_core/core.dart';
import 'create_inspection_bottom_sheet_logic.dart';
Widget step2Page(CreateInspectionBottomSheetLogic controller) {
return SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Column(
children: [
SizedBox(height: 35.h),
Container(
height: 630.h,
clipBehavior: Clip.none,
width: Get.width,
child: farmInfoWidget(
controller: controller,
title: 'وضعیت عمومی سالن',
child: generalConditionOfTheHall(controller),
),
),
SizedBox(height: 30.h),
ObxValue((data) {
return Container(
height: data.value.h,
clipBehavior: Clip.none,
width: Get.width,
child: farmInfoWidget(
controller: controller,
title: 'تلفات',
child: casualtiesInformation(controller),
),
);
}, controller.casualtiesInformationHeight),
SizedBox(height: 24.h),
Container(
height: 130.h,
clipBehavior: Clip.none,
width: Get.width,
child: farmInfoWidget(
controller: controller,
title: 'مسئول فنی ',
child: diseasesAndHealthInformation(controller),
),
),
],
),
);
}
Widget generalConditionOfTheHall(CreateInspectionBottomSheetLogic controller) {
return Column(
spacing: 10,
children: [
SizedBox.shrink(),
cardInfo(
padding: EdgeInsets.fromLTRB(0, 12, 12, 6),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
spacing: 8,
children: [
ObxValue((data) {
return Row(
children: data
.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.path),
fit: BoxFit.cover,
),
),
),
// Delete button
Positioned(
top: 4,
left: 4,
child: GestureDetector(
onTap: () => controller.removeImage(
controller.hallImages,
entry.path,
),
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
],
),
)
.toList(),
);
}, controller.pultryImages),
// Add image button
GestureDetector(
onTap: () async {
Get.to(
() => RImagePicker(
controller: controller.pultryImagesController,
),
fullscreenDialog: true,
transition: Transition.fade,
duration: Duration(milliseconds: 300),
);
},
child: Container(
height: 80.h,
width: 80.w,
margin: EdgeInsets.only(left: 8.h),
padding: EdgeInsets.all(22),
decoration: BoxDecoration(
color: Color(0xFFE9E9E9),
border: Border.all(
width: 1,
color: AppColor.blackLightHover,
),
borderRadius: BorderRadius.circular(8),
),
child: Assets.vec.galleryAddSvg.svg(
width: 36,
height: 36,
),
),
),
],
),
),
Text(
'تعداد موجود فعلی',
style: AppFonts.yekan14.copyWith(color: AppColor.textColorLight),
),
],
),
),
cardInfo(
padding: EdgeInsets.fromLTRB(12, 12, 12, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
Text(
'وضعیت بهداشتی سالن',
style: AppFonts.yekan14Bold.copyWith(color: AppColor.textColor2),
),
ObxValue((data) {
return Row(
spacing: 8,
children: [
Expanded(
child: containerChips(
selectedIndex: data.value,
index: 0,
label: 'عالی',
onTap: (index) => controller
.setSanitaryConditionOfTheHallIndex(index, 'عالی'),
),
),
Expanded(
child: containerChips(
selectedIndex: data.value,
index: 1,
label: 'خوب',
onTap: (index) => controller
.setSanitaryConditionOfTheHallIndex(index, 'خوب'),
),
),
Expanded(
child: containerChips(
selectedIndex: data.value,
index: 2,
label: 'متوسط',
onTap: (index) => controller
.setSanitaryConditionOfTheHallIndex(index, 'متوسط'),
),
),
Expanded(
child: containerChips(
selectedIndex: data.value,
index: 3,
label: 'ضعیف',
onTap: (index) => controller
.setSanitaryConditionOfTheHallIndex(index, 'ضعیف'),
),
),
],
);
}, controller.sanitaryConditionOfTheHallIndex),
],
),
),
cardInfo(
padding: EdgeInsets.fromLTRB(12, 12, 12, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
Text(
'وضعیت تهویه',
style: AppFonts.yekan14Bold.copyWith(color: AppColor.textColor2),
),
ObxValue((data) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 8,
children: [
Expanded(
child: containerChips(
onTap: (index) =>
controller.setVentilationStatusIndex(index, 'عالی'),
selectedIndex: data.value,
index: 0,
label: 'عالی',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setVentilationStatusIndex(index, 'خوب'),
selectedIndex: data.value,
index: 1,
label: 'خوب',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setVentilationStatusIndex(index, 'متوسط'),
selectedIndex: data.value,
index: 2,
label: 'متوسط',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setVentilationStatusIndex(index, 'ضعیف'),
selectedIndex: data.value,
index: 3,
label: 'ضعیف',
),
),
],
);
}, controller.ventilationStatusIndex),
],
),
),
cardInfo(
padding: EdgeInsets.fromLTRB(12, 12, 12, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
Text(
'وضعیت بستر',
style: AppFonts.yekan14Bold.copyWith(color: AppColor.textColor2),
),
ObxValue((data) {
return Row(
spacing: 8,
children: [
Expanded(
child: containerChips(
onTap: (index) =>
controller.setBeddingStatusIndex(index, 'خشک'),
selectedIndex: data.value,
index: 0,
label: 'خشک',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setBeddingStatusIndex(index, 'نیمه‌مرطوب'),
selectedIndex: data.value,
index: 1,
label: 'نیمه‌مرطوب',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setBeddingStatusIndex(index, 'مرطوب'),
selectedIndex: data.value,
index: 2,
label: 'مرطوب',
),
),
],
);
}, controller.beddingStatusIndex),
],
),
),
RTextField(
controller: controller.hatchingTemperatureController,
label: 'دمای سالن',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
minLines: 1,
keyboardType: TextInputType.number,
),
cardInfo(
padding: EdgeInsets.fromLTRB(12, 12, 12, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
Text(
' آب مصرفی',
style: AppFonts.yekan14Bold.copyWith(color: AppColor.textColor2),
),
ObxValue((data) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 10,
children: [
Expanded(
child: containerChips(
onTap: (index) =>
controller.setWaterQualityIndex(index, 'چاه'),
selectedIndex: data.value,
index: 0,
label: 'چاه',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setWaterQualityIndex(index, 'شهری'),
selectedIndex: data.value,
index: 1,
label: 'شهری',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setWaterQualityIndex(index, 'تصفیه‌شده'),
selectedIndex: data.value,
index: 2,
label: 'تصفیه‌شده',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setWaterQualityIndex(index, 'حمل تانکر'),
selectedIndex: data.value,
index: 3,
label: 'حمل تانکر',
),
),
],
);
}, controller.waterQualityIndex),
],
),
),
RTextField(
controller: controller.waterHardnessController,
label: 'درصد سختی آب (PPM)',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
minLines: 1,
keyboardType: TextInputType.numberWithOptions(decimal: true),
),
],
);
}
ChoiceChip formChips({
required int selectedIndex,
required int index,
required String label,
required Function(int) onTap,
}) {
return ChoiceChip(
selectedColor: AppColor.green1Normal,
labelStyle: index == selectedIndex
? AppFonts.yekan14Bold.copyWith(color: Colors.white)
: AppFonts.yekan14.copyWith(color: AppColor.textColor),
surfaceTintColor: Colors.white,
checkmarkColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: index == selectedIndex
? BorderSide.none
: BorderSide(width: 1, color: AppColor.blackLightHover),
),
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
label: Text(label),
selected: index == selectedIndex,
onSelected: (value) => onTap(index),
);
}
Column casualtiesInformation(CreateInspectionBottomSheetLogic controller) {
return Column(
spacing: 10,
children: [
SizedBox(height: 1.h),
RTextField(
controller: controller.normalLossesController,
label: 'تعداد تلفات عادی دوره',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
maxLines: 1,
minLines: 1,
),
RTextField(
controller: controller.abnormalLossesController,
label: 'تلفات غیرعادی',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
maxLines: 1,
minLines: 1,
),
ResourceOverlayDropdown(
items: controller.causeOfUnusualCasualtiesList,
onChanged: (item) => controller.setCauseOfUnusualCasualtiesIndex(item),
itemBuilder: (item) => Text(item),
labelBuilder: (selected) => Text(selected ?? 'علت تلفات غیرعادی'),
),
ObxValue((data) {
return Visibility(
visible: data.value,
child: RTextField(
controller: controller.otherCauseOfUnusualCasualtiesController,
label: 'علت تلفات غیرعادی',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
minLines: 1,
),
);
}, controller.isOthercauseOfUnusualCasualties),
ResourceOverlayDropdown(
items: controller.typeOfDiseaseList,
onChanged: (item) => controller.setTypeOfDiseaseIndex(item),
itemBuilder: (item) => Text(item),
labelBuilder: (selected) =>
Text(selected ?? 'نوع بیماری تشخیص داده‌شده / مشکوک'),
),
ObxValue((data) {
return Visibility(
visible: data.value,
child: RTextField(
controller: controller.otherTypeOfDiseaseController,
label: 'نوع بیماری',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
minLines: 1,
),
);
}, controller.isOtherTypeOfDiseaseSelected),
ResourceOverlayDropdown(
items: Resource.success(['انجام شد', 'انجام نشد']),
onChanged: (item) => controller.setSamplingDone(item),
itemBuilder: (item) => Text(item),
labelBuilder: (selected) => Text(selected ?? 'نمونه‌برداری انجام‌شده'),
),
ObxValue((data) {
return Visibility(
visible: data.value,
child: cardInfo(
padding: EdgeInsets.fromLTRB(12, 12, 12, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
Text(
'نوع نمونه',
style: AppFonts.yekan14Bold.copyWith(
color: AppColor.textColor2,
),
),
ObxValue((data) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 8,
children: [
Expanded(
child: containerChips(
onTap: (index) =>
controller.setSampleTypeIndex(index, 'لاشه'),
selectedIndex: data.value,
index: 0,
label: 'لاشه',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setSampleTypeIndex(index, 'آب'),
selectedIndex: data.value,
index: 1,
label: 'آب',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setSampleTypeIndex(index, 'دانه'),
selectedIndex: data.value,
index: 2,
label: 'دانه',
),
),
],
);
}, controller.sampleTypeIndex),
],
),
),
);
}, controller.samplingDone),
],
);
}
Column diseasesAndHealthInformation(
CreateInspectionBottomSheetLogic controller,
) {
return Column(
spacing: 10,
children: [
SizedBox(height: 1.h),
RTextField(
controller: controller.technicalHealthOfficerNameController,
label: 'نام مسئول فنی بهداشتی',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
),
RTextField(
controller: controller.technicalEngineeringOfficerNameController,
label: 'نام مسئول فنی نظام مهندسی',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
),
],
);
}
GestureDetector containerChips({
required int selectedIndex,
required int index,
required String label,
required Function(int) onTap,
}) {
return GestureDetector(
onTap: () => onTap(index),
child: Container(
height: 24.h,
decoration: BoxDecoration(
color: index == selectedIndex
? AppColor.green1Normal
: AppColor.whiteGreyNormal,
borderRadius: BorderRadius.circular(8),
border: index == selectedIndex
? Border.fromBorderSide(BorderSide.none)
: Border.all(width: 1, color: AppColor.blackLightHover),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 5,
children: [
if (index == selectedIndex)
Icon(Icons.check, color: Colors.white, size: 16),
Text(
label,
style: index == selectedIndex
? AppFonts.yekan14Bold.copyWith(color: Colors.white)
: AppFonts.yekan14.copyWith(color: AppColor.textColor),
),
],
),
),
);
}

View File

@@ -0,0 +1,516 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/card_info.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/step2_page.dart';
import 'package:rasadyar_core/core.dart';
import 'create_inspection_bottom_sheet_logic.dart';
Widget step3Page(CreateInspectionBottomSheetLogic controller) {
return SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Column(
children: [
SizedBox(height: 35.h),
Container(
height: 370.h,
clipBehavior: Clip.none,
width: Get.width,
child: farmInfoWidget(
controller: controller,
title: 'نهاده و خوراک',
child: agriculturalInput(controller),
),
),
SizedBox(height: 30.h),
Container(
height: 625.h,
clipBehavior: Clip.none,
width: Get.width,
child: farmInfoWidget(
controller: controller,
title: 'زیرساخت و انرژی',
child: infrastructureAndEnergy(controller),
),
),
SizedBox(height: 24.h),
Container(
height: 320.h,
clipBehavior: Clip.none,
width: Get.width,
child: farmInfoWidget(
controller: controller,
title: 'نیروی انسانی',
child: humanResources(controller),
),
),
SizedBox(height: 24.h),
Container(
height: 335.h,
clipBehavior: Clip.none,
width: Get.width,
child: farmInfoWidget(
controller: controller,
title: 'تسهیلات و حمایت‌ها',
child: facilitiesAndSupport(controller),
),
),
SizedBox(height: 24.h),
],
),
);
}
Column infrastructureAndEnergy(CreateInspectionBottomSheetLogic controller) {
return Column(
spacing: 10,
children: [
SizedBox(height: 1.h),
RTextField(
controller: controller.generatorTypeController,
label: 'نوع ژنراتور',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
),
RTextField(
controller: controller.generatorModelController,
label: 'مدل ژنراتور',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
),
RTextField(
controller: controller.generatorCountController,
label: 'تعداد ژنراتور',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
keyboardType: TextInputType.number,
),
ResourceOverlayDropdown(
items: Resource.success(['گازوئیل', 'نفت گاز']),
itemBuilder: (item) => Text(item),
labelBuilder: (selected) => Text(selected ?? 'نوع سوخت'),
onChanged: (item) => controller.setFuelType(item),
),
RTextField(
controller: controller.generatorCapacityController,
label: 'ظرفیت (KVA)',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
maxLines: 1,
),
cardInfo(
padding: EdgeInsets.fromLTRB(12, 12, 12, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
Text(
'وضعیت عملکرد ژنراتور',
style: AppFonts.yekan14Bold.copyWith(color: AppColor.textColor2),
),
ObxValue((data) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 10,
children: [
Expanded(
child: containerChips(
onTap: (index) => controller
.setGeneratorOperatingStatusIndex(index, 'سالم'),
selectedIndex: data.value,
index: 0,
label: 'سالم',
),
),
Expanded(
child: containerChips(
onTap: (index) => controller
.setGeneratorOperatingStatusIndex(index, 'نیمه‌سالم'),
selectedIndex: data.value,
index: 1,
label: 'نیمه‌سالم',
),
),
Expanded(
child: containerChips(
onTap: (index) => controller
.setGeneratorOperatingStatusIndex(index, 'معیوب'),
selectedIndex: data.value,
index: 2,
label: 'معیوب',
),
),
],
);
}, controller.generatorOperatingStatusIndex),
],
),
),
RTextField(
controller: controller.emergencyFuelInventoryController,
label: 'میزان موجودی سوخت اضطراری (لیتر)',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
maxLines: 1,
),
ResourceOverlayDropdown(
items: Resource.success(['ندارد', 'دارد']),
onChanged: (item) => controller.setPowerCutHistory(item),
itemBuilder: (item) => Text(item),
labelBuilder: (selected) =>
Text(selected ?? 'سابقه قطعی برق دوره جاری'),
),
RTextField(
controller: controller.powerCutDurationController,
label: 'مدت قطعی',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
maxLines: 1,
),
RTextField(
controller: controller.powerCutHourController,
label: 'ساعت قطعی',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
maxLines: 1,
),
RTextField(
controller: controller.additionalNotesController,
label: 'توضیحات تکمیلی',
filled: true,
maxLines: 5,
minLines: 5,
filledColor: AppColor.bgLight,
),
],
);
}
Column agriculturalInput(CreateInspectionBottomSheetLogic controller) {
return Column(
spacing: 10,
children: [
SizedBox.shrink(),
ResourceOverlayDropdown(
items: controller.inputStatusList,
itemBuilder: (item) => Text(item),
labelBuilder: (selected) => Text(selected ?? 'وضعیت نهاده'),
onChanged: (item) => controller.setInputStatus(item),
),
ObxValue((data) {
if (data.value == 'وابسته') {
return RTextField(
controller: controller.companyNameController,
label: 'نام کارخانه',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
);
} else {
return RTextField(
controller: controller.trackingCodeController,
label: 'شناسه خرید یا کد پیگیری',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
);
}
}, controller.inputStatus),
RTextField(
controller: controller.inputInventoryUntilVisitController,
label: 'موجودی نهاده مصرفی تا روز بازدید (کیلوگرم)',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
maxLines: 1,
),
ResourceOverlayDropdown(
items: controller.typeOfGrainList,
itemBuilder: (item) => Text(item),
labelBuilder: (selected) => Text(selected ?? 'نوع دان'),
onChanged: (item) => controller.setTypeOfGrain(item),
),
RTextField(
controller: controller.inputInventoryInWarehouseController,
label: 'موجودی نهاده موجود در انبار (کیلوگرم)',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
),
cardInfo(
padding: EdgeInsets.fromLTRB(12, 12, 12, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
Text(
'کیفیت دانه',
style: AppFonts.yekan14Bold.copyWith(color: AppColor.textColor2),
),
ObxValue((data) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 10,
children: [
Expanded(
child: containerChips(
onTap: (index) =>
controller.setGrainQualityInput(index, 'متوسط'),
selectedIndex: data.value,
index: 0,
label: 'خوب',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setGrainQualityInput(index, 'خوب'),
selectedIndex: data.value,
index: 1,
label: 'متوسط',
),
),
Expanded(
child: containerChips(
onTap: (index) =>
controller.setGrainQualityInput(index, 'ضعیف'),
selectedIndex: data.value,
index: 2,
label: 'ضعیف',
),
),
],
);
}, controller.grainQualityInputIndex),
],
),
),
],
);
}
Column humanResources(CreateInspectionBottomSheetLogic controller) {
return Column(
spacing: 10,
children: [
SizedBox(height: 1.h),
RTextField(
controller: controller.employedWorkersCountController,
label: 'تعداد افراد شاغل',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
maxLines: 1,
),
RTextField(
controller: controller.nativeWorkersCountController,
label: 'تعداد افراد بومی',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
maxLines: 1,
),
RTextField(
controller: controller.nonNativeWorkersCountController,
label: 'تعداد افراد غیر بومی',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
maxLines: 1,
),
cardInfo(
padding: EdgeInsets.fromLTRB(12, 12, 12, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
Text(
'وضعیت قرارداد کارگران',
style: AppFonts.yekan14Bold.copyWith(color: AppColor.textColor2),
),
ObxValue((data) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 8,
children: [
Expanded(
child: containerChips(
onTap: (index) => controller.setWorkerContractStatusIndex(
index,
'دائم',
),
selectedIndex: data.value,
index: 0,
label: 'دائم',
),
),
Expanded(
child: containerChips(
onTap: (index) => controller.setWorkerContractStatusIndex(
index,
'موقت',
),
selectedIndex: data.value,
index: 1,
label: 'موقت',
),
),
Expanded(
child: containerChips(
onTap: (index) => controller.setWorkerContractStatusIndex(
index,
'روزمزدی',
),
selectedIndex: data.value,
index: 2,
label: 'روزمزدی',
),
),
],
);
}, controller.workerContractStatusIndex),
],
),
),
ResourceOverlayDropdown(
items: Resource.success(['بله', 'خیر']),
onChanged: (item) => controller.setTrainingStatus(item),
itemBuilder: (item) => Text(item),
labelBuilder: (selected) =>
Text(selected ?? 'آموزش‌دیده در حوزه بهداشت و امنیت زیستی'),
),
],
);
}
Column facilitiesAndSupport(CreateInspectionBottomSheetLogic controller) {
return Column(
spacing: 10,
children: [
SizedBox(height: 1.h),
ResourceOverlayDropdown(
items: Resource.success(['دارد', 'ندارد']),
onChanged: (item) => controller.setHasFacilities(item),
itemBuilder: (item) => Text(item),
labelBuilder: (selected) => Text(selected ?? ' تسهیلات دریافتی فعال'),
),
RTextField(
controller: controller.facilityTypeController,
label: 'نوع تسهیلات',
filled: true,
filledColor: AppColor.bgLight,
maxLines: 1,
),
RTextField(
controller: controller.facilityAmountController,
label: 'مبلغ',
filled: true,
filledColor: AppColor.bgLight,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
maxLines: 1,
),
RTextField(
controller: controller.paymentYearController,
label: 'سال دریافت',
filled: true,
readonly: true,
onTap: () => controller.showPaymentYearDatePicker(),
filledColor: AppColor.bgLight,
maxLines: 1,
),
ResourceOverlayDropdown(
items: Resource.success(['دارای معوقه', 'منظم']),
onChanged: (item) => controller.setOverdueStatus(item),
itemBuilder: (item) => Text(item),
labelBuilder: (selected) => Text(selected ?? 'وضعیت بازپرداخت'),
),
ResourceOverlayDropdown(
items: Resource.success(['نهاده', 'تسهیلات', 'واکسن', 'تجهیزات']),
onChanged: (item) => controller.setNewBeneficiaryRequest(item),
itemBuilder: (item) => Text(item),
labelBuilder: (selected) => Text(selected ?? 'درخواست جدید بهره بردار'),
),
],
);
}

View File

@@ -0,0 +1,450 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/card_info.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/step2_page.dart';
import 'package:rasadyar_core/core.dart';
import 'create_inspection_bottom_sheet_logic.dart';
Widget step4Page(CreateInspectionBottomSheetLogic controller) {
return SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Column(
children: [
SizedBox(height: 35.h),
Container(
height: 455.h,
clipBehavior: Clip.none,
width: Get.width,
child: farmInfoWidget(
controller: controller,
title: 'مستندات',
child: documents(controller),
),
),
SizedBox(height: 30.h),
Container(
height: 235.h,
clipBehavior: Clip.none,
width: Get.width,
child: farmInfoWidget(
controller: controller,
title: 'جمع‌بندی بازرس',
child: inspectorConclusion(controller),
),
),
SizedBox(height: 24.h),
],
),
);
}
Column documents(CreateInspectionBottomSheetLogic controller) {
return Column(
spacing: 10,
children: [
cardInfo(
padding: EdgeInsets.fromLTRB(0, 12, 12, 6),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
SizedBox(height: 10.h),
Obx(
() => SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
spacing: 8,
children: [
...controller.hallImages.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.path),
fit: BoxFit.cover,
),
),
),
// Delete button
Positioned(
top: 4,
left: 4,
child: GestureDetector(
onTap: () => controller.removeImage(
controller.hallImages,
entry.path,
),
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: () => Get.to(
() => RImagePicker(
controller: controller.hallImagesController,
),
fullscreenDialog: true,
transition: Transition.fade,
duration: Duration(milliseconds: 300),
),
child: Container(
height: 80.h,
width: 80.w,
padding: EdgeInsets.all(22),
decoration: BoxDecoration(
color: Color(0xFFE9E9E9),
border: Border.all(
width: 1,
color: AppColor.blackLightHover,
),
borderRadius: BorderRadius.circular(8),
),
child: Assets.vec.galleryAddSvg.svg(
width: 36,
height: 36,
),
),
),
],
),
),
),
RichText(
text: TextSpan(
children: [
TextSpan(
text: 'ثبت عکس سالن (حداقل ۳ زاویه) ',
style: AppFonts.yekan14.copyWith(
color: AppColor.textColorLight,
),
),
TextSpan(
text: '*',
style: AppFonts.yekan14.copyWith(color: Colors.red),
),
],
),
),
],
),
),
cardInfo(
padding: EdgeInsets.fromLTRB(0, 12, 12, 6),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
Obx(
() => SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
spacing: 8,
children: [
...controller.inputWarehouseImages.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.path),
fit: BoxFit.cover,
),
),
),
// Delete button
Positioned(
top: 4,
left: 4,
child: GestureDetector(
onTap: () => controller.removeImage(
controller.inputWarehouseImages,
entry.path,
),
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: () => Get.to(
() => RImagePicker(
controller: controller.inputWarehouseImagesController,
),
fullscreenDialog: true,
transition: Transition.fade,
duration: Duration(milliseconds: 300),
),
child: Container(
height: 80.h,
width: 80.w,
padding: EdgeInsets.all(22),
decoration: BoxDecoration(
color: Color(0xFFE9E9E9),
border: Border.all(
width: 1,
color: AppColor.blackLightHover,
),
borderRadius: BorderRadius.circular(8),
),
child: Assets.vec.galleryAddSvg.svg(
width: 36,
height: 36,
),
),
),
],
),
),
),
Text(
'ثبت عکس انبار نهاده‌ها',
style: AppFonts.yekan14.copyWith(color: AppColor.textColorLight),
),
],
),
),
cardInfo(
padding: EdgeInsets.fromLTRB(0, 12, 12, 6),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
Obx(
() => SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
spacing: 8,
children: [
...controller.lossesImages.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.path),
fit: BoxFit.cover,
),
),
),
// Delete button
Positioned(
top: 4,
left: 4,
child: GestureDetector(
onTap: () => controller.removeImage(
controller.lossesImages,
entry.path,
),
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: () => Get.to(
() => RImagePicker(
controller: controller.lossesImagesController,
),
fullscreenDialog: true,
transition: Transition.fade,
duration: Duration(milliseconds: 300),
),
child: Container(
height: 80.h,
width: 80.w,
padding: EdgeInsets.all(22),
decoration: BoxDecoration(
color: Color(0xFFE9E9E9),
border: Border.all(
width: 1,
color: AppColor.blackLightHover,
),
borderRadius: BorderRadius.circular(8),
),
child: Assets.vec.galleryAddSvg.svg(
width: 36,
height: 36,
),
),
),
],
),
),
),
Text(
'ثبت عکس تلفات',
style: AppFonts.yekan14.copyWith(color: AppColor.textColorLight),
),
],
),
),
],
);
}
Column inspectorConclusion(CreateInspectionBottomSheetLogic controller) {
return Column(
spacing: 10,
children: [
SizedBox(height: 1.h),
cardInfo(
padding: EdgeInsets.fromLTRB(12, 12, 12, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 9,
children: [
Text(
'وضعیت کلی واحد',
style: AppFonts.yekan14Bold.copyWith(color: AppColor.textColor2),
),
ObxValue((data) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 8,
children: [
Expanded(
child: containerChips(
onTap: (index) => controller.setInspectorConclusionIndex(
index,
'تایید شده',
),
selectedIndex: data.value,
index: 0,
label: 'تایید شده',
),
),
Expanded(
child: containerChips(
onTap: (index) => controller.setInspectorConclusionIndex(
index,
'نیازمند اصلاح',
),
selectedIndex: data.value,
index: 1,
label: 'نیازمند اصلاح',
),
),
Expanded(
child: containerChips(
onTap: (index) => controller.setInspectorConclusionIndex(
index,
'پرریسک',
),
selectedIndex: data.value,
index: 2,
label: 'پرریسک',
),
),
],
);
}, controller.inspectorConclusionIndex),
],
),
),
RTextField(
height: 100.h,
controller: controller.inspectorConclusionDescriptionController,
hintText: 'توصیه‌ها / اخطارها / اقدامات اصلاحی ...',
hintStyle: AppFonts.yekan14.copyWith(color: AppColor.textColorLight),
maxLines: 5,
minLines: 5,
filled: true,
filledColor: AppColor.bgLight,
),
],
);
}

View File

@@ -0,0 +1,898 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/widgets/submit_inspection_bottom_sheet/create_inspection_bottom_sheet_logic.dart';
import 'package:rasadyar_core/core.dart';
Widget step5Page(CreateInspectionBottomSheetLogic controller) {
return SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: detailsWidget(controller),
);
}
Widget detailsWidget(CreateInspectionBottomSheetLogic controller) {
return Column(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
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),
ObxValue((data) {
return tabBarWidget(
[
'اطلاعات کلی',
'شرایط سالن',
'تلفات',
'کارشناس',
'نهاده',
'زیرساخت',
'نیروی انسانی',
'تسهیلات',
],
controller.selectedTabIndex.value,
(index) => controller.changeTab(index),
);
}, controller.selectedTabIndex),
ObxValue((data) {
switch (data.value) {
case 0:
return generalInfoTable(controller);
case 1:
return generalConditionHallTable(controller);
case 2:
return casualtiesTable(controller);
case 3:
return technicalOfficerTable(controller);
case 4:
return inputStatusTable(controller);
case 5:
return infrastructureEnergyTable(controller);
case 6:
return hrTable(controller);
case 7:
return facilitiesTable(controller);
default:
return generalInfoTable(controller);
}
}, controller.selectedTabIndex),
],
),
],
);
}
Widget generalInfoTable(CreateInspectionBottomSheetLogic controller) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
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: controller.unitNameController.text,
),
rTableRow(
title: 'کد یکتا / شناسه واحد',
value: controller.breedingUniqueIdController.text,
),
rTableRow(
title: 'پروانه بهداشتی',
value: controller.healthLicenseController.text,
),
rTableRow(
title: 'وضعیت مستاجر',
value: controller.tenantStatusController.text,
),
if (controller.hatchingModel?.poultry?.hasTenant == true) ...[
rTableRow(
title: 'نام مستاجر',
value: controller.tenantNameController.text,
),
rTableRow(
title: 'کد ملی مستاجر',
value: controller.tenantNationalIdController.text,
),
rTableRow(
title: 'شماره تماس مستاجر',
value: controller.tenantPhoneNumberController.text,
),
],
rTableRow(
title: 'نام مالک',
value: controller.ownerNameController.text,
),
rTableRow(
title: 'کد ملی مالک',
value: controller.ownerNationalCodeController.text,
),
rTableRow(
title: 'شماره تماس مالک',
value: controller.ownerPhoneNumberController.text,
),
rTableRow(
title: 'ظرفیت اسمی',
value: controller.totalCapacityController.text,
),
rTableRow(
title: 'تاریخ جوجه‌ریزی',
value: controller.hatchingDateController.text,
),
rTableRow(
title: 'تاریخ بازدید',
value: controller.visitDateController.text,
),
rTableRow(
title: 'تعداد جوجه‌ریزی',
value: controller.hatchingCountController.text,
),
rTableRow(
title: 'نژاد',
value: controller.hatchingBreedController.text,
),
if (controller.currentLocation.value != null)
rTableRow(
title: 'موقعیت مکانی',
value:
'${controller.currentLocation.value!.latitude.toStringAsFixed(6)}, ${controller.currentLocation.value!.longitude.toStringAsFixed(6)}',
),
rTableRow(
title: 'وضعیت بازرسی',
value: controller.inspectorConclusion.value ?? '-',
),
if (controller
.inspectorConclusionDescriptionController
.text
.isNotEmpty)
rTableRow(
title: 'یادداشت بازرسی',
value: controller.inspectorConclusionDescriptionController.text,
),
],
),
),
],
);
}
Widget generalConditionHallTable(CreateInspectionBottomSheetLogic controller) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
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: controller.sanitaryConditionOfTheHall.value ?? '-',
),
rTableRow(
title: 'وضعیت تهویه',
value: controller.ventilationStatus.value ?? '-',
),
rTableRow(
title: 'وضعیت بستر',
value: controller.beddingStatus.value ?? '-',
),
rTableRow(
title: 'دما',
value: controller.hatchingTemperatureController.text.isNotEmpty
? controller.hatchingTemperatureController.text
: '-',
),
rTableRow(
title: 'منبع آب آشامیدنی',
value: controller.waterQuality.value ?? '-',
),
rTableRow(
title: 'سختی آب',
value: controller.waterHardnessController.text.isNotEmpty
? controller.waterHardnessController.text
: '-',
),
],
),
),
Obx(() {
if (controller.hallImages.isEmpty) return SizedBox.shrink();
return Column(
children: [
SizedBox(height: 16),
Row(
children: [
Text(
'تصاویر سالن',
style: AppFonts.yekan14Bold.copyWith(
color: AppColor.textColor,
),
),
],
),
SizedBox(height: 10),
_buildLocalImagesList(controller.hallImages),
],
);
}),
],
);
}
Widget casualtiesTable(CreateInspectionBottomSheetLogic controller) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
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: controller.normalLossesController.text.isNotEmpty
? controller.normalLossesController.text
: '-',
),
rTableRow(
title: 'تلفات غیرعادی',
value: controller.abnormalLossesController.text.isNotEmpty
? controller.abnormalLossesController.text
: '-',
),
rTableRow(
title: 'منبع جوجه‌ریزی',
value: controller.sourceOfHatchingController.text.isNotEmpty
? controller.sourceOfHatchingController.text
: '-',
),
rTableRow(
title: 'علت تلفات غیرعادی',
value: controller.isOthercauseOfUnusualCasualties.value
? controller.otherCauseOfUnusualCasualtiesController.text
: (controller.causeOfUnusualCasualties.value ?? '-'),
),
rTableRow(
title: 'نوع بیماری',
value: controller.isOtherTypeOfDiseaseSelected.value
? controller.otherTypeOfDiseaseController.text
: (controller.typeOfDisease.value ?? '-'),
),
rTableRow(
title: 'نمونه‌برداری',
value: controller.samplingDone.value ? 'انجام شده' : 'انجام نشده',
),
if (controller.samplingDone.value)
rTableRow(
title: 'نوع نمونه‌برداری',
value: controller.sampleType.value ?? '-',
),
],
),
),
Obx(() {
if (controller.lossesImages.isEmpty) return SizedBox.shrink();
return Column(
children: [
SizedBox(height: 16),
Row(
children: [
Text(
'تصاویر تلفات',
style: AppFonts.yekan14Bold.copyWith(
color: AppColor.textColor,
),
),
],
),
SizedBox(height: 10),
_buildLocalImagesList(controller.lossesImages),
],
);
}),
],
);
}
Widget technicalOfficerTable(CreateInspectionBottomSheetLogic controller) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
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:
controller
.technicalHealthOfficerNameController
.text
.isNotEmpty
? controller.technicalHealthOfficerNameController.text
: '-',
),
rTableRow(
title: 'کارشناس فنی',
value:
controller
.technicalEngineeringOfficerNameController
.text
.isNotEmpty
? controller.technicalEngineeringOfficerNameController.text
: '-',
),
],
),
),
],
);
}
Widget inputStatusTable(CreateInspectionBottomSheetLogic controller) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
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: controller.inputStatus.value ?? '-',
),
if (controller.inputStatus.value == 'وابسته')
rTableRow(
title: 'نام شرکت',
value: controller.companyNameController.text.isNotEmpty
? controller.companyNameController.text
: '-',
)
else
rTableRow(
title: 'کد پیگیری',
value: controller.trackingCodeController.text.isNotEmpty
? controller.trackingCodeController.text
: '-',
),
rTableRow(
title: 'نوع دانه',
value: controller.typeOfGrain.value ?? '-',
),
rTableRow(
title: 'موجودی در انبار',
value:
controller.inputInventoryInWarehouseController.text.isNotEmpty
? controller.inputInventoryInWarehouseController.text
: '-',
),
rTableRow(
title: 'موجودی تا بازدید',
value:
controller.inputInventoryUntilVisitController.text.isNotEmpty
? controller.inputInventoryUntilVisitController.text
: '-',
),
rTableRow(
title: 'درجه دانه',
value: controller.grainQualityInput.value ?? '-',
),
],
),
),
Obx(() {
if (controller.inputWarehouseImages.isEmpty) return SizedBox.shrink();
return Column(
children: [
SizedBox(height: 16),
Row(
children: [
Text(
'تصاویر انبار نهاده',
style: AppFonts.yekan14Bold.copyWith(
color: AppColor.textColor,
),
),
],
),
SizedBox(height: 10),
_buildLocalImagesList(controller.inputWarehouseImages),
],
);
}),
],
);
}
Widget infrastructureEnergyTable(CreateInspectionBottomSheetLogic controller) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
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: controller.generatorTypeController.text.isNotEmpty
? controller.generatorTypeController.text
: '-',
),
rTableRow(
title: 'مدل ژنراتور',
value: controller.generatorModelController.text.isNotEmpty
? controller.generatorModelController.text
: '-',
),
rTableRow(
title: 'تعداد ژنراتور',
value: controller.generatorCountController.text.isNotEmpty
? controller.generatorCountController.text
: '-',
),
rTableRow(
title: 'ظرفیت ژنراتور',
value: controller.generatorCapacityController.text.isNotEmpty
? controller.generatorCapacityController.text
: '-',
),
rTableRow(
title: 'نوع سوخت',
value: controller.fuelType.value ?? '-',
),
rTableRow(
title: 'عملکرد ژنراتور',
value: controller.generatorOperatingStatus.value ?? '-',
),
rTableRow(
title: 'موجودی سوخت اضطراری',
value: controller.emergencyFuelInventoryController.text.isNotEmpty
? controller.emergencyFuelInventoryController.text
: '-',
),
rTableRow(
title: 'سابقه قطع برق',
value: controller.powerCutHistory.value ? 'دارد' : 'ندارد',
),
if (controller.powerCutHistory.value) ...[
rTableRow(
title: 'مدت قطع برق',
value: controller.powerCutDurationController.text.isNotEmpty
? controller.powerCutDurationController.text
: '-',
),
rTableRow(
title: 'ساعت قطع برق',
value: controller.powerCutHourController.text.isNotEmpty
? controller.powerCutHourController.text
: '-',
),
],
if (controller.additionalNotesController.text.isNotEmpty)
rTableRow(
title: 'یادداشت اضافی',
value: controller.additionalNotesController.text,
),
],
),
),
],
);
}
Widget hrTable(CreateInspectionBottomSheetLogic controller) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
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: controller.employedWorkersCountController.text.isNotEmpty
? controller.employedWorkersCountController.text
: '-',
),
rTableRow(
title: 'تعداد بومی',
value: controller.nativeWorkersCountController.text.isNotEmpty
? controller.nativeWorkersCountController.text
: '-',
),
rTableRow(
title: 'تعداد غیربومی',
value: controller.nonNativeWorkersCountController.text.isNotEmpty
? controller.nonNativeWorkersCountController.text
: '-',
),
rTableRow(
title: 'وضعیت قرارداد',
value: controller.workerContractStatus.value ?? '-',
),
rTableRow(
title: 'آموزش دیده',
value: controller.trainingStatus.value ? 'بله' : 'خیر',
),
],
),
),
],
);
}
Widget facilitiesTable(CreateInspectionBottomSheetLogic controller) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
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: controller.hasFacilities.value ? 'بله' : 'خیر',
),
if (controller.hasFacilities.value) ...[
rTableRow(
title: 'نوع تسهیلات',
value: controller.facilityTypeController.text.isNotEmpty
? controller.facilityTypeController.text
: '-',
),
rTableRow(
title: 'مبلغ',
value: controller.facilityAmountController.text.isNotEmpty
? controller.facilityAmountController.text
: '-',
),
rTableRow(
title: 'تاریخ',
value: controller.facilityYearController.text.isNotEmpty
? controller.facilityYearController.text
: '-',
),
rTableRow(
title: 'وضعیت بازپرداخت',
value: controller.overdueStatus.value
? 'دارای معوقه'
: 'بدون معوقه',
),
],
rTableRow(
title: 'درخواست تسهیلات',
value: controller.newBeneficiaryRequest.value ?? '-',
),
],
),
),
],
);
}
Widget _buildLocalImagesList(RxList<XFile> images) {
return SizedBox(
height: 100.h,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: images.length,
separatorBuilder: (context, index) => SizedBox(width: 10),
itemBuilder: (context, index) => GestureDetector(
onTap: () => _showLocalImageDialog(images, index),
child: Container(
width: 80.w,
height: 80.h,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
image: DecorationImage(
fit: BoxFit.cover,
image: FileImage(File(images[index].path)),
),
),
),
),
),
);
}
void _showLocalImageDialog(List<XFile> images, int initialIndex) {
final pageController = PageController(initialPage: initialIndex);
final currentIndex = initialIndex.obs;
Get.dialog(
Dialog(
backgroundColor: Colors.transparent,
insetPadding: EdgeInsets.zero,
child: Container(
width: Get.width,
height: Get.height,
color: Colors.black,
child: Stack(
children: [
PageView.builder(
controller: pageController,
itemCount: images.length,
onPageChanged: (index) {
currentIndex.value = index;
},
itemBuilder: (context, index) {
return InteractiveViewer(
minScale: 0.5,
maxScale: 4.0,
child: Center(
child: Image.file(
File(images[index].path),
fit: BoxFit.contain,
errorBuilder: (context, error, stackTrace) {
return Center(
child: Icon(
Icons.error,
color: Colors.white,
size: 50,
),
);
},
),
),
);
},
),
Positioned(
top: 40,
right: 16,
child: IconButton(
icon: Icon(Icons.close, color: Colors.white, size: 30),
onPressed: () => Get.back(),
),
),
if (images.length > 1)
Positioned(
bottom: 20,
left: 0,
right: 0,
child: Center(
child: Obx(
() => Container(
padding: EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
decoration: BoxDecoration(
color: Colors.black54,
borderRadius: BorderRadius.circular(20),
),
child: Text(
'${currentIndex.value + 1} / ${images.length}',
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
),
),
),
],
),
),
),
barrierDismissible: true,
);
}
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),
),
),
),
],
);
}
Widget tabBarWidget(
List<String> tabs,
int selectedIndex,
Function(int) onTabSelected,
) {
return SizedBox(
height: 40.h,
width: Get.width,
child: Column(
children: [
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,
),
),
),
),
),
],
),
),
Divider(color: AppColor.blackLightHover, height: 1, thickness: 1),
],
),
);
}