fix : pagination list and load

more and refresh
This commit is contained in:
2025-07-08 11:56:38 +03:30
parent bbaeb6c2a5
commit 8b656d4915
7 changed files with 212 additions and 124 deletions

View File

@@ -15,7 +15,8 @@ class BuyInProvinceWaitingLogic extends GetxController {
RxnString searchedValue = RxnString(); RxnString searchedValue = RxnString();
RxMap<String, bool> isLoadingConfirmMap = RxMap(); RxMap<String, bool> isLoadingConfirmMap = RxMap();
Rx<Color> bgConfirmAllColor = AppColor.blueNormal.obs; Rx<Color> bgConfirmAllColor = AppColor.blueNormal.obs;
RxInt currentPage = 1.obs;
final RxBool isLoadingMoreAllocationsMade = false.obs;
RootLogic rootLogic = Get.find<RootLogic>(); RootLogic rootLogic = Get.find<RootLogic>();
Rx<Resource<PaginationModel<WaitingArrivalModel>>> waitingProduct = Rx<Resource<PaginationModel<WaitingArrivalModel>>> waitingProduct =
Resource<PaginationModel<WaitingArrivalModel>>.loading().obs; Resource<PaginationModel<WaitingArrivalModel>>.loading().obs;
@@ -45,14 +46,26 @@ class BuyInProvinceWaitingLogic extends GetxController {
searchedValue.value = data?.trim(); searchedValue.value = data?.trim();
} }
Future<void> getWaitingArrivals() async { Future<void> getWaitingArrivals([bool isLoadingMore = false]) async {
if (isLoadingMore) {
isLoadingMoreAllocationsMade.value = true;
} else {
waitingProduct.value = Resource<PaginationModel<WaitingArrivalModel>>.loading();
}
if (searchedValue.value != null &&
searchedValue.value!.trim().isNotEmpty &&
currentPage.value > 1) {
currentPage.value = 1; // Reset to first page if search value is set
}
safeCall( safeCall(
call: () async => await rootLogic.chickenRepository.getWaitingArrivals( call: () async => await rootLogic.chickenRepository.getWaitingArrivals(
token: rootLogic.tokenService.accessToken.value!, token: rootLogic.tokenService.accessToken.value!,
queryParameters: buildQueryParams( queryParameters: buildQueryParams(
queryParams: {'type': 'not_entered'}, queryParams: {'type': 'not_entered'},
pageSize: 10, pageSize: 20,
page: 1, page: currentPage.value,
search: 'filter', search: 'filter',
role: 'Steward', role: 'Steward',
value: searchedValue.value, value: searchedValue.value,

View File

@@ -35,7 +35,11 @@ class BuyInProvinceWaitingPage extends GetView<BuyInProvinceWaitingLogic> {
}, },
itemCount: data.value.data?.results?.length ?? 0, itemCount: data.value.data?.results?.length ?? 0,
separatorBuilder: (context, index) => SizedBox(height: 8.h), separatorBuilder: (context, index) => SizedBox(height: 8.h),
onLoadMore: () async {}, onLoadMore: () async => controller.getWaitingArrivals(true),
onRefresh: () async {
controller.currentPage.value = 1;
await controller.getWaitingArrivals();
},
); );
}, controller.waitingProduct), }, controller.waitingProduct),
), ),

View File

@@ -7,14 +7,16 @@ import 'package:rasadyar_chicken/data/models/response/steward_free_bar/steward_f
import 'package:rasadyar_chicken/presentation/pages/buy/logic.dart'; import 'package:rasadyar_chicken/presentation/pages/buy/logic.dart';
import 'package:rasadyar_chicken/presentation/pages/root/logic.dart'; import 'package:rasadyar_chicken/presentation/pages/root/logic.dart';
import 'package:rasadyar_chicken/presentation/pages/sale/logic.dart'; import 'package:rasadyar_chicken/presentation/pages/sale/logic.dart';
import 'package:rasadyar_chicken/presentation/utils/utils.dart';
import 'package:rasadyar_core/core.dart'; import 'package:rasadyar_core/core.dart';
class BuyOutOfProvinceLogic extends GetxController { class BuyOutOfProvinceLogic extends GetxController {
late List<String> routesName;
RxBool isExpanded = false.obs; RxBool isExpanded = false.obs;
RxBool isSubmitButtonEnabled = false.obs; RxBool isSubmitButtonEnabled = false.obs;
RxList<int> isExpandedList = <int>[].obs; RxList<int> isExpandedList = <int>[].obs;
final RxInt currentPage = 1.obs;
late List<String> routesName; final RxBool isLoadingMoreAllocationsMade = false.obs;
//TODO add this to Di //TODO add this to Di
ImagePicker imagePicker = ImagePicker(); ImagePicker imagePicker = ImagePicker();
@@ -61,6 +63,12 @@ class BuyOutOfProvinceLogic extends GetxController {
}); });
setupListeners(); setupListeners();
debounce(
searchedValue,
(callback) => getStewardPurchaseOutOfProvince(),
time: Duration(milliseconds: timeDebounce),
);
} }
@override @override
@@ -75,17 +83,26 @@ class BuyOutOfProvinceLogic extends GetxController {
void setSearchValue(String? data) { void setSearchValue(String? data) {
searchedValue.value = data?.trim(); searchedValue.value = data?.trim();
getStewardPurchaseOutOfProvince();
} }
Future<void> getStewardPurchaseOutOfProvince() async { Future<void> getStewardPurchaseOutOfProvince([bool isLoadingMore = false]) async {
purchaseOutOfProvinceList.value = Resource<PaginationModel<StewardFreeBar>>.loading(); if (isLoadingMore) {
isLoadingMoreAllocationsMade.value = true;
} else {
purchaseOutOfProvinceList.value = Resource<PaginationModel<StewardFreeBar>>.loading();
}
if (searchedValue.value != null &&
searchedValue.value!.trim().isNotEmpty &&
currentPage.value > 1) {
currentPage.value = 1; // Reset to first page if search value is set
}
await safeCall( await safeCall(
call: () => rootLogic.chickenRepository.getStewardPurchasesOutSideOfTheProvince( call: () => rootLogic.chickenRepository.getStewardPurchasesOutSideOfTheProvince(
token: rootLogic.tokenService.accessToken.value!, token: rootLogic.tokenService.accessToken.value!,
queryParameters: buildQueryParams( queryParameters: buildQueryParams(
pageSize: 10, pageSize: 10,
page: 1, page: currentPage.value,
search: 'filter', search: 'filter',
role: 'Steward', role: 'Steward',
value: searchedValue.value, value: searchedValue.value,

View File

@@ -47,7 +47,11 @@ class BuyOutOfProvincePage extends GetView<BuyOutOfProvinceLogic> {
}, },
itemCount: data.value.data?.results?.length ?? 0, itemCount: data.value.data?.results?.length ?? 0,
separatorBuilder: (context, index) => SizedBox(height: 8.h), separatorBuilder: (context, index) => SizedBox(height: 8.h),
onLoadMore: () async {}, onLoadMore: () async => controller.getStewardPurchaseOutOfProvince(true),
onRefresh: () async {
controller.currentPage.value = 1;
await controller.getStewardPurchaseOutOfProvince();
},
); );
}, controller.purchaseOutOfProvinceList), }, controller.purchaseOutOfProvinceList),
), ),
@@ -561,10 +565,7 @@ class BuyOutOfProvincePage extends GetView<BuyOutOfProvinceLogic> {
width: Get.width, width: Get.width,
height: 39, height: 39,
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4), margin: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration( decoration: BoxDecoration(color: AppColor.greenLight, borderRadius: BorderRadius.circular(8)),
color: AppColor.greenLight,
borderRadius: BorderRadius.circular(8),
),
alignment: Alignment.center, alignment: Alignment.center,
child: ObxValue((data) { child: ObxValue((data) {
return Text( return Text(
@@ -574,4 +575,5 @@ class BuyOutOfProvincePage extends GetView<BuyOutOfProvinceLogic> {
}, controller.rootLogic.inventoryModel), }, controller.rootLogic.inventoryModel),
); );
} }
} }

View File

@@ -7,8 +7,7 @@ import 'package:rasadyar_core/core.dart';
import 'logic.dart'; import 'logic.dart';
class SalesOutOfProvinceBuyersPage class SalesOutOfProvinceBuyersPage extends GetView<SalesOutOfProvinceBuyersLogic> {
extends GetView<SalesOutOfProvinceBuyersLogic> {
const SalesOutOfProvinceBuyersPage({super.key}); const SalesOutOfProvinceBuyersPage({super.key});
@override @override
@@ -44,10 +43,7 @@ class SalesOutOfProvinceBuyersPage
}, controller.buyerList), }, controller.buyerList),
floatingActionButton: RFab.add( floatingActionButton: RFab.add(
onPressed: () { onPressed: () {
Get.bottomSheet( Get.bottomSheet(addOrEditBuyerBottomSheet(), isScrollControlled: true);
addOrEditBuyerBottomSheet(),
isScrollControlled: true,
);
}, },
), ),
floatingActionButtonLocation: FloatingActionButtonLocation.startFloat, floatingActionButtonLocation: FloatingActionButtonLocation.startFloat,
@@ -61,55 +57,82 @@ class SalesOutOfProvinceBuyersPage
child: Form( child: Form(
key: controller.formKey, key: controller.formKey,
child: Column( child: Column(
spacing: 16, spacing: 8,
children: [ children: [
Text( Text(
isOnEdit ? 'ویرایش خریدار' : 'افزودن خریدار', isOnEdit ? 'ویرایش خریدار' : 'افزودن خریدار',
style: AppFonts.yekan16Bold.copyWith( style: AppFonts.yekan16Bold.copyWith(color: AppColor.darkGreyDarkHover),
color: AppColor.darkGreyDarkHover, ),
Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: AppColor.darkGreyLight, width: 1),
),
child: Column(spacing: 12, children: [_provinceWidget(), _cityWidget()]),
),
Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: AppColor.darkGreyLight, width: 1),
),
child: Column(
spacing: 12,
children: [
RTextField(
controller: controller.buyerNameController,
label: 'نام خریدار',
borderColor: AppColor.darkGreyLight,
filledColor: AppColor.bgLight,
filled: true,
),
RTextField(
controller: controller.buyerLastNameController,
label: 'نام خانوادگی خریدار',
borderColor: AppColor.darkGreyLight,
filledColor: AppColor.bgLight,
filled: true,
),
RTextField(
controller: controller.buyerPhoneController,
label: 'تلفن خریدار',
keyboardType: TextInputType.phone,
borderColor: AppColor.darkGreyLight,
filledColor: AppColor.bgLight,
filled: true,
maxLength: 11,
validator: (value) {
if (value == null || value.isEmpty) {
return 'لطفاً شماره موبایل را وارد کنید';
}
// حذف کاماها برای اعتبارسنجی
String cleaned = value.replaceAll(',', '');
if (cleaned.length != 11) {
return 'شماره موبایل باید ۱۱ رقم باشد';
}
if (!cleaned.startsWith('09')) {
return 'شماره موبایل باید با 09 شروع شود';
}
return null;
},
),
RTextField(
controller: controller.buyerUnitNameController,
label: 'نام واحد',
borderColor: AppColor.darkGreyLight,
filledColor: AppColor.bgLight,
filled: true,
),
submitButtonWidget(isOnEdit),
],
), ),
), ),
RTextField(
controller: controller.buyerPhoneController,
label: 'تلفن خریدار',
keyboardType: TextInputType.phone,
borderColor: AppColor.darkGreyLight,
maxLength: 11,
validator: (value) {
if (value == null || value.isEmpty) {
return 'لطفاً شماره موبایل را وارد کنید';
}
// حذف کاماها برای اعتبارسنجی
String cleaned = value.replaceAll(',', '');
if (cleaned.length != 11) {
return 'شماره موبایل باید ۱۱ رقم باشد';
}
if (!cleaned.startsWith('09')) {
return 'شماره موبایل باید با 09 شروع شود';
}
return null;
},
),
RTextField(
controller: controller.buyerNameController,
label: 'نام خریدار',
borderColor: AppColor.darkGreyLight,
),
RTextField(
controller: controller.buyerLastNameController,
label: 'نام خانوادگی خریدار',
borderColor: AppColor.darkGreyLight,
),
RTextField(
controller: controller.buyerUnitNameController,
label: 'نام واحد',
borderColor: AppColor.darkGreyLight,
),
_provinceWidget(),
_cityWidget(),
submitButtonWidget(isOnEdit),
SizedBox(), SizedBox(),
], ],
), ),
@@ -139,7 +162,7 @@ class SalesOutOfProvinceBuyersPage
Widget _provinceWidget() { Widget _provinceWidget() {
return Obx(() { return Obx(() {
return OverlayDropdownWidget2<IranProvinceCityModel>( return OverlayDropdownWidget<IranProvinceCityModel>(
items: controller.rootLogic.provinces, items: controller.rootLogic.provinces,
onChanged: (value) { onChanged: (value) {
controller.selectedProvince.value = value; controller.selectedProvince.value = value;
@@ -217,17 +240,13 @@ class SalesOutOfProvinceBuyersPage
return Container( return Container(
padding: EdgeInsets.fromLTRB(8, 12, 14, 12), padding: EdgeInsets.fromLTRB(8, 12, 14, 12),
decoration: BoxDecoration( decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(8)),
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Column( child: Column(
spacing: 8, spacing: 8,
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
Text( Text(
'${item.province}-${item.city}', '${item.province}-${item.city}',
textAlign: TextAlign.center, textAlign: TextAlign.center,
@@ -244,10 +263,7 @@ class SalesOutOfProvinceBuyersPage
title: 'تعداد درخواست ها', title: 'تعداد درخواست ها',
value: '${item.requestsInfo?.numberOfRequests.separatedByComma}', value: '${item.requestsInfo?.numberOfRequests.separatedByComma}',
), ),
buildRow( buildRow(title: 'وزن', value: '${item.requestsInfo?.totalWeight.separatedByComma}'),
title: 'وزن',
value: '${item.requestsInfo?.totalWeight.separatedByComma}',
),
Row( Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,

View File

@@ -56,6 +56,7 @@ class SalesOutOfProvinceSalesListLogic extends GetxController {
void onReady() { void onReady() {
super.onReady(); super.onReady();
selectedProduct.value = saleLogic.rolesProductsModel.first;
debounce( debounce(
searchedValue, searchedValue,
(callback) => getOutProvinceSales(), (callback) => getOutProvinceSales(),

View File

@@ -211,7 +211,7 @@ class SalesOutOfProvinceSalesListPage
Widget addOrEditSaleBottomSheet([bool isOnEdit = false]) { Widget addOrEditSaleBottomSheet([bool isOnEdit = false]) {
return BaseBottomSheet( return BaseBottomSheet(
height: 600, height: 500.h,
child: SingleChildScrollView( child: SingleChildScrollView(
child: Form( child: Form(
key: controller.formKey, key: controller.formKey,
@@ -224,51 +224,67 @@ class SalesOutOfProvinceSalesListPage
color: AppColor.darkGreyDarkHover, color: AppColor.darkGreyDarkHover,
), ),
), ),
_productWidget(), _productDropDown(),
_buyerWidget(),
RTextField(
controller: controller.saleWeightController,
label: 'وزن لاشه',
keyboardType: TextInputType.number,
borderColor: AppColor.darkGreyLight,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
validator: (value) { Container(
if (value == null) { padding: EdgeInsets.all(8),
return 'لطفاً وزن لاشه را وارد کنید'; decoration: BoxDecoration(
} color: Colors.white,
return null; borderRadius: BorderRadius.circular(8),
}, border: Border.all(color: AppColor.darkGreyLight, width: 1),
), ),
RTextField( child: Column(
controller: controller.quarantineCodeController, spacing: 12,
label: 'کد قرنطینه', children: [
borderColor: AppColor.darkGreyLight, Row(
validator: (value) { spacing: 8,
if (value == null) { children: [
return 'لطفاً کد قرنطینه را وارد کنید'; Expanded(
} child: timeFilterWidget(
return null; date: controller.saleDate,
}, onChanged: (jalali) => controller.saleDate.value = jalali,
), ),
),
Row( ],
spacing: 8,
children: [
Expanded(
child: timeFilterWidget(
date: controller.saleDate,
onChanged: (jalali) => controller.saleDate.value = jalali,
), ),
), _buyerWidget(),
], RTextField(
), controller: controller.saleWeightController,
label: 'وزن لاشه',
keyboardType: TextInputType.number,
borderColor: AppColor.darkGreyLight,
filledColor: AppColor.bgLight,
filled: true,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
SeparatorInputFormatter(),
],
submitButtonWidget(isOnEdit), validator: (value) {
if (value == null) {
return 'لطفاً وزن لاشه را وارد کنید';
}
return null;
},
),
RTextField(
controller: controller.quarantineCodeController,
label: 'کد قرنطینه',
borderColor: AppColor.darkGreyLight,
filledColor: AppColor.bgLight,
filled: true,
validator: (value) {
if (value == null) {
return 'لطفاً کد قرنطینه را وارد کنید';
}
return null;
},
),
submitButtonWidget(isOnEdit),
],
),
),
SizedBox(), SizedBox(),
], ],
), ),
@@ -314,21 +330,39 @@ class SalesOutOfProvinceSalesListPage
}); });
} }
Widget _productWidget() {
return ObxValue((data) { Widget _productDropDown() {
return Obx(() {
return OverlayDropdownWidget<ProductModel>( return OverlayDropdownWidget<ProductModel>(
items: controller.saleLogic.rolesProductsModel, items: controller.saleLogic.rolesProductsModel,
height: 56,
hasDropIcon: false,
background: Colors.white,
onChanged: (value) { onChanged: (value) {
controller.selectedProduct.value = value; controller.selectedProduct.value = value;
}, },
selectedItem: controller.selectedProduct.value, selectedItem: controller.selectedProduct.value,
initialValue: controller.selectedProduct.value, initialValue: controller.selectedProduct.value,
itemBuilder: (item) => Text(item.name ?? 'بدون نام'), itemBuilder: (item) => Text(item.name ?? 'بدون نام'),
labelBuilder: (item) => Text(item?.name ?? 'انتخاب محصول'), labelBuilder: (item) => Row(
spacing: 8,
children: [
(item?.name?.contains('مرغ گرم') ?? false)
? Assets.images.chicken.image(width: 40, height: 40)
: Assets.vec.placeHolderSvg.svg(width: 40, height: 40),
Text(item?.name ?? 'انتخاب محصول'),
Spacer(),
Text(
'موجودی:${controller.rootLogic.inventoryModel.value?.totalRemainWeight.separatedByComma ?? 0}',
),
],
),
); );
}, controller.selectedProduct); });
} }
GestureDetector timeFilterWidget({ GestureDetector timeFilterWidget({
isFrom = true, isFrom = true,
required Rx<Jalali> date, required Rx<Jalali> date,
@@ -339,10 +373,11 @@ class SalesOutOfProvinceSalesListPage
Get.bottomSheet(modalDatePicker((value) => onChanged(value))); Get.bottomSheet(modalDatePicker((value) => onChanged(value)));
}, },
child: Container( child: Container(
height: 35, height: 40,
decoration: BoxDecoration( decoration: BoxDecoration(
color:AppColor.bgLight,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
border: Border.all(width: 1, color: AppColor.bgDark), border: Border.all(width: 1, color: AppColor.darkGreyLight),
), ),
padding: EdgeInsets.symmetric(horizontal: 11, vertical: 4), padding: EdgeInsets.symmetric(horizontal: 11, vertical: 4),
child: Row( child: Row(