chore: update app version to 1.3.36+32, change build mode to release, and enhance user role handling in chicken feature with new routes and DI setup

This commit is contained in:
2025-12-13 16:30:02 +03:30
parent 0d47710e81
commit afbd72404d
121 changed files with 15161 additions and 16 deletions

View File

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

View File

@@ -1,5 +1,12 @@
import 'package:rasadyar_chicken/features/city_jahad/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/features/poultry_science/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/features/province_inspector/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/features/province_operator/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/features/province_supervisor/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/features/steward/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/features/super_admin/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/features/jahad/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/features/vet_farm/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/presentation/routes/routes.dart';
String getFaUserRole(String? role) {
@@ -90,7 +97,7 @@ Map<String, String?> getFaUserRoleWithOnTap(String? role) {
case "Poultry":
return {"مرغدار": null};
case "ProvinceOperator":
return {"مدیر اجرایی": null};
return {"مدیر اجرایی": ProvinceOperatorRoutes.initProvinceOperator};
case "ProvinceFinancial":
return {"مالی اتحادیه": null};
case "KillHouse":
@@ -98,17 +105,17 @@ Map<String, String?> getFaUserRoleWithOnTap(String? role) {
case "KillHouseVet":
return {"دامپزشک کشتارگاه": null};
case "VetFarm":
return {"دامپزشک فارم": null};
return {"دامپزشک فارم": VetFarmRoutes.initVetFarm};
case "Driver":
return {"راننده": null};
case "ProvinceInspector":
return {"بازرس اتحادیه": null};
return {"بازرس اتحادیه": ProvinceInspectorRoutes.initProvinceInspector};
case "VetSupervisor":
return {"دامپزشک کل": null};
case "Jahad":
return {"جهاد کشاورزی استان": null};
return {"جهاد کشاورزی استان": JahadRoutes.initJahad};
case "CityJahad":
return {"جهاد کشاورزی شهرستان": null};
return {"جهاد کشاورزی شهرستان": CityJahadRoutes.initCityJahad};
case "ProvincialGovernment":
return {"استانداری": null};
case "Guilds":
@@ -124,7 +131,7 @@ Map<String, String?> getFaUserRoleWithOnTap(String? role) {
case "Observatory":
return {"رصدخانه": null};
case "ProvinceSupervisor":
return {"ناظر استان": null};
return {"ناظر استان": ProvinceSupervisorRoutes.initProvinceSupervisor};
case "GuildRoom":
return {"اتاق اصناف": null};
case "PosCompany":
@@ -132,7 +139,7 @@ Map<String, String?> getFaUserRoleWithOnTap(String? role) {
case "LiveStockSupport":
return {"پشتیبانی امور دام": null};
case "SuperAdmin":
return {"ادمین کل": null};
return {"ادمین کل": SuperAdminRoutes.initSuperAdmin};
case "ChainCompany":
return {"شرکت زنجیره": null};
case "AdminX":

View File

@@ -7,6 +7,13 @@ import 'package:rasadyar_chicken/data/repositories/kill_house/kill_house_reposit
import 'package:rasadyar_chicken/data/repositories/kill_house/kill_house_repository_impl.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/di/poultry_science_di.dart';
import 'package:rasadyar_chicken/features/steward/data/di/steward_di.dart';
import 'package:rasadyar_chicken/features/province_operator/data/di/province_operator_di.dart';
import 'package:rasadyar_chicken/features/province_inspector/data/di/province_inspector_di.dart';
import 'package:rasadyar_chicken/features/city_jahad/data/di/city_jahad_di.dart';
import 'package:rasadyar_chicken/features/vet_farm/data/di/vet_farm_di.dart';
import 'package:rasadyar_chicken/features/super_admin/data/di/super_admin_di.dart';
import 'package:rasadyar_chicken/features/province_supervisor/data/di/province_supervisor_di.dart';
import 'package:rasadyar_chicken/features/jahad/data/di/jahad_di.dart';
import 'package:rasadyar_core/core.dart';
GetIt diChicken = GetIt.asNewInstance();
@@ -58,6 +65,27 @@ Future<void> setupChickenDI() async {
// Setup steward feature DI
await setupStewardDI(diChicken, dioRemote);
// Setup province_operator feature DI
await setupProvinceOperatorDI(diChicken, dioRemote);
// Setup province_inspector feature DI
await setupProvinceInspectorDI(diChicken, dioRemote);
// Setup city_jahad feature DI
await setupCityJahadDI(diChicken, dioRemote);
// Setup vet_farm feature DI
await setupVetFarmDI(diChicken, dioRemote);
// Setup super_admin feature DI
await setupSuperAdminDI(diChicken, dioRemote);
// Setup province_supervisor feature DI
await setupProvinceSupervisorDI(diChicken, dioRemote);
// Setup jahad feature DI
await setupJahadDI(diChicken, dioRemote);
//region kill house module DI
diChicken.registerLazySingleton<KillHouseRemoteDataSource>(
() => KillHouseRemoteDataSourceImpl(diChicken.get<DioRemote>()),
@@ -103,10 +131,17 @@ Future<void> newSetupAuthDI(String newUrl) async {
final dioRemote = diChicken.get<DioRemote>();
await dioRemote.init();
// --- common, poultry_science, steward
// --- common, poultry_science, steward, and other features
await setupCommonDI(diChicken, dioRemote);
await setupPoultryScienceDI(diChicken, dioRemote);
await setupStewardDI(diChicken, dioRemote);
await setupProvinceOperatorDI(diChicken, dioRemote);
await setupProvinceInspectorDI(diChicken, dioRemote);
await setupCityJahadDI(diChicken, dioRemote);
await setupVetFarmDI(diChicken, dioRemote);
await setupSuperAdminDI(diChicken, dioRemote);
await setupProvinceSupervisorDI(diChicken, dioRemote);
await setupJahadDI(diChicken, dioRemote);
}
Future<void> reRegister<T extends Object>(T Function() factory) async {

View File

@@ -0,0 +1,3 @@
export 'data/di/city_jahad_di.dart';
export 'presentation/routes/routes.dart';
export 'presentation/routes/pages.dart';

View File

@@ -0,0 +1,15 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_core/core.dart';
abstract class CityJahadRemoteDataSource {
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
});
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
});
}

View File

@@ -0,0 +1,39 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_chicken/features/city_jahad/data/datasources/remote/city_jahad_remote_data_source.dart';
import 'package:rasadyar_core/core.dart';
class CityJahadRemoteDataSourceImpl implements CityJahadRemoteDataSource {
final DioRemote _httpClient;
CityJahadRemoteDataSourceImpl(this._httpClient);
@override
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
}) async {
var res = await _httpClient.get(
'/poultry_science_report/',
headers: {'Authorization': 'Bearer $token'},
queryParameters: queryParameters,
fromJson: (json) => PaginationModel<PoultryScienceReport>.fromJson(
json,
(json) => PoultryScienceReport.fromJson(json as Map<String, dynamic>),
),
);
return res.data;
}
@override
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
}) async {
await _httpClient.post(
'/poultry_science_report/',
headers: {'Authorization': 'Bearer $token'},
data: request.toJson(),
);
}
}

View File

@@ -0,0 +1,36 @@
import 'package:rasadyar_chicken/features/city_jahad/data/datasources/remote/city_jahad_remote_data_source.dart';
import 'package:rasadyar_chicken/features/city_jahad/data/datasources/remote/city_jahad_remote_data_source_impl.dart';
import 'package:rasadyar_chicken/features/city_jahad/data/repositories/city_jahad_repository.dart';
import 'package:rasadyar_chicken/features/city_jahad/data/repositories/city_jahad_repository_impl.dart';
import 'package:rasadyar_core/core.dart';
/// Setup dependency injection for city_jahad feature
Future<void> setupCityJahadDI(GetIt di, DioRemote dioRemote) async {
di.registerLazySingleton<CityJahadRemoteDataSource>(
() => CityJahadRemoteDataSourceImpl(dioRemote),
);
di.registerLazySingleton<CityJahadRepository>(
() => CityJahadRepositoryImpl(di.get<CityJahadRemoteDataSource>()),
);
}
/// Re-register city_jahad dependencies (used when base URL changes)
Future<void> reRegisterCityJahadDI(GetIt di, DioRemote dioRemote) async {
await reRegister(di, () => CityJahadRemoteDataSourceImpl(dioRemote));
await reRegister(
di,
() => CityJahadRepositoryImpl(di.get<CityJahadRemoteDataSource>()),
);
}
/// Helper function to re-register a dependency
Future<void> reRegister<T extends Object>(
GetIt di,
T Function() factory,
) async {
if (di.isRegistered<T>()) {
await di.unregister<T>();
}
di.registerLazySingleton<T>(factory);
}

View File

@@ -0,0 +1,15 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_core/core.dart';
abstract class CityJahadRepository {
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
});
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
});
}

View File

@@ -0,0 +1,30 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_chicken/features/city_jahad/data/datasources/remote/city_jahad_remote_data_source.dart';
import 'package:rasadyar_chicken/features/city_jahad/data/repositories/city_jahad_repository.dart';
import 'package:rasadyar_core/core.dart';
class CityJahadRepositoryImpl implements CityJahadRepository {
final CityJahadRemoteDataSource _remote;
CityJahadRepositoryImpl(this._remote);
@override
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
}) async {
return await _remote.getSubmitInspectionList(
token: token,
queryParameters: queryParameters,
);
}
@override
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
}) async {
return await _remote.submitInspection(token: token, request: request);
}
}

View File

@@ -0,0 +1,95 @@
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/city_jahad/presentation/pages/root/logic.dart';
import 'package:rasadyar_core/core.dart';
class ActiveHatchingLogic extends GetxController {
CityJahadRootLogic rootLogic = Get.find<CityJahadRootLogic>();
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: 'CityJahad',
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/city_jahad/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: cityJahadActionKey,
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/A',
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,29 @@
import 'package:rasadyar_chicken/features/city_jahad/presentation/routes/routes.dart';
import 'package:rasadyar_core/core.dart';
class CityJahadActionItem {
final String title;
final String route;
final String icon;
CityJahadActionItem({
required this.title,
required this.route,
required this.icon,
});
}
class CityJahadHomeLogic extends GetxController {
RxList<CityJahadActionItem> items = [
CityJahadActionItem(
title: "جوجه ریزی فعال",
route: CityJahadRoutes.activeHatchingCityJahad,
icon: Assets.vec.activeFramSvg.path,
),
CityJahadActionItem(
title: "بازرسی مزارع طیور",
route: CityJahadRoutes.newInspectionCityJahad,
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 CityJahadHomePage extends GetView<CityJahadHomeLogic> {
CityJahadHomePage({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: cityJahadActionKey);
},
);
},
);
}, controller.items);
}
}

View File

@@ -0,0 +1,160 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/features/city_jahad/presentation/pages/root/logic.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/presentation/widget/base_page/logic.dart';
import 'package:rasadyar_core/core.dart';
class NewInspectionLogic extends GetxController {
BaseLogic baseLogic = Get.find<BaseLogic>();
Rx<Resource<PaginationModel<PoultryScienceReport>>> submitInspectionList =
Resource<PaginationModel<PoultryScienceReport>>.loading().obs;
CityJahadRootLogic rootLogic = Get.find<CityJahadRootLogic>();
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.cityJahadRepository.getSubmitInspectionList(
token: rootLogic.tokenService.accessToken.value!,
queryParameters: buildQueryParams(
role: 'CityJahad',
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,84 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:rasadyar_chicken/data/di/chicken_di.dart';
import 'package:rasadyar_chicken/features/city_jahad/data/repositories/city_jahad_repository.dart';
import 'package:rasadyar_chicken/features/city_jahad/presentation/routes/pages.dart';
import 'package:rasadyar_chicken/features/city_jahad/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/features/city_jahad/presentation/pages/home/view.dart';
import 'package:rasadyar_chicken/features/common/presentation/page/profile/view.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 CityJahadRootLogic extends GetxController {
var tokenService = Get.find<TokenStorageService>();
late CityJahadRepository cityJahadRepository;
RxList<ErrorLocationType> errorLocationType = RxList();
RxMap<int, dynamic> homeExpandedList = RxMap();
DateTime? _lastBackPressed;
RxInt currentPage = 0.obs;
final pages = [
Navigator(
key: Get.nestedKey(cityJahadActionKey),
onGenerateRoute: (settings) {
final page = CityJahadPages.pages.firstWhere(
(e) => e.name == settings.name,
orElse: () => CityJahadPages.pages.firstWhere(
(e) => e.name == CityJahadRoutes.homeCityJahad,
),
);
return buildRouteFromGetPage(page);
},
),
ProfilePage(),
];
@override
void onInit() {
super.onInit();
cityJahadRepository = diChicken.get<CityJahadRepository>();
}
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;
}
void popBackTaped() async {
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,58 @@
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 CityJahadRootPage extends GetView<CityJahadRootLogic> {
const CityJahadRootPage({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.homeSvg.path,
isSelected: controller.currentPage.value == 0,
onTap: () {
Get.nestedKey(
cityJahadActionKey,
)?.currentState?.popUntil((route) => route.isFirst);
controller.changePage(0);
},
),
RBottomNavigationItem(
label: 'پروفایل',
icon: Assets.vec.profileCircleSvg.path,
isSelected: controller.currentPage.value == 1,
onTap: () {
Get.nestedKey(
cityJahadActionKey,
)?.currentState?.popUntil((route) => route.isFirst);
controller.changePage(1);
},
),
],
),
),
],
);
}, controller.currentPage),
);
}
}

View File

@@ -0,0 +1,66 @@
import 'package:rasadyar_chicken/features/city_jahad/presentation/pages/home/logic.dart';
import 'package:rasadyar_chicken/features/city_jahad/presentation/pages/home/view.dart';
import 'package:rasadyar_chicken/features/city_jahad/presentation/pages/root/logic.dart';
import 'package:rasadyar_chicken/features/city_jahad/presentation/pages/root/view.dart';
import 'package:rasadyar_chicken/features/city_jahad/presentation/pages/active_hatching/logic.dart';
import 'package:rasadyar_chicken/features/city_jahad/presentation/pages/active_hatching/view.dart';
import 'package:rasadyar_chicken/features/city_jahad/presentation/pages/new_inspection/logic.dart';
import 'package:rasadyar_chicken/features/city_jahad/presentation/pages/new_inspection/view.dart';
import 'package:rasadyar_chicken/features/city_jahad/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_chicken/presentation/widget/base_page/logic.dart';
import 'package:rasadyar_core/core.dart';
class CityJahadPages {
CityJahadPages._();
static List<GetPage> get pages => [
GetPage(
name: CityJahadRoutes.initCityJahad,
page: () => CityJahadRootPage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => ChickenBaseLogic(), fenix: true);
Get.lazyPut(() => CityJahadRootLogic());
Get.lazyPut(() => CityJahadHomeLogic());
}),
],
),
GetPage(
name: CityJahadRoutes.homeCityJahad,
page: () => CityJahadHomePage(),
middlewares: [AuthMiddleware()],
binding: BindingsBuilder(() {
Get.put(CityJahadHomeLogic());
Get.lazyPut(() => ChickenBaseLogic());
}),
),
GetPage(
name: CityJahadRoutes.activeHatchingCityJahad,
page: () => ActiveHatchingPage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => ActiveHatchingLogic());
Get.lazyPut(() => CreateInspectionBottomSheetLogic());
}),
],
),
GetPage(
name: CityJahadRoutes.newInspectionCityJahad,
page: () => NewInspectionPage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => NewInspectionLogic());
}),
],
),
];
}

View File

@@ -0,0 +1,10 @@
sealed class CityJahadRoutes {
CityJahadRoutes._();
static const _base = '/chicken/cityJahad';
static const initCityJahad = '$_base/';
static const homeCityJahad = '$_base/home';
static const actionCityJahad = '$_base/action';
static const activeHatchingCityJahad = '$_base/activeHatching';
static const newInspectionCityJahad = '$_base/newInspection';
}

View File

@@ -135,7 +135,18 @@ class AuthLogic extends GetxController with GetTickerProviderStateMixin {
result?.accessToken ?? '',
);
var tmpRoles = result?.role?.where((element) {
final allowedRoles = {'poultryscience', 'steward', 'killhouse'};
final allowedRoles = {
'poultryscience',
'steward',
'killhouse',
'provinceinspector',
'cityjahad',
'jahad',
'vetfarm',
'provincesupervisor',
'superadmin',
};
final lowerElement = element.toString().toLowerCase().trim();
return allowedRoles.contains(lowerElement);
}).toList();

View File

@@ -0,0 +1,15 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_core/core.dart';
abstract class JahadRemoteDataSource {
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
});
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
});
}

View File

@@ -0,0 +1,39 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_chicken/features/jahad/data/datasources/remote/jahad_remote_data_source.dart';
import 'package:rasadyar_core/core.dart';
class JahadRemoteDataSourceImpl implements JahadRemoteDataSource {
final DioRemote _httpClient;
JahadRemoteDataSourceImpl(this._httpClient);
@override
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
}) async {
var res = await _httpClient.get(
'/poultry_science_report/',
headers: {'Authorization': 'Bearer $token'},
queryParameters: queryParameters,
fromJson: (json) => PaginationModel<PoultryScienceReport>.fromJson(
json,
(json) => PoultryScienceReport.fromJson(json as Map<String, dynamic>),
),
);
return res.data;
}
@override
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
}) async {
await _httpClient.post(
'/poultry_science_report/',
headers: {'Authorization': 'Bearer $token'},
data: request.toJson(),
);
}
}

View File

@@ -0,0 +1,36 @@
import 'package:rasadyar_chicken/features/jahad/data/datasources/remote/jahad_remote_data_source.dart';
import 'package:rasadyar_chicken/features/jahad/data/datasources/remote/jahad_remote_data_source_impl.dart';
import 'package:rasadyar_chicken/features/jahad/data/repositories/jahad_repository.dart';
import 'package:rasadyar_chicken/features/jahad/data/repositories/jahad_repository_impl.dart';
import 'package:rasadyar_core/core.dart';
/// Setup dependency injection for jahad feature
Future<void> setupJahadDI(GetIt di, DioRemote dioRemote) async {
di.registerLazySingleton<JahadRemoteDataSource>(
() => JahadRemoteDataSourceImpl(dioRemote),
);
di.registerLazySingleton<JahadRepository>(
() => JahadRepositoryImpl(di.get<JahadRemoteDataSource>()),
);
}
/// Re-register jahad dependencies (used when base URL changes)
Future<void> reRegisterJahadDI(GetIt di, DioRemote dioRemote) async {
await reRegister(di, () => JahadRemoteDataSourceImpl(dioRemote));
await reRegister(
di,
() => JahadRepositoryImpl(di.get<JahadRemoteDataSource>()),
);
}
/// Helper function to re-register a dependency
Future<void> reRegister<T extends Object>(
GetIt di,
T Function() factory,
) async {
if (di.isRegistered<T>()) {
await di.unregister<T>();
}
di.registerLazySingleton<T>(factory);
}

View File

@@ -0,0 +1,15 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_core/core.dart';
abstract class JahadRepository {
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
});
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
});
}

View File

@@ -0,0 +1,30 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_chicken/features/jahad/data/datasources/remote/jahad_remote_data_source.dart';
import 'package:rasadyar_chicken/features/jahad/data/repositories/jahad_repository.dart';
import 'package:rasadyar_core/core.dart';
class JahadRepositoryImpl implements JahadRepository {
final JahadRemoteDataSource _remote;
JahadRepositoryImpl(this._remote);
@override
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
}) async {
return await _remote.getSubmitInspectionList(
token: token,
queryParameters: queryParameters,
);
}
@override
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
}) async {
return await _remote.submitInspection(token: token, request: request);
}
}

View File

@@ -0,0 +1,3 @@
export 'data/di/jahad_di.dart';
export 'presentation/routes/routes.dart';
export 'presentation/routes/pages.dart';

View File

@@ -0,0 +1,95 @@
import 'package:rasadyar_chicken/data/di/chicken_di.dart';
import 'package:rasadyar_chicken/features/jahad/presentation/pages/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/repositories/poultry_science_repository.dart';
import 'package:rasadyar_core/core.dart';
class ActiveHatchingLogic extends GetxController {
JahadRootLogic rootLogic = Get.find<JahadRootLogic>();
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: 'Jahad',
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/jahad/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: jahadActionKey,
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,29 @@
import 'package:rasadyar_chicken/features/jahad/presentation/routes/routes.dart';
import 'package:rasadyar_core/core.dart';
class JahadActionItem {
final String title;
final String route;
final String icon;
JahadActionItem({
required this.title,
required this.route,
required this.icon,
});
}
class JahadHomeLogic extends GetxController {
RxList<JahadActionItem> items = [
JahadActionItem(
title: "جوجه ریزی فعال",
route: JahadRoutes.activeHatchingJahad,
icon: Assets.vec.activeFramSvg.path,
),
JahadActionItem(
title: "بازرسی مزارع طیور",
route: JahadRoutes.newInspectionJahad,
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 JahadHomePage extends GetView<JahadHomeLogic> {
JahadHomePage({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: jahadActionKey);
},
);
},
);
}, controller.items);
}
}

View File

@@ -0,0 +1,159 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/features/jahad/presentation/pages/root/logic.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/presentation/widget/base_page/logic.dart';
import 'package:rasadyar_core/core.dart';
class NewInspectionLogic extends GetxController {
BaseLogic baseLogic = Get.find<BaseLogic>();
Rx<Resource<PaginationModel<PoultryScienceReport>>> submitInspectionList =
Resource<PaginationModel<PoultryScienceReport>>.loading().obs;
JahadRootLogic rootLogic = Get.find<JahadRootLogic>();
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.jahadRepository.getSubmitInspectionList(
token: rootLogic.tokenService.accessToken.value!,
queryParameters: buildQueryParams(
role: 'Jahad',
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;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,84 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:rasadyar_chicken/data/di/chicken_di.dart';
import 'package:rasadyar_chicken/features/jahad/data/repositories/jahad_repository.dart';
import 'package:rasadyar_chicken/features/jahad/presentation/routes/pages.dart';
import 'package:rasadyar_chicken/features/jahad/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/features/jahad/presentation/pages/home/view.dart';
import 'package:rasadyar_chicken/features/common/presentation/page/profile/view.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 JahadRootLogic extends GetxController {
var tokenService = Get.find<TokenStorageService>();
late JahadRepository jahadRepository;
RxList<ErrorLocationType> errorLocationType = RxList();
RxMap<int, dynamic> homeExpandedList = RxMap();
DateTime? _lastBackPressed;
RxInt currentPage = 0.obs;
final pages = [
Navigator(
key: Get.nestedKey(jahadActionKey),
onGenerateRoute: (settings) {
final page = JahadPages.pages.firstWhere(
(e) => e.name == settings.name,
orElse: () => JahadPages.pages.firstWhere(
(e) => e.name == JahadRoutes.homeJahad,
),
);
return buildRouteFromGetPage(page);
},
),
ProfilePage(),
];
@override
void onInit() {
super.onInit();
jahadRepository = diChicken.get<JahadRepository>();
}
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;
}
void popBackTaped() async {
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,57 @@
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 JahadRootPage extends GetView<JahadRootLogic> {
const JahadRootPage({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.homeSvg.path,
isSelected: controller.currentPage.value == 0,
onTap: () {
Get.nestedKey(
jahadActionKey,
)?.currentState?.popUntil((route) => route.isFirst);
controller.changePage(0);
},
),
RBottomNavigationItem(
label: 'پروفایل',
icon: Assets.vec.profileCircleSvg.path,
isSelected: controller.currentPage.value == 1,
onTap: () {
Get.nestedKey(
jahadActionKey,
)?.currentState?.popUntil((route) => route.isFirst);
controller.changePage(1);
},
),
],
),
),
],
);
}, controller.currentPage),
);
}
}

View File

@@ -0,0 +1,64 @@
import 'package:rasadyar_chicken/features/jahad/presentation/pages/home/logic.dart';
import 'package:rasadyar_chicken/features/jahad/presentation/pages/home/view.dart';
import 'package:rasadyar_chicken/features/jahad/presentation/pages/root/logic.dart';
import 'package:rasadyar_chicken/features/jahad/presentation/pages/root/view.dart';
import 'package:rasadyar_chicken/features/jahad/presentation/pages/active_hatching/logic.dart';
import 'package:rasadyar_chicken/features/jahad/presentation/pages/active_hatching/view.dart';
import 'package:rasadyar_chicken/features/jahad/presentation/pages/new_inspection/logic.dart';
import 'package:rasadyar_chicken/features/jahad/presentation/pages/new_inspection/view.dart';
import 'package:rasadyar_chicken/features/jahad/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/presentation/routes/global_binding.dart';
import 'package:rasadyar_chicken/presentation/widget/base_page/logic.dart';
import 'package:rasadyar_core/core.dart';
class JahadPages {
JahadPages._();
static List<GetPage> get pages => [
GetPage(
name: JahadRoutes.initJahad,
page: () => JahadRootPage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => ChickenBaseLogic(), fenix: true);
Get.lazyPut(() => JahadRootLogic());
Get.lazyPut(() => JahadHomeLogic());
}),
],
),
GetPage(
name: JahadRoutes.homeJahad,
page: () => JahadHomePage(),
middlewares: [AuthMiddleware()],
binding: BindingsBuilder(() {
Get.put(JahadHomeLogic());
Get.lazyPut(() => ChickenBaseLogic());
}),
),
GetPage(
name: JahadRoutes.activeHatchingJahad,
page: () => ActiveHatchingPage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => ActiveHatchingLogic());
}),
],
),
GetPage(
name: JahadRoutes.newInspectionJahad,
page: () => NewInspectionPage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => NewInspectionLogic());
}),
],
),
];
}

View File

@@ -0,0 +1,10 @@
sealed class JahadRoutes {
JahadRoutes._();
static const _base = '/chicken/jahad';
static const initJahad = '$_base/';
static const homeJahad = '$_base/home';
static const actionJahad = '$_base/action';
static const activeHatchingJahad = '$_base/activeHatching';
static const newInspectionJahad = '$_base/newInspection';
}

View File

@@ -1,4 +1,16 @@
/// مدل داده‌ای برای ارسال اطلاعات بازرسی مرغداری به سرور
///
/// این کلاس شامل تمام اطلاعات مربوط به بازرسی یک واحد مرغداری است که شامل:
/// - موقعیت جغرافیایی (عرض و طول جغرافیایی)
/// - شناسه جوجه‌ریزی
/// - نقش کاربر
/// - اطلاعات مختلف بازرسی در بخش‌های مختلف
///
/// این مدل برای ارسال درخواست POST به endpoint `/poultry_science_report/` استفاده می‌شود.
class SubmitInspectionResponse {
/// سازنده کلاس SubmitInspectionResponse
///
/// تمام پارامترها اختیاری هستند و می‌توانند null باشند.
SubmitInspectionResponse({
this.lat,
this.log,
@@ -15,20 +27,49 @@ class SubmitInspectionResponse {
this.inspectionNotes,
});
/// عرض جغرافیایی (Latitude) محل بازرسی
String? lat;
/// طول جغرافیایی (Longitude) محل بازرسی
String? log;
/// شناسه یکتای جوجه‌ریزی که بازرسی برای آن انجام می‌شود
int? poultryHatchingId;
/// نقش کاربری که بازرسی را انجام می‌دهد
String? role;
/// اطلاعات مربوط به شرایط عمومی سالن مرغداری
GeneralConditionHall? generalConditionHall;
/// اطلاعات مربوط به تلفات و خسارات
Casualties? casualties;
/// اطلاعات مربوط به کارشناسان فنی
TechnicalOfficer? technicalOfficer;
/// اطلاعات مربوط به وضعیت نهاده‌ها
InputStatus? inputStatus;
/// اطلاعات مربوط به زیرساخت و انرژی
InfrastructureEnergy? infrastructureEnergy;
/// اطلاعات مربوط به نیروی انسانی
Hr? hr;
/// اطلاعات مربوط به تسهیلات
Facilities? facilities;
/// وضعیت بازرسی (مثلاً: انجام شده، در حال انجام، لغو شده)
String? inspectionStatus;
/// یادداشت‌ها و توضیحات بازرس
String? inspectionNotes;
/// تبدیل شیء به فرمت JSON برای ارسال به سرور
///
/// تمام اطلاعات بازرسی را در یک ساختار JSON سازماندهی می‌کند.
/// بخش‌های مختلف بازرسی در زیر کلید `report_information` قرار می‌گیرند.
Map<String, dynamic> toJson() {
return {
'lat': lat,
@@ -49,6 +90,9 @@ class SubmitInspectionResponse {
};
}
/// ساخت شیء از JSON دریافتی از سرور
///
/// این متد برای تبدیل پاسخ JSON سرور به یک شیء SubmitInspectionResponse استفاده می‌شود.
factory SubmitInspectionResponse.fromJson(Map<String, dynamic> json) {
return SubmitInspectionResponse(
lat: json['lat'].toString(),
@@ -88,7 +132,11 @@ class SubmitInspectionResponse {
}
}
/// اطلاعات مربوط به شرایط عمومی سالن مرغداری
///
/// این کلاس شامل اطلاعاتی در مورد وضعیت سلامت، تهویه، بستر، دما و آب آشامیدنی سالن است.
class GeneralConditionHall {
/// سازنده کلاس GeneralConditionHall
GeneralConditionHall({
this.images,
this.healthStatus,
@@ -99,12 +147,25 @@ class GeneralConditionHall {
this.drinkingWaterQuality,
});
/// لیست آدرس تصاویر مربوط به شرایط سالن
List<String>? images;
/// وضعیت سلامت سالن
String? healthStatus;
/// وضعیت سیستم تهویه
String? ventilationStatus;
/// وضعیت بستر سالن
String? bedCondition;
/// دمای سالن
String? temperature;
/// منبع آب آشامیدنی
String? drinkingWaterSource;
/// کیفیت آب آشامیدنی
String? drinkingWaterQuality;
Map<String, dynamic> toJson() {
@@ -134,7 +195,12 @@ class GeneralConditionHall {
}
}
/// اطلاعات مربوط به تلفات و خسارات
///
/// این کلاس شامل اطلاعاتی در مورد تلفات عادی و غیرعادی، منبع جوجه‌ریزی،
/// علت تلفات غیرعادی، نوع بیماری و نمونه‌برداری است.
class Casualties {
/// سازنده کلاس Casualties
Casualties({
this.normalLosses,
this.abnormalLosses,
@@ -146,13 +212,28 @@ class Casualties {
this.images,
});
/// تعداد تلفات عادی
int? normalLosses;
/// تعداد تلفات غیرعادی
int? abnormalLosses;
/// منبع جوجه‌ریزی
String? sourceOfHatching;
/// علت تلفات غیرعادی
String? causeAbnormalLosses;
/// نوع بیماری (در صورت وجود)
String? typeDisease;
/// آیا نمونه‌برداری انجام شده است؟
bool? samplingDone;
/// نوع نمونه‌برداری انجام شده
String? typeSampling;
/// لیست آدرس تصاویر مربوط به تلفات
List<String>? images;
Map<String, dynamic> toJson() {
@@ -184,13 +265,20 @@ class Casualties {
}
}
/// اطلاعات مربوط به کارشناسان فنی
///
/// این کلاس شامل اطلاعات کارشناس بهداشت و کارشناس فنی مهندسی است.
class TechnicalOfficer {
/// سازنده کلاس TechnicalOfficer
TechnicalOfficer({
this.technicalHealthOfficer,
this.technicalEngineeringOfficer,
});
/// نام یا اطلاعات کارشناس بهداشت
String? technicalHealthOfficer;
/// نام یا اطلاعات کارشناس فنی مهندسی
String? technicalEngineeringOfficer;
Map<String, dynamic> toJson() {
@@ -209,7 +297,12 @@ class TechnicalOfficer {
}
}
/// اطلاعات مربوط به وضعیت نهاده‌ها
///
/// این کلاس شامل اطلاعاتی در مورد نهاده‌های مصرفی مرغداری شامل:
/// وضعیت نهاده، نام شرکت، کد رهگیری، نوع دان، موجودی انبار و درجه دان است.
class InputStatus {
/// سازنده کلاس InputStatus
InputStatus({
this.inputStatus,
this.companyName,
@@ -221,13 +314,28 @@ class InputStatus {
this.images,
});
/// وضعیت نهاده‌ها
String? inputStatus;
/// نام شرکت تامین‌کننده نهاده
String? companyName;
/// کد رهگیری نهاده
String? trackingCode;
/// نوع دان مصرفی
String? typeOfGrain;
/// موجودی نهاده در انبار
String? inventoryInWarehouse;
/// موجودی نهاده تا زمان بازدید
String? inventoryUntilVisit;
/// درجه دان
String? gradeGrain;
/// لیست آدرس تصاویر مربوط به نهاده‌ها
List<String>? images;
Map<String, dynamic> toJson() {
@@ -259,7 +367,12 @@ class InputStatus {
}
}
/// اطلاعات مربوط به زیرساخت و انرژی
///
/// این کلاس شامل اطلاعاتی در مورد ژنراتور، سوخت، قطعی برق و سایر
/// اطلاعات مربوط به زیرساخت انرژی مرغداری است.
class InfrastructureEnergy {
/// سازنده کلاس InfrastructureEnergy
InfrastructureEnergy({
this.generatorType,
this.generatorModel,
@@ -274,16 +387,37 @@ class InfrastructureEnergy {
this.additionalNotes,
});
/// نوع ژنراتور
String? generatorType;
/// مدل ژنراتور
String? generatorModel;
/// تعداد ژنراتورها
String? generatorCount;
/// ظرفیت ژنراتور
String? generatorCapacity;
/// نوع سوخت ژنراتور
String? fuelType;
/// عملکرد ژنراتور
String? generatorPerformance;
/// موجودی سوخت اضطراری
String? emergencyFuelInventory;
/// آیا سابقه قطعی برق وجود دارد؟
bool? hasPowerCutHistory;
/// مدت زمان قطعی برق
String? powerCutDuration;
/// ساعت قطعی برق
String? powerCutHour;
/// یادداشت‌های اضافی
String? additionalNotes;
Map<String, dynamic> toJson() {
@@ -319,7 +453,12 @@ class InfrastructureEnergy {
}
}
/// اطلاعات مربوط به نیروی انسانی
///
/// این کلاس شامل اطلاعاتی در مورد تعداد کارکنان، وضعیت بومی/غیربومی بودن
/// کارکنان، وضعیت قرارداد و آموزش کارکنان است.
class Hr {
/// سازنده کلاس Hr
Hr({
this.numberEmployed,
this.numberIndigenous,
@@ -328,10 +467,19 @@ class Hr {
this.trained,
});
/// تعداد کل کارکنان
int? numberEmployed;
/// تعداد کارکنان بومی
int? numberIndigenous;
/// تعداد کارکنان غیربومی
int? numberNonIndigenous;
/// وضعیت قرارداد کارکنان
String? contractStatus;
/// آیا کارکنان آموزش دیده‌اند؟
bool? trained;
Map<String, dynamic> toJson() {
@@ -355,7 +503,12 @@ class Hr {
}
}
/// اطلاعات مربوط به تسهیلات
///
/// این کلاس شامل اطلاعاتی در مورد تسهیلات دریافتی مرغداری شامل:
/// وجود تسهیلات، نوع تسهیلات، مبلغ، تاریخ و وضعیت بازپرداخت است.
class Facilities {
/// سازنده کلاس Facilities
Facilities({
this.hasFacilities,
this.typeOfFacility,
@@ -365,11 +518,22 @@ class Facilities {
this.requestFacilities,
});
/// آیا تسهیلات دریافت شده است؟
bool? hasFacilities;
/// نوع تسهیلات
String? typeOfFacility;
/// مبلغ تسهیلات
int? amount;
/// تاریخ دریافت تسهیلات
String? date;
/// وضعیت بازپرداخت تسهیلات
String? repaymentStatus;
/// درخواست تسهیلات
String? requestFacilities;
Map<String, dynamic> toJson() {

View File

@@ -0,0 +1,424 @@
{
"description": "ساختار و توضیحات کامل مدل SubmitInspectionResponse برای ارسال اطلاعات بازرسی مرغداری",
"endpoint": "/poultry_science_report/",
"method": "POST",
"schema": {
"lat": {
"type": "string",
"required": false,
"description": "عرض جغرافیایی (Latitude) محل بازرسی",
"example": "35.6892"
},
"log": {
"type": "string",
"required": false,
"description": "طول جغرافیایی (Longitude) محل بازرسی",
"example": "51.3890"
},
"hatching_id": {
"type": "integer",
"required": false,
"description": "شناسه یکتای جوجه‌ریزی که بازرسی برای آن انجام می‌شود",
"example": 12345
},
"role": {
"type": "string",
"required": false,
"description": "نقش کاربری که بازرسی را انجام می‌دهد",
"example": "inspector"
},
"report_information": {
"type": "object",
"required": false,
"description": "اطلاعات کامل بازرسی در بخش‌های مختلف",
"properties": {
"general_condition_hall": {
"type": "object",
"required": false,
"description": "اطلاعات مربوط به شرایط عمومی سالن مرغداری",
"properties": {
"images": {
"type": "array",
"items": {
"type": "string"
},
"required": false,
"description": "لیست آدرس تصاویر مربوط به شرایط سالن"
},
"health_status": {
"type": "string",
"required": false,
"description": "وضعیت سلامت سالن"
},
"ventilation_status": {
"type": "string",
"required": false,
"description": "وضعیت سیستم تهویه"
},
"bed_condition": {
"type": "string",
"required": false,
"description": "وضعیت بستر سالن"
},
"temperature": {
"type": "string",
"required": false,
"description": "دمای سالن"
},
"drinking_water_source": {
"type": "string",
"required": false,
"description": "منبع آب آشامیدنی"
},
"drinking_water_quality": {
"type": "string",
"required": false,
"description": "کیفیت آب آشامیدنی"
}
}
},
"casualties": {
"type": "object",
"required": false,
"description": "اطلاعات مربوط به تلفات و خسارات",
"properties": {
"normal_losses": {
"type": "integer",
"required": false,
"description": "تعداد تلفات عادی",
"example": 50
},
"abnormal_losses": {
"type": "integer",
"required": false,
"description": "تعداد تلفات غیرعادی",
"example": 10
},
"source_of_hatching": {
"type": "string",
"required": false,
"description": "منبع جوجه‌ریزی"
},
"cause_abnormal_losses": {
"type": "string",
"required": false,
"description": "علت تلفات غیرعادی"
},
"type_disease": {
"type": "string",
"required": false,
"description": "نوع بیماری (در صورت وجود)"
},
"sampling_done": {
"type": "boolean",
"required": false,
"description": "آیا نمونه‌برداری انجام شده است؟",
"example": true
},
"type_sampling": {
"type": "string",
"required": false,
"description": "نوع نمونه‌برداری انجام شده"
},
"images": {
"type": "array",
"items": {
"type": "string"
},
"required": false,
"description": "لیست آدرس تصاویر مربوط به تلفات"
}
}
},
"technical_officer": {
"type": "object",
"required": false,
"description": "اطلاعات مربوط به کارشناسان فنی",
"properties": {
"technical_health_officer": {
"type": "string",
"required": false,
"description": "نام یا اطلاعات کارشناس بهداشت"
},
"technical_engineering_officer": {
"type": "string",
"required": false,
"description": "نام یا اطلاعات کارشناس فنی مهندسی"
}
}
},
"input_status": {
"type": "object",
"required": false,
"description": "اطلاعات مربوط به وضعیت نهاده‌ها",
"properties": {
"input_status": {
"type": "string",
"required": false,
"description": "وضعیت نهاده‌ها"
},
"company_name": {
"type": "string",
"required": false,
"description": "نام شرکت تامین‌کننده نهاده"
},
"tracking_code": {
"type": "string",
"required": false,
"description": "کد رهگیری نهاده"
},
"type_of_grain": {
"type": "string",
"required": false,
"description": "نوع دان مصرفی"
},
"inventory_in_warehouse": {
"type": "string",
"required": false,
"description": "موجودی نهاده در انبار"
},
"inventory_until_visit": {
"type": "string",
"required": false,
"description": "موجودی نهاده تا زمان بازدید"
},
"grade_grain": {
"type": "string",
"required": false,
"description": "درجه دان"
},
"images": {
"type": "array",
"items": {
"type": "string"
},
"required": false,
"description": "لیست آدرس تصاویر مربوط به نهاده‌ها"
}
}
},
"infrastructure_energy": {
"type": "object",
"required": false,
"description": "اطلاعات مربوط به زیرساخت و انرژی",
"properties": {
"generator_type": {
"type": "string",
"required": false,
"description": "نوع ژنراتور"
},
"generator_model": {
"type": "string",
"required": false,
"description": "مدل ژنراتور"
},
"generator_count": {
"type": "string",
"required": false,
"description": "تعداد ژنراتورها"
},
"generator_capacity": {
"type": "string",
"required": false,
"description": "ظرفیت ژنراتور"
},
"fuel_type": {
"type": "string",
"required": false,
"description": "نوع سوخت ژنراتور"
},
"generator_performance": {
"type": "string",
"required": false,
"description": "عملکرد ژنراتور"
},
"emergency_fuel_inventory": {
"type": "string",
"required": false,
"description": "موجودی سوخت اضطراری"
},
"has_power_cut_history": {
"type": "boolean",
"required": false,
"description": "آیا سابقه قطعی برق وجود دارد؟",
"example": true
},
"power_cut_duration": {
"type": "string",
"required": false,
"description": "مدت زمان قطعی برق"
},
"power_cut_hour": {
"type": "string",
"required": false,
"description": "ساعت قطعی برق"
},
"additional_notes": {
"type": "string",
"required": false,
"description": "یادداشت‌های اضافی"
}
}
},
"hr": {
"type": "object",
"required": false,
"description": "اطلاعات مربوط به نیروی انسانی",
"properties": {
"number_employed": {
"type": "integer",
"required": false,
"description": "تعداد کل کارکنان",
"example": 15
},
"number_indigenous": {
"type": "integer",
"required": false,
"description": "تعداد کارکنان بومی",
"example": 10
},
"number_non_indigenous": {
"type": "integer",
"required": false,
"description": "تعداد کارکنان غیربومی",
"example": 5
},
"contract_status": {
"type": "string",
"required": false,
"description": "وضعیت قرارداد کارکنان"
},
"trained": {
"type": "boolean",
"required": false,
"description": "آیا کارکنان آموزش دیده‌اند؟",
"example": true
}
}
},
"facilities": {
"type": "object",
"required": false,
"description": "اطلاعات مربوط به تسهیلات",
"properties": {
"has_facilities": {
"type": "boolean",
"required": false,
"description": "آیا تسهیلات دریافت شده است؟",
"example": true
},
"type_of_facility": {
"type": "string",
"required": false,
"description": "نوع تسهیلات"
},
"amount": {
"type": "integer",
"required": false,
"description": "مبلغ تسهیلات",
"example": 500000000
},
"date": {
"type": "string",
"required": false,
"description": "تاریخ دریافت تسهیلات"
},
"repayment_status": {
"type": "string",
"required": false,
"description": "وضعیت بازپرداخت تسهیلات"
},
"request_facilities": {
"type": "string",
"required": false,
"description": "درخواست تسهیلات"
}
}
},
"inspection_status": {
"type": "string",
"required": false,
"description": "وضعیت بازرسی (مثلاً: انجام شده، در حال انجام، لغو شده)"
},
"inspection_notes": {
"type": "string",
"required": false,
"description": "یادداشت‌ها و توضیحات بازرس"
}
}
}
},
"example_request": {
"lat": "35.6892",
"log": "51.3890",
"hatching_id": 12345,
"role": "inspector",
"report_information": {
"general_condition_hall": {
"images": [
"https://example.com/image1.jpg"
],
"health_status": "خوب",
"ventilation_status": "مناسب",
"bed_condition": "تمیز",
"temperature": "25",
"drinking_water_source": "چاه",
"drinking_water_quality": "عالی"
},
"casualties": {
"normal_losses": 50,
"abnormal_losses": 10,
"source_of_hatching": "مرکز جوجه‌کشی",
"cause_abnormal_losses": "بیماری",
"type_disease": "نیوکاسل",
"sampling_done": true,
"type_sampling": "خون",
"images": []
},
"technical_officer": {
"technical_health_officer": "احمد محمدی",
"technical_engineering_officer": "علی رضایی"
},
"input_status": {
"input_status": "کافی",
"company_name": "شرکت دان",
"tracking_code": "TR123456",
"type_of_grain": "استارتر",
"inventory_in_warehouse": "5000 کیلوگرم",
"inventory_until_visit": "4500 کیلوگرم",
"grade_grain": "A",
"images": []
},
"infrastructure_energy": {
"generator_type": "دیزلی",
"generator_model": "کامینز",
"generator_count": "2",
"generator_capacity": "100 کیلووات",
"fuel_type": "گازوئیل",
"generator_performance": "عالی",
"emergency_fuel_inventory": "5000 لیتر",
"has_power_cut_history": true,
"power_cut_duration": "2 ساعت",
"power_cut_hour": "14:00",
"additional_notes": "ژنراتورها به درستی نگهداری می‌شوند"
},
"hr": {
"number_employed": 15,
"number_indigenous": 10,
"number_non_indigenous": 5,
"contract_status": "قراردادی",
"trained": true
},
"facilities": {
"has_facilities": true,
"type_of_facility": "وام",
"amount": 500000000,
"date": "1403/01/15",
"repayment_status": "در حال بازپرداخت",
"request_facilities": "درخواست تسهیلات جدید"
},
"inspection_status": "انجام شده",
"inspection_notes": "بازرسی با موفقیت انجام شد"
}
}
}

View File

@@ -0,0 +1,15 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_core/core.dart';
abstract class ProvinceInspectorRemoteDataSource {
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
});
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
});
}

View File

@@ -0,0 +1,40 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_chicken/features/province_inspector/data/datasources/remote/province_inspector_remote_data_source.dart';
import 'package:rasadyar_core/core.dart';
class ProvinceInspectorRemoteDataSourceImpl
implements ProvinceInspectorRemoteDataSource {
final DioRemote _httpClient;
ProvinceInspectorRemoteDataSourceImpl(this._httpClient);
@override
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
}) async {
var res = await _httpClient.get(
'/poultry_science_report/',
headers: {'Authorization': 'Bearer $token'},
queryParameters: queryParameters,
fromJson: (json) => PaginationModel<PoultryScienceReport>.fromJson(
json,
(json) => PoultryScienceReport.fromJson(json as Map<String, dynamic>),
),
);
return res.data;
}
@override
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
}) async {
await _httpClient.post(
'/poultry_science_report/',
headers: {'Authorization': 'Bearer $token'},
data: request.toJson(),
);
}
}

View File

@@ -0,0 +1,36 @@
import 'package:rasadyar_chicken/features/province_inspector/data/datasources/remote/province_inspector_remote_data_source.dart';
import 'package:rasadyar_chicken/features/province_inspector/data/datasources/remote/province_inspector_remote_data_source_impl.dart';
import 'package:rasadyar_chicken/features/province_inspector/data/repositories/province_inspector_repository.dart';
import 'package:rasadyar_chicken/features/province_inspector/data/repositories/province_inspector_repository_impl.dart';
import 'package:rasadyar_core/core.dart';
/// Setup dependency injection for province_inspector feature
Future<void> setupProvinceInspectorDI(GetIt di, DioRemote dioRemote) async {
di.registerLazySingleton<ProvinceInspectorRemoteDataSource>(
() => ProvinceInspectorRemoteDataSourceImpl(dioRemote),
);
di.registerLazySingleton<ProvinceInspectorRepository>(
() => ProvinceInspectorRepositoryImpl(di.get<ProvinceInspectorRemoteDataSource>()),
);
}
/// Re-register province_inspector dependencies (used when base URL changes)
Future<void> reRegisterProvinceInspectorDI(GetIt di, DioRemote dioRemote) async {
await reRegister(di, () => ProvinceInspectorRemoteDataSourceImpl(dioRemote));
await reRegister(
di,
() => ProvinceInspectorRepositoryImpl(di.get<ProvinceInspectorRemoteDataSource>()),
);
}
/// Helper function to re-register a dependency
Future<void> reRegister<T extends Object>(
GetIt di,
T Function() factory,
) async {
if (di.isRegistered<T>()) {
await di.unregister<T>();
}
di.registerLazySingleton<T>(factory);
}

View File

@@ -0,0 +1,15 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_core/core.dart';
abstract class ProvinceInspectorRepository {
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
});
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
});
}

View File

@@ -0,0 +1,30 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_chicken/features/province_inspector/data/datasources/remote/province_inspector_remote_data_source.dart';
import 'package:rasadyar_chicken/features/province_inspector/data/repositories/province_inspector_repository.dart';
import 'package:rasadyar_core/core.dart';
class ProvinceInspectorRepositoryImpl implements ProvinceInspectorRepository {
final ProvinceInspectorRemoteDataSource _remote;
ProvinceInspectorRepositoryImpl(this._remote);
@override
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
}) async {
return await _remote.getSubmitInspectionList(
token: token,
queryParameters: queryParameters,
);
}
@override
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
}) async {
return await _remote.submitInspection(token: token, request: request);
}
}

View File

@@ -0,0 +1,95 @@
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/province_inspector/presentation/pages/root/logic.dart';
import 'package:rasadyar_core/core.dart';
class ActiveHatchingLogic extends GetxController {
ProvinceInspectorRootLogic rootLogic = Get.find<ProvinceInspectorRootLogic>();
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: 'ProvinceInspector',
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/province_inspector/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: provinceInspectorActionKey,
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,30 @@
import 'package:rasadyar_chicken/features/province_inspector/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/presentation/routes/routes.dart';
import 'package:rasadyar_core/core.dart';
class ProvinceInspectorActionItem {
final String title;
final String route;
final String icon;
ProvinceInspectorActionItem({
required this.title,
required this.route,
required this.icon,
});
}
class ProvinceInspectorHomeLogic extends GetxController {
RxList<ProvinceInspectorActionItem> items = [
ProvinceInspectorActionItem(
title: "جوجه ریزی فعال",
route: ProvinceInspectorRoutes.activeHatchingProvinceInspector,
icon: Assets.vec.activeFramSvg.path,
),
ProvinceInspectorActionItem(
title: "بازرسی مزارع طیور",
route: ProvinceInspectorRoutes.newInspectionProvinceInspector,
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 ProvinceInspectorHomePage extends GetView<ProvinceInspectorHomeLogic> {
ProvinceInspectorHomePage({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: provinceInspectorActionKey);
},
);
},
);
}, controller.items);
}
}

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/province_inspector/presentation/pages/root/logic.dart';
import 'package:rasadyar_core/core.dart';
class NewInspectionLogic extends GetxController {
BaseLogic baseLogic = Get.find<BaseLogic>();
Rx<Resource<PaginationModel<PoultryScienceReport>>> submitInspectionList =
Resource<PaginationModel<PoultryScienceReport>>.loading().obs;
ProvinceInspectorRootLogic rootLogic = Get.find<ProvinceInspectorRootLogic>();
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.provinceInspectorRepository.getSubmitInspectionList(
token: rootLogic.tokenService.accessToken.value!,
queryParameters: buildQueryParams(
role: 'ProvinceInspector',
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,84 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:rasadyar_chicken/data/di/chicken_di.dart';
import 'package:rasadyar_chicken/features/province_inspector/data/repositories/province_inspector_repository.dart';
import 'package:rasadyar_chicken/features/province_inspector/presentation/pages/home/view.dart';
import 'package:rasadyar_chicken/features/common/presentation/page/profile/view.dart';
import 'package:rasadyar_chicken/features/province_inspector/presentation/routes/pages.dart';
import 'package:rasadyar_chicken/features/province_inspector/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 ProvinceInspectorRootLogic extends GetxController {
var tokenService = Get.find<TokenStorageService>();
late ProvinceInspectorRepository provinceInspectorRepository;
RxList<ErrorLocationType> errorLocationType = RxList();
RxMap<int, dynamic> homeExpandedList = RxMap();
DateTime? _lastBackPressed;
RxInt currentPage = 0.obs;
final pages = [
Navigator(
key: Get.nestedKey(provinceInspectorActionKey),
onGenerateRoute: (settings) {
final page = ProvinceInspectorPages.pages.firstWhere(
(e) => e.name == settings.name,
orElse: () => ProvinceInspectorPages.pages.firstWhere(
(e) => e.name == ProvinceInspectorRoutes.homeProvinceInspector,
),
);
return buildRouteFromGetPage(page);
},
),
ProfilePage(),
];
@override
void onInit() {
super.onInit();
provinceInspectorRepository = diChicken.get<ProvinceInspectorRepository>();
}
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;
}
void popBackTaped() async {
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,57 @@
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 ProvinceInspectorRootPage extends GetView<ProvinceInspectorRootLogic> {
const ProvinceInspectorRootPage({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.homeSvg.path,
isSelected: controller.currentPage.value == 0,
onTap: () {
Get.nestedKey(
provinceInspectorActionKey,
)?.currentState?.popUntil((route) => route.isFirst);
controller.changePage(0);
},
),
RBottomNavigationItem(
label: 'پروفایل',
icon: Assets.vec.profileCircleSvg.path,
isSelected: controller.currentPage.value == 1,
onTap: () {
Get.nestedKey(
provinceInspectorActionKey,
)?.currentState?.popUntil((route) => route.isFirst);
controller.changePage(1);
},
),
],
),
),
],
);
}, controller.currentPage),
);
}
}

View File

@@ -0,0 +1,63 @@
import 'package:rasadyar_chicken/features/province_inspector/presentation/pages/home/logic.dart';
import 'package:rasadyar_chicken/features/province_inspector/presentation/pages/home/view.dart';
import 'package:rasadyar_chicken/features/province_inspector/presentation/pages/root/logic.dart';
import 'package:rasadyar_chicken/features/province_inspector/presentation/pages/root/view.dart';
import 'package:rasadyar_chicken/features/province_inspector/presentation/pages/active_hatching/logic.dart';
import 'package:rasadyar_chicken/features/province_inspector/presentation/pages/active_hatching/view.dart';
import 'package:rasadyar_chicken/features/province_inspector/presentation/pages/new_inspection/logic.dart';
import 'package:rasadyar_chicken/features/province_inspector/presentation/pages/new_inspection/view.dart';
import 'package:rasadyar_chicken/features/province_inspector/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/presentation/routes/global_binding.dart';
import 'package:rasadyar_chicken/presentation/widget/base_page/logic.dart';
import 'package:rasadyar_core/core.dart';
class ProvinceInspectorPages {
ProvinceInspectorPages._();
static List<GetPage> get pages => [
GetPage(
name: ProvinceInspectorRoutes.initProvinceInspector,
page: () => ProvinceInspectorRootPage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => ChickenBaseLogic(), fenix: true);
Get.lazyPut(() => ProvinceInspectorRootLogic());
Get.lazyPut(() => ProvinceInspectorHomeLogic());
}),
],
),
GetPage(
name: ProvinceInspectorRoutes.homeProvinceInspector,
page: () => ProvinceInspectorHomePage(),
middlewares: [AuthMiddleware()],
binding: BindingsBuilder(() {
Get.put(ProvinceInspectorHomeLogic());
Get.lazyPut(() => ChickenBaseLogic());
}),
),
GetPage(
name: ProvinceInspectorRoutes.activeHatchingProvinceInspector,
page: () => ActiveHatchingPage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => ActiveHatchingLogic());
}),
],
),
GetPage(
name: ProvinceInspectorRoutes.newInspectionProvinceInspector,
page: () => NewInspectionPage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => NewInspectionLogic());
}),
],
),
];
}

View File

@@ -0,0 +1,10 @@
sealed class ProvinceInspectorRoutes {
ProvinceInspectorRoutes._();
static const _base = '/chicken/provinceInspector';
static const initProvinceInspector = '$_base/';
static const homeProvinceInspector = '$_base/home';
static const actionProvinceInspector = '$_base/action';
static const activeHatchingProvinceInspector = '$_base/activeHatching';
static const newInspectionProvinceInspector = '$_base/newInspection';
}

View File

@@ -0,0 +1,3 @@
export 'data/di/province_inspector_di.dart';
export 'presentation/routes/routes.dart';
export 'presentation/routes/pages.dart';

View File

@@ -0,0 +1,15 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_core/core.dart';
abstract class ProvinceOperatorRemoteDataSource {
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
});
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
});
}

View File

@@ -0,0 +1,40 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_chicken/features/province_operator/data/datasources/remote/province_operator_remote_data_source.dart';
import 'package:rasadyar_core/core.dart';
class ProvinceOperatorRemoteDataSourceImpl
implements ProvinceOperatorRemoteDataSource {
final DioRemote _httpClient;
ProvinceOperatorRemoteDataSourceImpl(this._httpClient);
@override
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
}) async {
var res = await _httpClient.get(
'/poultry_science_report/',
headers: {'Authorization': 'Bearer $token'},
queryParameters: queryParameters,
fromJson: (json) => PaginationModel<PoultryScienceReport>.fromJson(
json,
(json) => PoultryScienceReport.fromJson(json as Map<String, dynamic>),
),
);
return res.data;
}
@override
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
}) async {
await _httpClient.post(
'/poultry_science_report/',
headers: {'Authorization': 'Bearer $token'},
data: request.toJson(),
);
}
}

View File

@@ -0,0 +1,46 @@
import 'package:rasadyar_chicken/features/province_operator/data/datasources/remote/province_operator_remote_data_source.dart';
import 'package:rasadyar_chicken/features/province_operator/data/datasources/remote/province_operator_remote_data_source_impl.dart';
import 'package:rasadyar_chicken/features/province_operator/data/repositories/province_operator_repository.dart';
import 'package:rasadyar_chicken/features/province_operator/data/repositories/province_operator_repository_impl.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/repositories/poultry_science_repository.dart';
import 'package:rasadyar_core/core.dart';
/// Setup dependency injection for province_operator feature
Future<void> setupProvinceOperatorDI(GetIt di, DioRemote dioRemote) async {
di.registerLazySingleton<ProvinceOperatorRemoteDataSource>(
() => ProvinceOperatorRemoteDataSourceImpl(dioRemote),
);
di.registerLazySingleton<ProvinceOperatorRepository>(
() => ProvinceOperatorRepositoryImpl(
di.get<ProvinceOperatorRemoteDataSource>(),
),
);
// Use PoultryScienceRepository for shared functionality
if (!di.isRegistered<PoultryScienceRepository>()) {
// PoultryScienceRepository should already be registered, but just in case
}
}
/// Re-register province_operator dependencies (used when base URL changes)
Future<void> reRegisterProvinceOperatorDI(GetIt di, DioRemote dioRemote) async {
await reRegister(di, () => ProvinceOperatorRemoteDataSourceImpl(dioRemote));
await reRegister(
di,
() => ProvinceOperatorRepositoryImpl(
di.get<ProvinceOperatorRemoteDataSource>(),
),
);
}
/// Helper function to re-register a dependency
Future<void> reRegister<T extends Object>(
GetIt di,
T Function() factory,
) async {
if (di.isRegistered<T>()) {
await di.unregister<T>();
}
di.registerLazySingleton<T>(factory);
}

View File

@@ -0,0 +1,15 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_core/core.dart';
abstract class ProvinceOperatorRepository {
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
});
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
});
}

View File

@@ -0,0 +1,30 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_chicken/features/province_operator/data/datasources/remote/province_operator_remote_data_source.dart';
import 'package:rasadyar_chicken/features/province_operator/data/repositories/province_operator_repository.dart';
import 'package:rasadyar_core/core.dart';
class ProvinceOperatorRepositoryImpl implements ProvinceOperatorRepository {
final ProvinceOperatorRemoteDataSource _remote;
ProvinceOperatorRepositoryImpl(this._remote);
@override
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
}) async {
return await _remote.getSubmitInspectionList(
token: token,
queryParameters: queryParameters,
);
}
@override
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
}) async {
return await _remote.submitInspection(token: token, request: request);
}
}

View File

@@ -0,0 +1,95 @@
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/province_operator/presentation/pages/root/logic.dart';
import 'package:rasadyar_core/core.dart';
class ActiveHatchingLogic extends GetxController {
ProvinceOperatorRootLogic rootLogic = Get.find<ProvinceOperatorRootLogic>();
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: 'ProvinceOperator',
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/province_operator/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: provinceOperatorActionKey,
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,30 @@
import 'package:rasadyar_chicken/features/province_operator/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/presentation/routes/routes.dart';
import 'package:rasadyar_core/core.dart';
class ProvinceOperatorHomeItem {
final String title;
final String route;
final String icon;
ProvinceOperatorHomeItem({
required this.title,
required this.route,
required this.icon,
});
}
class ProvinceOperatorHomeLogic extends GetxController {
RxList<ProvinceOperatorHomeItem> items = [
ProvinceOperatorHomeItem(
title: "جوجه ریزی فعال",
route: ProvinceOperatorRoutes.activeHatchingProvinceOperator,
icon: Assets.vec.activeFramSvg.path,
),
ProvinceOperatorHomeItem(
title: "بازرسی مزارع طیور",
route: ProvinceOperatorRoutes.newInspectionProvinceOperator,
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 ProvinceOperatorHomePage extends GetView<ProvinceOperatorHomeLogic> {
ProvinceOperatorHomePage({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: provinceOperatorActionKey);
},
);
},
);
}, controller.items);
}
}

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/province_operator/presentation/pages/root/logic.dart';
import 'package:rasadyar_core/core.dart';
class NewInspectionLogic extends GetxController {
BaseLogic baseLogic = Get.find<BaseLogic>();
Rx<Resource<PaginationModel<PoultryScienceReport>>> submitInspectionList =
Resource<PaginationModel<PoultryScienceReport>>.loading().obs;
ProvinceOperatorRootLogic rootLogic = Get.find<ProvinceOperatorRootLogic>();
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.provinceOperatorRepository.getSubmitInspectionList(
token: rootLogic.tokenService.accessToken.value!,
queryParameters: buildQueryParams(
role: 'ProvinceOperator',
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,83 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:rasadyar_chicken/data/di/chicken_di.dart';
import 'package:rasadyar_chicken/features/province_operator/data/repositories/province_operator_repository.dart';
import 'package:rasadyar_chicken/features/common/presentation/page/profile/view.dart';
import 'package:rasadyar_chicken/features/province_operator/presentation/routes/pages.dart';
import 'package:rasadyar_chicken/features/province_operator/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 ProvinceOperatorRootLogic extends GetxController {
var tokenService = Get.find<TokenStorageService>();
late ProvinceOperatorRepository provinceOperatorRepository;
RxList<ErrorLocationType> errorLocationType = RxList();
RxMap<int, dynamic> homeExpandedList = RxMap();
DateTime? _lastBackPressed;
RxInt currentPage = 0.obs;
final pages = [
Navigator(
key: Get.nestedKey(provinceOperatorActionKey),
onGenerateRoute: (settings) {
final page = ProvinceOperatorPages.pages.firstWhere(
(e) => e.name == settings.name,
orElse: () => ProvinceOperatorPages.pages.firstWhere(
(e) => e.name == ProvinceOperatorRoutes.homeProvinceOperator,
),
);
return buildRouteFromGetPage(page);
},
),
ProfilePage(),
];
@override
void onInit() {
super.onInit();
provinceOperatorRepository = diChicken.get<ProvinceOperatorRepository>();
}
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;
}
void popBackTaped() async {
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,59 @@
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 ProvinceOperatorRootPage extends GetView<ProvinceOperatorRootLogic> {
const ProvinceOperatorRootPage({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.homeSvg.path,
isSelected: controller.currentPage.value == 0,
onTap: () {
Get.nestedKey(
provinceOperatorActionKey,
)?.currentState?.popUntil((route) => route.isFirst);
controller.changePage(0);
},
),
RBottomNavigationItem(
label: 'پروفایل',
icon: Assets.vec.profileCircleSvg.path,
isSelected: controller.currentPage.value == 1,
onTap: () {
Get.nestedKey(
provinceOperatorActionKey,
)?.currentState?.popUntil((route) => route.isFirst);
controller.changePage(1);
},
),
],
),
),
],
);
}, controller.currentPage),
);
}
}

View File

@@ -0,0 +1,76 @@
import 'package:rasadyar_chicken/features/province_operator/presentation/pages/home/logic.dart';
import 'package:rasadyar_chicken/features/province_operator/presentation/pages/home/view.dart';
import 'package:rasadyar_chicken/features/province_operator/presentation/pages/root/logic.dart';
import 'package:rasadyar_chicken/features/province_operator/presentation/pages/root/view.dart';
import 'package:rasadyar_chicken/features/province_operator/presentation/pages/home/logic.dart';
import 'package:rasadyar_chicken/features/province_operator/presentation/pages/home/view.dart';
import 'package:rasadyar_chicken/features/province_operator/presentation/pages/active_hatching/logic.dart';
import 'package:rasadyar_chicken/features/province_operator/presentation/pages/active_hatching/view.dart';
import 'package:rasadyar_chicken/features/province_operator/presentation/pages/new_inspection/logic.dart';
import 'package:rasadyar_chicken/features/province_operator/presentation/pages/new_inspection/view.dart';
import 'package:rasadyar_chicken/features/province_operator/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/presentation/routes/global_binding.dart';
import 'package:rasadyar_chicken/presentation/widget/base_page/logic.dart';
import 'package:rasadyar_core/core.dart';
class ProvinceOperatorPages {
ProvinceOperatorPages._();
static List<GetPage> get pages => [
GetPage(
name: ProvinceOperatorRoutes.initProvinceOperator,
page: () => ProvinceOperatorRootPage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => ChickenBaseLogic(), fenix: true);
Get.lazyPut(() => ProvinceOperatorRootLogic());
Get.lazyPut(() => ProvinceOperatorHomeLogic());
}),
],
),
GetPage(
name: ProvinceOperatorRoutes.homeProvinceOperator,
page: () => ProvinceOperatorHomePage(),
middlewares: [AuthMiddleware()],
binding: BindingsBuilder(() {
Get.put(ProvinceOperatorHomeLogic());
Get.lazyPut(() => ChickenBaseLogic());
}),
),
GetPage(
name: ProvinceOperatorRoutes.actionProvinceOperator,
page: () => ProvinceOperatorHomePage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => ProvinceOperatorHomeLogic());
}),
],
),
GetPage(
name: ProvinceOperatorRoutes.activeHatchingProvinceOperator,
page: () => ActiveHatchingPage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => ActiveHatchingLogic());
}),
],
),
GetPage(
name: ProvinceOperatorRoutes.newInspectionProvinceOperator,
page: () => NewInspectionPage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => NewInspectionLogic());
}),
],
),
];
}

View File

@@ -0,0 +1,10 @@
sealed class ProvinceOperatorRoutes {
ProvinceOperatorRoutes._();
static const _base = '/chicken/provinceOperator';
static const initProvinceOperator = '$_base/';
static const homeProvinceOperator = '$_base/home';
static const actionProvinceOperator = '$_base/action';
static const activeHatchingProvinceOperator = '$_base/activeHatching';
static const newInspectionProvinceOperator = '$_base/newInspection';
}

View File

@@ -0,0 +1,3 @@
export 'data/di/province_operator_di.dart';
export 'presentation/routes/routes.dart';
export 'presentation/routes/pages.dart';

View File

@@ -0,0 +1,15 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_core/core.dart';
abstract class ProvinceSupervisorRemoteDataSource {
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
});
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
});
}

View File

@@ -0,0 +1,40 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_chicken/features/province_supervisor/data/datasources/remote/province_supervisor_remote_data_source.dart';
import 'package:rasadyar_core/core.dart';
class ProvinceSupervisorRemoteDataSourceImpl
implements ProvinceSupervisorRemoteDataSource {
final DioRemote _httpClient;
ProvinceSupervisorRemoteDataSourceImpl(this._httpClient);
@override
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
}) async {
var res = await _httpClient.get(
'/poultry_science_report/',
headers: {'Authorization': 'Bearer $token'},
queryParameters: queryParameters,
fromJson: (json) => PaginationModel<PoultryScienceReport>.fromJson(
json,
(json) => PoultryScienceReport.fromJson(json as Map<String, dynamic>),
),
);
return res.data;
}
@override
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
}) async {
await _httpClient.post(
'/poultry_science_report/',
headers: {'Authorization': 'Bearer $token'},
data: request.toJson(),
);
}
}

View File

@@ -0,0 +1,40 @@
import 'package:rasadyar_chicken/features/province_supervisor/data/datasources/remote/province_supervisor_remote_data_source.dart';
import 'package:rasadyar_chicken/features/province_supervisor/data/datasources/remote/province_supervisor_remote_data_source_impl.dart';
import 'package:rasadyar_chicken/features/province_supervisor/data/repositories/province_supervisor_repository.dart';
import 'package:rasadyar_chicken/features/province_supervisor/data/repositories/province_supervisor_repository_impl.dart';
import 'package:rasadyar_core/core.dart';
/// Setup dependency injection for province_supervisor feature
Future<void> setupProvinceSupervisorDI(GetIt di, DioRemote dioRemote) async {
di.registerLazySingleton<ProvinceSupervisorRemoteDataSource>(
() => ProvinceSupervisorRemoteDataSourceImpl(dioRemote),
);
di.registerLazySingleton<ProvinceSupervisorRepository>(
() => ProvinceSupervisorRepositoryImpl(
di.get<ProvinceSupervisorRemoteDataSource>()),
);
}
/// Re-register province_supervisor dependencies (used when base URL changes)
Future<void> reRegisterProvinceSupervisorDI(
GetIt di, DioRemote dioRemote) async {
await reRegister(
di, () => ProvinceSupervisorRemoteDataSourceImpl(dioRemote));
await reRegister(
di,
() => ProvinceSupervisorRepositoryImpl(
di.get<ProvinceSupervisorRemoteDataSource>()),
);
}
/// Helper function to re-register a dependency
Future<void> reRegister<T extends Object>(
GetIt di,
T Function() factory,
) async {
if (di.isRegistered<T>()) {
await di.unregister<T>();
}
di.registerLazySingleton<T>(factory);
}

View File

@@ -0,0 +1,15 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_core/core.dart';
abstract class ProvinceSupervisorRepository {
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
});
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
});
}

View File

@@ -0,0 +1,30 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_chicken/features/province_supervisor/data/datasources/remote/province_supervisor_remote_data_source.dart';
import 'package:rasadyar_chicken/features/province_supervisor/data/repositories/province_supervisor_repository.dart';
import 'package:rasadyar_core/core.dart';
class ProvinceSupervisorRepositoryImpl implements ProvinceSupervisorRepository {
final ProvinceSupervisorRemoteDataSource _remote;
ProvinceSupervisorRepositoryImpl(this._remote);
@override
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
}) async {
return await _remote.getSubmitInspectionList(
token: token,
queryParameters: queryParameters,
);
}
@override
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
}) async {
return await _remote.submitInspection(token: token, request: request);
}
}

View File

@@ -0,0 +1,95 @@
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/province_operator/presentation/pages/root/logic.dart';
import 'package:rasadyar_core/core.dart';
class ActiveHatchingLogic extends GetxController {
ProvinceOperatorRootLogic rootLogic = Get.find<ProvinceOperatorRootLogic>();
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: 'ProvinceOperator',
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/province_operator/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: provinceOperatorActionKey,
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,30 @@
import 'package:rasadyar_chicken/features/province_supervisor/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/presentation/routes/routes.dart';
import 'package:rasadyar_core/core.dart';
class ProvinceSupervisorActionItem {
final String title;
final String route;
final String icon;
ProvinceSupervisorActionItem({
required this.title,
required this.route,
required this.icon,
});
}
class ProvinceSupervisorHomeLogic extends GetxController {
RxList<ProvinceSupervisorActionItem> items = [
ProvinceSupervisorActionItem(
title: "جوجه ریزی فعال",
route: ProvinceSupervisorRoutes.activeHatchingProvinceSupervisor,
icon: Assets.vec.activeFramSvg.path,
),
ProvinceSupervisorActionItem(
title: "بازرسی مزارع طیور",
route: ProvinceSupervisorRoutes.newInspectionProvinceSupervisor,
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 ProvinceSupervisorHomePage extends GetView<ProvinceSupervisorHomeLogic> {
ProvinceSupervisorHomePage({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: provinceSupervisorActionKey);
},
);
},
);
}, controller.items);
}
}

View File

@@ -0,0 +1,160 @@
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/province_supervisor/presentation/pages/root/logic.dart';
import 'package:rasadyar_core/core.dart';
class NewInspectionLogic extends GetxController {
BaseLogic baseLogic = Get.find<BaseLogic>();
Rx<Resource<PaginationModel<PoultryScienceReport>>> submitInspectionList =
Resource<PaginationModel<PoultryScienceReport>>.loading().obs;
ProvinceSupervisorRootLogic rootLogic =
Get.find<ProvinceSupervisorRootLogic>();
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.provinceSupervisorRepository.getSubmitInspectionList(
token: rootLogic.tokenService.accessToken.value!,
queryParameters: buildQueryParams(
role: 'ProvinceSupervisor',
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,85 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:rasadyar_chicken/data/di/chicken_di.dart';
import 'package:rasadyar_chicken/features/province_supervisor/data/repositories/province_supervisor_repository.dart';
import 'package:rasadyar_chicken/features/province_supervisor/presentation/pages/home/view.dart';
import 'package:rasadyar_chicken/features/common/presentation/page/profile/view.dart';
import 'package:rasadyar_chicken/features/province_supervisor/presentation/routes/pages.dart';
import 'package:rasadyar_chicken/features/province_supervisor/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 ProvinceSupervisorRootLogic extends GetxController {
var tokenService = Get.find<TokenStorageService>();
late ProvinceSupervisorRepository provinceSupervisorRepository;
RxList<ErrorLocationType> errorLocationType = RxList();
RxMap<int, dynamic> homeExpandedList = RxMap();
DateTime? _lastBackPressed;
RxInt currentPage = 0.obs;
final pages = [
Navigator(
key: Get.nestedKey(provinceSupervisorActionKey),
onGenerateRoute: (settings) {
final page = ProvinceSupervisorPages.pages.firstWhere(
(e) => e.name == settings.name,
orElse: () => ProvinceSupervisorPages.pages.firstWhere(
(e) => e.name == ProvinceSupervisorRoutes.homeProvinceSupervisor,
),
);
return buildRouteFromGetPage(page);
},
),
ProfilePage(),
];
@override
void onInit() {
super.onInit();
provinceSupervisorRepository = diChicken
.get<ProvinceSupervisorRepository>();
}
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;
}
void popBackTaped() async {
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,57 @@
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 ProvinceSupervisorRootPage extends GetView<ProvinceSupervisorRootLogic> {
const ProvinceSupervisorRootPage({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.homeSvg.path,
isSelected: controller.currentPage.value == 0,
onTap: () {
Get.nestedKey(
provinceSupervisorActionKey,
)?.currentState?.popUntil((route) => route.isFirst);
controller.changePage(0);
},
),
RBottomNavigationItem(
label: 'پروفایل',
icon: Assets.vec.profileCircleSvg.path,
isSelected: controller.currentPage.value == 1,
onTap: () {
Get.nestedKey(
provinceSupervisorActionKey,
)?.currentState?.popUntil((route) => route.isFirst);
controller.changePage(1);
},
),
],
),
),
],
);
}, controller.currentPage),
);
}
}

View File

@@ -0,0 +1,64 @@
import 'package:rasadyar_chicken/features/province_supervisor/presentation/pages/home/logic.dart';
import 'package:rasadyar_chicken/features/province_supervisor/presentation/pages/home/view.dart';
import 'package:rasadyar_chicken/features/province_supervisor/presentation/pages/root/logic.dart';
import 'package:rasadyar_chicken/features/province_supervisor/presentation/pages/root/view.dart';
import 'package:rasadyar_chicken/features/province_supervisor/presentation/pages/new_inspection/logic.dart';
import 'package:rasadyar_chicken/features/province_supervisor/presentation/pages/new_inspection/view.dart';
import 'package:rasadyar_chicken/features/province_supervisor/presentation/pages/active_hatching/logic.dart';
import 'package:rasadyar_chicken/features/province_supervisor/presentation/pages/active_hatching/view.dart';
import 'package:rasadyar_chicken/features/province_supervisor/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/presentation/routes/global_binding.dart';
import 'package:rasadyar_chicken/presentation/widget/base_page/logic.dart';
import 'package:rasadyar_core/core.dart';
class ProvinceSupervisorPages {
ProvinceSupervisorPages._();
static List<GetPage> get pages => [
GetPage(
name: ProvinceSupervisorRoutes.initProvinceSupervisor,
page: () => ProvinceSupervisorRootPage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => ChickenBaseLogic(), fenix: true);
Get.lazyPut(() => ProvinceSupervisorRootLogic());
Get.lazyPut(() => ProvinceSupervisorHomeLogic());
}),
],
),
GetPage(
name: ProvinceSupervisorRoutes.homeProvinceSupervisor,
page: () => ProvinceSupervisorHomePage(),
middlewares: [AuthMiddleware()],
binding: BindingsBuilder(() {
Get.put(ProvinceSupervisorHomeLogic());
Get.lazyPut(() => ChickenBaseLogic());
}),
),
GetPage(
name: ProvinceSupervisorRoutes.activeHatchingProvinceSupervisor,
page: () => ActiveHatchingPage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => ActiveHatchingLogic());
}),
],
),
GetPage(
name: ProvinceSupervisorRoutes.newInspectionProvinceSupervisor,
page: () => NewInspectionPage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => NewInspectionLogic());
}),
],
),
];
}

View File

@@ -0,0 +1,10 @@
sealed class ProvinceSupervisorRoutes {
ProvinceSupervisorRoutes._();
static const _base = '/chicken/provinceSupervisor';
static const initProvinceSupervisor = '$_base/';
static const homeProvinceSupervisor = '$_base/home';
static const actionProvinceSupervisor = '$_base/action';
static const activeHatchingProvinceSupervisor = '$_base/activeHatching';
static const newInspectionProvinceSupervisor = '$_base/newInspection';
}

View File

@@ -0,0 +1,3 @@
export 'data/di/province_supervisor_di.dart';
export 'presentation/routes/routes.dart';
export 'presentation/routes/pages.dart';

View File

@@ -0,0 +1,15 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_core/core.dart';
abstract class SuperAdminRemoteDataSource {
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
});
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
});
}

View File

@@ -0,0 +1,39 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_chicken/features/super_admin/data/datasources/remote/super_admin_remote_data_source.dart';
import 'package:rasadyar_core/core.dart';
class SuperAdminRemoteDataSourceImpl implements SuperAdminRemoteDataSource {
final DioRemote _httpClient;
SuperAdminRemoteDataSourceImpl(this._httpClient);
@override
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
}) async {
var res = await _httpClient.get(
'/poultry_science_report/',
headers: {'Authorization': 'Bearer $token'},
queryParameters: queryParameters,
fromJson: (json) => PaginationModel<PoultryScienceReport>.fromJson(
json,
(json) => PoultryScienceReport.fromJson(json as Map<String, dynamic>),
),
);
return res.data;
}
@override
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
}) async {
await _httpClient.post(
'/poultry_science_report/',
headers: {'Authorization': 'Bearer $token'},
data: request.toJson(),
);
}
}

View File

@@ -0,0 +1,36 @@
import 'package:rasadyar_chicken/features/super_admin/data/datasources/remote/super_admin_remote_data_source.dart';
import 'package:rasadyar_chicken/features/super_admin/data/datasources/remote/super_admin_remote_data_source_impl.dart';
import 'package:rasadyar_chicken/features/super_admin/data/repositories/super_admin_repository.dart';
import 'package:rasadyar_chicken/features/super_admin/data/repositories/super_admin_repository_impl.dart';
import 'package:rasadyar_core/core.dart';
/// Setup dependency injection for super_admin feature
Future<void> setupSuperAdminDI(GetIt di, DioRemote dioRemote) async {
di.registerLazySingleton<SuperAdminRemoteDataSource>(
() => SuperAdminRemoteDataSourceImpl(dioRemote),
);
di.registerLazySingleton<SuperAdminRepository>(
() => SuperAdminRepositoryImpl(di.get<SuperAdminRemoteDataSource>()),
);
}
/// Re-register super_admin dependencies (used when base URL changes)
Future<void> reRegisterSuperAdminDI(GetIt di, DioRemote dioRemote) async {
await reRegister(di, () => SuperAdminRemoteDataSourceImpl(dioRemote));
await reRegister(
di,
() => SuperAdminRepositoryImpl(di.get<SuperAdminRemoteDataSource>()),
);
}
/// Helper function to re-register a dependency
Future<void> reRegister<T extends Object>(
GetIt di,
T Function() factory,
) async {
if (di.isRegistered<T>()) {
await di.unregister<T>();
}
di.registerLazySingleton<T>(factory);
}

View File

@@ -0,0 +1,15 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_core/core.dart';
abstract class SuperAdminRepository {
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
});
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
});
}

View File

@@ -0,0 +1,30 @@
import 'package:rasadyar_chicken/features/poultry_science/data/model/response/poultry_science_report/poultry_science_report.dart';
import 'package:rasadyar_chicken/features/poultry_science/data/model/request/submit_inspection/submit_inspection_response.dart';
import 'package:rasadyar_chicken/features/super_admin/data/datasources/remote/super_admin_remote_data_source.dart';
import 'package:rasadyar_chicken/features/super_admin/data/repositories/super_admin_repository.dart';
import 'package:rasadyar_core/core.dart';
class SuperAdminRepositoryImpl implements SuperAdminRepository {
final SuperAdminRemoteDataSource _remote;
SuperAdminRepositoryImpl(this._remote);
@override
Future<PaginationModel<PoultryScienceReport>?> getSubmitInspectionList({
required String token,
Map<String, dynamic>? queryParameters,
}) async {
return await _remote.getSubmitInspectionList(
token: token,
queryParameters: queryParameters,
);
}
@override
Future<void> submitInspection({
required String token,
required SubmitInspectionResponse request,
}) async {
return await _remote.submitInspection(token: token, request: request);
}
}

View File

@@ -0,0 +1,95 @@
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/super_admin/presentation/pages/root/logic.dart';
import 'package:rasadyar_core/core.dart';
class ActiveHatchingLogic extends GetxController {
SuperAdminRootLogic rootLogic = Get.find<SuperAdminRootLogic>();
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: 'SuperAdmin',
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/super_admin/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: superAdminActionKey,
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,26 @@
import 'package:rasadyar_chicken/features/super_admin/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/presentation/routes/routes.dart';
import 'package:rasadyar_core/core.dart';
class SuperAdminHomeItem {
final String title;
final String route;
final String icon;
SuperAdminHomeItem({required this.title, required this.route, required this.icon});
}
class SuperAdminHomeLogic extends GetxController {
RxList<SuperAdminHomeItem> items = [
SuperAdminHomeItem(
title: "جوجه ریزی فعال",
route: SuperAdminRoutes.activeHatchingSuperAdmin,
icon: Assets.vec.activeFramSvg.path,
),
SuperAdminHomeItem(
title: "بازرسی مزارع طیور",
route: SuperAdminRoutes.newInspectionSuperAdmin,
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 SuperAdminHomePage extends GetView<SuperAdminHomeLogic> {
SuperAdminHomePage({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: superAdminActionKey);
},
);
},
);
}, controller.items);
}
}

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/super_admin/presentation/pages/root/logic.dart';
import 'package:rasadyar_core/core.dart';
class NewInspectionLogic extends GetxController {
BaseLogic baseLogic = Get.find<BaseLogic>();
Rx<Resource<PaginationModel<PoultryScienceReport>>> submitInspectionList =
Resource<PaginationModel<PoultryScienceReport>>.loading().obs;
SuperAdminRootLogic rootLogic = Get.find<SuperAdminRootLogic>();
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.superAdminRepository.getSubmitInspectionList(
token: rootLogic.tokenService.accessToken.value!,
queryParameters: buildQueryParams(
role: 'SuperAdmin',
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,84 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:rasadyar_chicken/data/di/chicken_di.dart';
import 'package:rasadyar_chicken/features/super_admin/data/repositories/super_admin_repository.dart';
import 'package:rasadyar_chicken/features/common/presentation/page/profile/view.dart';
import 'package:rasadyar_chicken/features/super_admin/presentation/routes/pages.dart';
import 'package:rasadyar_chicken/features/super_admin/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 SuperAdminRootLogic extends GetxController {
var tokenService = Get.find<TokenStorageService>();
late SuperAdminRepository superAdminRepository;
RxList<ErrorLocationType> errorLocationType = RxList();
RxMap<int, dynamic> homeExpandedList = RxMap();
DateTime? _lastBackPressed;
RxInt currentPage = 0.obs;
final pages = [
Navigator(
key: Get.nestedKey(superAdminActionKey),
onGenerateRoute: (settings) {
final page = SuperAdminPages.pages.firstWhere(
(e) => e.name == settings.name,
orElse: () => SuperAdminPages.pages.firstWhere(
(e) => e.name == SuperAdminRoutes.homeSuperAdmin,
),
);
return buildRouteFromGetPage(page);
},
),
ProfilePage(),
];
@override
void onInit() {
super.onInit();
superAdminRepository = diChicken.get<SuperAdminRepository>();
}
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;
}
void popBackTaped() async {
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,58 @@
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 SuperAdminRootPage extends GetView<SuperAdminRootLogic> {
const SuperAdminRootPage({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.homeSvg.path,
isSelected: controller.currentPage.value == 0,
onTap: () {
Get.nestedKey(
superAdminActionKey,
)?.currentState?.popUntil((route) => route.isFirst);
controller.changePage(0);
},
),
RBottomNavigationItem(
label: 'پروفایل',
icon: Assets.vec.profileCircleSvg.path,
isSelected: controller.currentPage.value == 1,
onTap: () {
Get.nestedKey(
superAdminActionKey,
)?.currentState?.popUntil((route) => route.isFirst);
controller.changePage(1);
},
),
],
),
),
],
);
}, controller.currentPage),
);
}
}

View File

@@ -0,0 +1,74 @@
import 'package:rasadyar_chicken/features/super_admin/presentation/pages/home/logic.dart';
import 'package:rasadyar_chicken/features/super_admin/presentation/pages/home/view.dart';
import 'package:rasadyar_chicken/features/super_admin/presentation/pages/root/logic.dart';
import 'package:rasadyar_chicken/features/super_admin/presentation/pages/root/view.dart';
import 'package:rasadyar_chicken/features/super_admin/presentation/pages/active_hatching/logic.dart';
import 'package:rasadyar_chicken/features/super_admin/presentation/pages/active_hatching/view.dart';
import 'package:rasadyar_chicken/features/super_admin/presentation/pages/new_inspection/logic.dart';
import 'package:rasadyar_chicken/features/super_admin/presentation/pages/new_inspection/view.dart';
import 'package:rasadyar_chicken/features/super_admin/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/presentation/routes/global_binding.dart';
import 'package:rasadyar_chicken/presentation/widget/base_page/logic.dart';
import 'package:rasadyar_core/core.dart';
class SuperAdminPages {
SuperAdminPages._();
static List<GetPage> get pages => [
GetPage(
name: SuperAdminRoutes.initSuperAdmin,
page: () => SuperAdminRootPage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => ChickenBaseLogic(), fenix: true);
Get.lazyPut(() => SuperAdminRootLogic());
Get.lazyPut(() => SuperAdminHomeLogic());
}),
],
),
GetPage(
name: SuperAdminRoutes.homeSuperAdmin,
page: () => SuperAdminHomePage(),
middlewares: [AuthMiddleware()],
binding: BindingsBuilder(() {
Get.put(SuperAdminHomeLogic());
Get.lazyPut(() => ChickenBaseLogic());
}),
),
GetPage(
name: SuperAdminRoutes.actionSuperAdmin,
page: () => SuperAdminHomePage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => SuperAdminHomeLogic());
}),
],
),
GetPage(
name: SuperAdminRoutes.activeHatchingSuperAdmin,
page: () => ActiveHatchingPage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => ActiveHatchingLogic());
}),
],
),
GetPage(
name: SuperAdminRoutes.newInspectionSuperAdmin,
page: () => NewInspectionPage(),
middlewares: [AuthMiddleware()],
bindings: [
GlobalBinding(),
BindingsBuilder(() {
Get.lazyPut(() => NewInspectionLogic());
}),
],
),
];
}

Some files were not shown because too many files have changed in this diff Show More