refactor: chicken module for other role's
feat: steward Role chore: add some rive file and library
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
class BuyLogic extends GetxController {
|
||||
|
||||
List<String> routesName = ['خرید'];
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
// TODO: implement onReady
|
||||
super.onReady();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
// TODO: implement onClose
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_chicken/presentation/routes/routes.dart';
|
||||
import 'package:rasadyar_chicken/presentation/widget/base_page/view.dart';
|
||||
import 'package:rasadyar_chicken/presentation/widget/sale_buy_card_item.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
import 'logic.dart';
|
||||
|
||||
class BuyPage extends GetView<BuyLogic> {
|
||||
const BuyPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BasePage(
|
||||
routes: controller.routesName,
|
||||
isBase: true,
|
||||
widgets: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Wrap(
|
||||
alignment: WrapAlignment.center,
|
||||
spacing: 14.w,
|
||||
children: [
|
||||
saleOrBuyItemCard(
|
||||
title: 'خرید داخل استان',
|
||||
iconPath: Assets.vec.cubeSvg.path,
|
||||
color: AppColor.greenNormalActive,
|
||||
onTap: () {
|
||||
Get.toNamed(ChickenRoutes.buysInProvinceSteward, id: 0);
|
||||
},
|
||||
),
|
||||
saleOrBuyItemCard(
|
||||
title: 'خرید خارج استان',
|
||||
iconPath: Assets.vec.truckFastSvg.path,
|
||||
color: AppColor.greenNormalActive,
|
||||
onTap: () {
|
||||
Get.toNamed(ChickenRoutes.buysOutOfProvinceSteward, id: 0);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/buy/logic.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/buy_in_province_all/logic.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/buy_in_province_waiting/logic.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/root/logic.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
class BuyInProvinceLogic extends GetxController {
|
||||
RxList<String> routesName = RxList();
|
||||
RxList<int> isExpandedList = <int>[].obs;
|
||||
RxnString searchedValue = RxnString();
|
||||
Rx<Jalali> fromDateFilter = Jalali.now().obs;
|
||||
Rx<Jalali> toDateFilter = Jalali.now().obs;
|
||||
|
||||
StewardRootLogic get rootLogic => Get.find<StewardRootLogic>();
|
||||
|
||||
BuyLogic get buyLogic => Get.find<BuyLogic>();
|
||||
RxInt selectedSegmentIndex = 0.obs;
|
||||
|
||||
BuyInProvinceAllLogic buyAllLogic = Get.find<BuyInProvinceAllLogic>();
|
||||
BuyInProvinceWaitingLogic buyWaitingLogic = Get.find<BuyInProvinceWaitingLogic>();
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
routesName.value = [...buyLogic.routesName, 'داخل استان'].toList();
|
||||
routesName.add(selectedSegmentIndex.value == 0 ? 'در انتظار' : 'همه');
|
||||
ever(selectedSegmentIndex, (callback) {
|
||||
routesName.removeLast();
|
||||
routesName.add(callback == 0 ? 'در انتظار' : 'همه');
|
||||
});
|
||||
|
||||
ever(fromDateFilter, (callback) => _setFromDateFilter(callback));
|
||||
ever(toDateFilter, (callback) => _setToDateFilter(callback));
|
||||
}
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
// TODO: implement onReady
|
||||
super.onReady();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
// TODO: implement onClose
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
void _setFromDateFilter(Jalali jalali) {
|
||||
final isWaiting = selectedSegmentIndex.value == 0;
|
||||
if (isWaiting) {
|
||||
buyWaitingLogic.fromDateFilter.value = fromDateFilter.value;
|
||||
} else {
|
||||
buyAllLogic.fromDateFilter.value = fromDateFilter.value;
|
||||
}
|
||||
}
|
||||
|
||||
void _setToDateFilter(Jalali jalali) {
|
||||
final isWaiting = selectedSegmentIndex.value == 0;
|
||||
if (isWaiting) {
|
||||
buyWaitingLogic.toDateFilter.value = fromDateFilter.value;
|
||||
} else {
|
||||
buyAllLogic.toDateFilter.value = fromDateFilter.value;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> submitFilter() async {
|
||||
final isWaiting = selectedSegmentIndex.value == 0;
|
||||
if (isWaiting) {
|
||||
buyWaitingLogic.getWaitingArrivals();
|
||||
} else {
|
||||
buyAllLogic.getAllArrivals();
|
||||
}
|
||||
}
|
||||
|
||||
void setSearchValue(String? data) {
|
||||
searchedValue.value = data?.trim();
|
||||
final isWaiting = selectedSegmentIndex.value == 0;
|
||||
if (isWaiting) {
|
||||
buyWaitingLogic.searchedValue.value = searchedValue.value;
|
||||
} else {
|
||||
buyAllLogic.searchedValue.value = searchedValue.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/buy_in_province_all/view.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/buy_in_province_waiting/view.dart';
|
||||
import 'package:rasadyar_chicken/presentation/widget/base_page/view.dart';
|
||||
import 'package:rasadyar_chicken/presentation/widget/steward/inventory_widget.dart';
|
||||
import 'package:rasadyar_chicken/presentation/widget/page_route.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
import 'logic.dart';
|
||||
|
||||
class BuyInProvincePage extends GetView<BuyInProvinceLogic> {
|
||||
const BuyInProvincePage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BasePage(
|
||||
routesWidget: ObxValue((route) => buildPageRoute(route), controller.routesName),
|
||||
onBackPressed: () => Get.back(id: 0),
|
||||
onSearchChanged: (data) => controller.setSearchValue(data),
|
||||
filteringWidget: filterBottomSheet(),
|
||||
widgets: [
|
||||
inventoryWidget(controller.rootLogic),
|
||||
segmentWidget(),
|
||||
ObxValue((index) {
|
||||
return Expanded(
|
||||
child: index.value == 0 ? BuyInProvinceWaitingPage() : BuyInProvinceAllPage(),
|
||||
);
|
||||
}, controller.selectedSegmentIndex),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Padding segmentWidget() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(8, 0, 8, 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: RSegment(
|
||||
children: ['در انتظار', 'بایگانی'],
|
||||
selectedIndex: 0,
|
||||
borderColor: const Color(0xFFB4B4B4),
|
||||
selectedBorderColor: AppColor.blueNormal,
|
||||
selectedBackgroundColor: AppColor.blueLight,
|
||||
onSegmentSelected: (index) => controller.selectedSegmentIndex.value = index,
|
||||
backgroundColor: AppColor.whiteGreyNormal,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget filterBottomSheet() {
|
||||
return BaseBottomSheet(
|
||||
height: 200,
|
||||
child: Column(
|
||||
spacing: 16,
|
||||
children: [
|
||||
Text('فیلترها', style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal)),
|
||||
Row(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Expanded(
|
||||
child: dateFilterWidget(
|
||||
date: controller.fromDateFilter,
|
||||
onChanged: (jalali) => controller.fromDateFilter.value = jalali,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: dateFilterWidget(
|
||||
isFrom: false,
|
||||
date: controller.toDateFilter,
|
||||
onChanged: (jalali) => controller.toDateFilter.value = jalali,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
RElevated(
|
||||
text: 'اعمال فیلتر',
|
||||
|
||||
isFullWidth: true,
|
||||
backgroundColor: AppColor.greenNormal,
|
||||
onPressed: () {
|
||||
controller.submitFilter();
|
||||
Get.back();
|
||||
},
|
||||
height: 40,
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
import 'package:rasadyar_chicken/data/models/request/steward_allocation/steward_allocation_request.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/waiting_arrival/waiting_arrival.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/root/logic.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
class BuyInProvinceAllLogic extends GetxController {
|
||||
RxList<int> isExpandedList = <int>[].obs;
|
||||
Rxn<Jalali> fromDateFilter = Rxn();
|
||||
Rxn<Jalali> toDateFilter = Rxn();
|
||||
RxnString searchedValue = RxnString();
|
||||
RxMap<String, bool> isLoadingConfirmMap = RxMap();
|
||||
final RxBool isLoadingMoreAllocationsMade = false.obs;
|
||||
RxInt currentPage = 1.obs;
|
||||
|
||||
StewardRootLogic rootLogic = Get.find<StewardRootLogic>();
|
||||
|
||||
Rx<Resource<PaginationModel<WaitingArrivalModel>>> allProduct =
|
||||
Resource<PaginationModel<WaitingArrivalModel>>.loading().obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
getAllArrivals();
|
||||
}
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
debounce(searchedValue, (callback) => getAllArrivals(), time: Duration(milliseconds: 2000));
|
||||
super.onReady();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
// TODO: implement onClose
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
Future<void> getAllArrivals([bool isLoadingMore = false]) async {
|
||||
if (isLoadingMore) {
|
||||
isLoadingMoreAllocationsMade.value = true;
|
||||
} else {
|
||||
allProduct.value = Resource<PaginationModel<WaitingArrivalModel>>.loading();
|
||||
}
|
||||
|
||||
if (searchedValue.value != null &&
|
||||
searchedValue.value!.trim().isNotEmpty &&
|
||||
currentPage.value > 1) {
|
||||
currentPage.value = 1;
|
||||
}
|
||||
|
||||
safeCall(
|
||||
call: () async => await rootLogic.chickenRepository.getWaitingArrivals(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
queryParameters: buildQueryParams(
|
||||
queryParams: {'type': 'all'},
|
||||
pageSize: 20,
|
||||
page: currentPage.value,
|
||||
search: 'filter',
|
||||
role: 'Steward',
|
||||
value: searchedValue.value,
|
||||
fromDate: fromDateFilter.value?.toDateTime(),
|
||||
toDate: toDateFilter.value?.toDateTime(),
|
||||
),
|
||||
),
|
||||
onSuccess: (res) async {
|
||||
await Future.delayed(Duration(milliseconds: 200));
|
||||
if ((res?.count ?? 0) == 0) {
|
||||
allProduct.value = Resource<PaginationModel<WaitingArrivalModel>>.empty();
|
||||
} else {
|
||||
allProduct.value = Resource<PaginationModel<WaitingArrivalModel>>.success(
|
||||
PaginationModel<WaitingArrivalModel>(
|
||||
count: res?.count ?? 0,
|
||||
next: res?.next,
|
||||
previous: res?.previous,
|
||||
results: [...(allProduct.value.data?.results ?? []), ...(res?.results ?? [])],
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> acceptEntries(WaitingArrivalModel model) async {
|
||||
var request = StewardAllocationRequest(
|
||||
allocationKey: model.key,
|
||||
checkAllocation: true,
|
||||
state: 'accepted',
|
||||
receiverRealNumberOfCarcasses: model.realNumberOfCarcasses ?? 0,
|
||||
receiverRealWeightOfCarcasses: model.realWeightOfCarcasses?.toInt() ?? 0,
|
||||
registrationCode: model.registrationCode ?? 0,
|
||||
weightLossOfCarcasses: model.weightLossOfCarcasses?.toInt() ?? 0,
|
||||
).toJson();
|
||||
request.removeWhere((key, value) => value == null);
|
||||
|
||||
safeCall(
|
||||
call: () async => await rootLogic.chickenRepository.setSateForArrivals(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
request: request,
|
||||
),
|
||||
onError: (error, stackTrace) {
|
||||
eLog(error);
|
||||
},
|
||||
onSuccess: (result) {
|
||||
getAllArrivals();
|
||||
rootLogic.getInventory();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> denyEntries(WaitingArrivalModel model) async {
|
||||
var request = StewardAllocationRequest(
|
||||
allocationKey: model.key,
|
||||
checkAllocation: true,
|
||||
state: 'rejected',
|
||||
).toJson();
|
||||
request.removeWhere((key, value) => value == null);
|
||||
|
||||
safeCall(
|
||||
call: () async => await rootLogic.chickenRepository.setSateForArrivals(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
request: request,
|
||||
),
|
||||
onError: (error, stackTrace) {
|
||||
eLog(error);
|
||||
},
|
||||
onSuccess: (result) {
|
||||
getAllArrivals();
|
||||
rootLogic.getInventory();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
String getVecPathItem(String? item) {
|
||||
switch (item) {
|
||||
case 'pending':
|
||||
return Assets.vec.timerSvg.path;
|
||||
case 'accepted':
|
||||
return Assets.vec.checkSquareSvg.path;
|
||||
case 'rejected':
|
||||
return Assets.vec.closeCircleSvg.path;
|
||||
default:
|
||||
return Assets.vec.timerSvg.path;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/waiting_arrival/waiting_arrival.dart';
|
||||
import 'package:rasadyar_chicken/presentation/utils/string_utils.dart';
|
||||
import 'package:rasadyar_chicken/presentation/widget/list_item/list_item.dart' hide ListItem2;
|
||||
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
import 'logic.dart';
|
||||
|
||||
class BuyInProvinceAllPage extends GetView<BuyInProvinceAllLogic> {
|
||||
const BuyInProvinceAllPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: ObxValue((data) {
|
||||
return RPaginatedListView(
|
||||
listType: ListType.separated,
|
||||
resource: data.value,
|
||||
hasMore: data.value.data?.next != null,
|
||||
padding: EdgeInsets.fromLTRB(8, 8, 8, 80),
|
||||
itemBuilder: (context, index) {
|
||||
var item = data.value.data!.results![index];
|
||||
return ObxValue((val) {
|
||||
return ExpandableListItem2(
|
||||
selected: val.contains(index),
|
||||
onTap: () => controller.isExpandedList.toggle(index),
|
||||
index: index,
|
||||
child: itemListWidget(item),
|
||||
secondChild: itemListExpandedWidget(item),
|
||||
labelColor: getLabelColor(item.receiverState),
|
||||
labelIcon: controller.getVecPathItem(item.receiverState),
|
||||
);
|
||||
}, controller.isExpandedList);
|
||||
},
|
||||
itemCount: data.value.data?.results?.length ?? 0,
|
||||
separatorBuilder: (context, index) => SizedBox(height: 8.h),
|
||||
onLoadMore: () async => controller.getAllArrivals(true),
|
||||
onRefresh: () async {
|
||||
controller.currentPage.value = 1;
|
||||
await controller.getAllArrivals();
|
||||
},
|
||||
);
|
||||
}, controller.allProduct),
|
||||
);
|
||||
}
|
||||
|
||||
Row itemListWidget(WaitingArrivalModel 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.toSteward?.user?.fullname ?? 'N/A',
|
||||
textAlign: TextAlign.start,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
Text(
|
||||
item.date?.formattedJalaliDate ?? '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: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
spacing: 6,
|
||||
children: [
|
||||
Visibility(
|
||||
visible: item.product?.name?.contains('مرغ گرم') ?? false,
|
||||
child: Assets.vec.hotChickenSvg.svg(
|
||||
width: 24,
|
||||
height: 24,
|
||||
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${item.weightOfCarcasses?.separatedByComma}kg',
|
||||
textAlign: TextAlign.left,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Text(
|
||||
item.toSteward?.guildsName ?? 'N/Aaq',
|
||||
textAlign: TextAlign.start,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.bgDark),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Assets.vec.scanSvg.svg(
|
||||
width: 32.w,
|
||||
height: 32.h,
|
||||
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Container itemListExpandedWidget(WaitingArrivalModel 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.steward?.user?.fullname ?? 'N/A',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan16.copyWith(color: AppColor.greenDark),
|
||||
),
|
||||
Spacer(),
|
||||
Text(
|
||||
item.receiverState?.faItem,
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan10.copyWith(color: AppColor.darkGreyDark),
|
||||
),
|
||||
SizedBox(width: 7),
|
||||
SvgGenImage.vec(
|
||||
controller.getVecPathItem(item.receiverState),
|
||||
).svg(width: 16.w, height: 16.h,
|
||||
colorFilter: ColorFilter.mode(AppColor.darkGreyDark, BlendMode.srcIn)
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
Container(
|
||||
height: 32,
|
||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||
decoration: ShapeDecoration(
|
||||
color: AppColor.blueLight,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(width: 1, color: AppColor.blueLightHover),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 3,
|
||||
children: [
|
||||
Text(
|
||||
item.date?.toJalali.formatter.wN ?? 'N/A',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
|
||||
Text(
|
||||
'${item.date?.toJalali.formatter.d} ${item.date?.toJalali.formatter.mN ?? 'N/A'}',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Text(
|
||||
'${item.date?.toJalali.formatter.y}',
|
||||
style: AppFonts.yekan20.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
|
||||
Text(
|
||||
'${item.date?.toJalali.formatter.tHH}:${item.date?.toJalali.formatter.tMM ?? 'N/A'}',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
buildRow(
|
||||
title: 'مشخصات فروشنده',
|
||||
value: item.steward?.user?.fullname ?? 'N/A',
|
||||
),
|
||||
buildRow(
|
||||
title: 'تلفن فروشنده',
|
||||
value: item.steward?.user?.mobile ?? 'N/A',
|
||||
valueStyle: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
buildRow(
|
||||
title: 'نوع تخصیص',
|
||||
value: item.allocationType?.faAllocationType ?? 'N/A',
|
||||
),
|
||||
buildRow(title: 'محصول', value: item.product?.name ?? 'N/A'),
|
||||
buildRow(
|
||||
title: 'وزن خریداری شده',
|
||||
value: '${item.weightOfCarcasses?.separatedByComma} کیلوگرم',
|
||||
),
|
||||
buildRow(title: 'قیمت کل', value: '${item.totalAmount?.separatedByComma} ریال'),
|
||||
Visibility(
|
||||
visible: item.receiverState == 'pending',
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 16.w,
|
||||
children: [
|
||||
ObxValue((data) {
|
||||
return RElevated(
|
||||
text: 'تایید',
|
||||
width: 150.w,
|
||||
height: 40.h,
|
||||
isLoading: data[item.key!] ?? false,
|
||||
onPressed: () async {
|
||||
data[item.key!] = !(data[item.key!] ?? false);
|
||||
await controller.acceptEntries(item);
|
||||
data.remove(item.key!);
|
||||
},
|
||||
textStyle: AppFonts.yekan20.copyWith(color: Colors.white),
|
||||
backgroundColor: AppColor.greenNormal,
|
||||
);
|
||||
}, controller.isLoadingConfirmMap),
|
||||
ROutlinedElevated(
|
||||
text: 'رد',
|
||||
textStyle: AppFonts.yekan20.copyWith(color: AppColor.redNormal),
|
||||
width: 150.w,
|
||||
height: 40.h,
|
||||
onPressed: () {
|
||||
buildWarningDialog(
|
||||
title: 'اخطار',
|
||||
middleText: 'آیا از رد شدن این مورد اطمینان دارید؟',
|
||||
onConfirm: () => controller.denyEntries(item),
|
||||
onRefresh: () => controller.getAllArrivals(),
|
||||
);
|
||||
},
|
||||
borderColor: AppColor.redNormal,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Color getLabelColor(String? item) {
|
||||
switch (item) {
|
||||
case 'pending':
|
||||
return AppColor.blueLight;
|
||||
case 'accepted':
|
||||
|
||||
return AppColor.greenLightHover;
|
||||
case 'rejected':
|
||||
return AppColor.redLightHover;
|
||||
default:
|
||||
return AppColor.blueLight;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_chicken/data/models/request/steward_allocation/steward_allocation_request.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/waiting_arrival/waiting_arrival.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/root/logic.dart';
|
||||
import 'package:rasadyar_chicken/presentation/utils/utils.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
class BuyInProvinceWaitingLogic extends GetxController {
|
||||
RxList<int> isExpandedList = <int>[].obs;
|
||||
Rxn<Jalali> fromDateFilter = Rxn();
|
||||
Rxn<Jalali> toDateFilter = Rxn();
|
||||
RxnString searchedValue = RxnString();
|
||||
RxMap<String, bool> isLoadingConfirmMap = RxMap();
|
||||
Rx<Color> bgConfirmAllColor = AppColor.blueNormal.obs;
|
||||
RxInt currentPage = 1.obs;
|
||||
final RxBool isLoadingMoreAllocationsMade = false.obs;
|
||||
StewardRootLogic rootLogic = Get.find<StewardRootLogic>();
|
||||
|
||||
Rx<Resource<PaginationModel<WaitingArrivalModel>>> waitingProduct =
|
||||
Resource<PaginationModel<WaitingArrivalModel>>.loading().obs;
|
||||
|
||||
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
debounce(
|
||||
searchedValue,
|
||||
(callback) => getWaitingArrivals(),
|
||||
time: Duration(milliseconds: timeDebounce),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
getWaitingArrivals();
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
void setSearchValue(String? data) {
|
||||
searchedValue.value = data?.trim();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
safeCall(
|
||||
call: () async => await rootLogic.chickenRepository.getWaitingArrivals(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
queryParameters: buildQueryParams(
|
||||
queryParams: {'type': 'not_entered'},
|
||||
pageSize: 20,
|
||||
page: currentPage.value,
|
||||
search: 'filter',
|
||||
role: 'Steward',
|
||||
value: searchedValue.value,
|
||||
fromDate: fromDateFilter.value?.toDateTime(),
|
||||
toDate: toDateFilter.value?.toDateTime(),
|
||||
),
|
||||
),
|
||||
onSuccess: (res) async {
|
||||
await Future.delayed(Duration(milliseconds: 200));
|
||||
if ((res?.count ?? 0) == 0) {
|
||||
waitingProduct.value = Resource<PaginationModel<WaitingArrivalModel>>.empty();
|
||||
} else {
|
||||
waitingProduct.value = Resource<PaginationModel<WaitingArrivalModel>>.success(
|
||||
PaginationModel<WaitingArrivalModel>(
|
||||
count: res?.count ?? 0,
|
||||
next: res?.next,
|
||||
previous: res?.previous,
|
||||
results: [...(waitingProduct.value.data?.results ?? []), ...(res?.results ?? [])],
|
||||
),
|
||||
);
|
||||
flashingFabBgColor();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Future<void> acceptEntries(WaitingArrivalModel model) async {
|
||||
var request = StewardAllocationRequest(
|
||||
allocationKey: model.key,
|
||||
checkAllocation: true,
|
||||
state: 'accepted',
|
||||
receiverRealNumberOfCarcasses: model.realNumberOfCarcasses ?? 0,
|
||||
receiverRealWeightOfCarcasses: model.realWeightOfCarcasses?.toInt() ?? 0,
|
||||
registrationCode: model.registrationCode ?? 0,
|
||||
weightLossOfCarcasses: model.weightLossOfCarcasses?.toInt() ?? 0,
|
||||
).toJson();
|
||||
request.removeWhere((key, value) => value == null);
|
||||
|
||||
safeCall(
|
||||
call: () async => await rootLogic.chickenRepository.setSateForArrivals(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
request: request,
|
||||
),
|
||||
onError: (error, stackTrace) {
|
||||
eLog(error);
|
||||
},
|
||||
onSuccess: (result) {
|
||||
getWaitingArrivals();
|
||||
// getBarGeneralInformation();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> denyEntries(WaitingArrivalModel model) async {
|
||||
var request = StewardAllocationRequest(
|
||||
allocationKey: model.key,
|
||||
checkAllocation: true,
|
||||
state: 'rejected',
|
||||
).toJson();
|
||||
request.removeWhere((key, value) => value == null);
|
||||
|
||||
safeCall(
|
||||
call: () async => await rootLogic.chickenRepository.setSateForArrivals(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
request: request,
|
||||
),
|
||||
onError: (error, stackTrace) {
|
||||
eLog(error);
|
||||
},
|
||||
onSuccess: (result) {
|
||||
getWaitingArrivals();
|
||||
//TODO
|
||||
// getBarGeneralInformation();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void flashingFabBgColor() {
|
||||
Timer.periodic(Duration(seconds: 2), (timer) {
|
||||
if (bgConfirmAllColor.value == AppColor.blueNormal) {
|
||||
bgConfirmAllColor.value = AppColor.blueLightHover;
|
||||
} else {
|
||||
bgConfirmAllColor.value = AppColor.blueNormal;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/waiting_arrival/waiting_arrival.dart';
|
||||
import 'package:rasadyar_chicken/presentation/utils/string_utils.dart';
|
||||
import 'package:rasadyar_chicken/presentation/widget/list_item/list_item.dart' hide ListItem2;
|
||||
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
import 'logic.dart';
|
||||
|
||||
class BuyInProvinceWaitingPage extends GetView<BuyInProvinceWaitingLogic> {
|
||||
const BuyInProvinceWaitingPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: ObxValue((data) {
|
||||
return RPaginatedListView(
|
||||
listType: ListType.separated,
|
||||
resource: data.value,
|
||||
hasMore: data.value.data?.next != null,
|
||||
padding: EdgeInsets.fromLTRB(8, 8, 8, 80),
|
||||
itemBuilder: (context, index) {
|
||||
var item = data.value.data!.results![index];
|
||||
return ObxValue((val) {
|
||||
return ExpandableListItem2(
|
||||
selected: val.contains(index),
|
||||
onTap: () => controller.isExpandedList.toggle(index),
|
||||
index: index,
|
||||
child: itemListWidget(item),
|
||||
secondChild: itemListExpandedWidget(item),
|
||||
labelColor: AppColor.blueLight,
|
||||
labelIcon: Assets.vec.timerSvg.path,
|
||||
);
|
||||
}, controller.isExpandedList);
|
||||
},
|
||||
itemCount: data.value.data?.results?.length ?? 0,
|
||||
separatorBuilder: (context, index) => SizedBox(height: 8.h),
|
||||
onLoadMore: () async => controller.getWaitingArrivals(true),
|
||||
onRefresh: () async {
|
||||
controller.currentPage.value = 1;
|
||||
await controller.getWaitingArrivals();
|
||||
},
|
||||
);
|
||||
}, controller.waitingProduct),
|
||||
),
|
||||
/* floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
|
||||
floatingActionButton: ObxValue((data) {
|
||||
if ((data.value.data?.results?.length ?? 0) > 1) {
|
||||
return AnimatedFab(
|
||||
onPressed: () {
|
||||
//TODO FAB
|
||||
},
|
||||
message: 'تایید یکجا',
|
||||
icon: Assets.vec.clipboardTaskSvg.svg(width: 45.w, height: 42.h),
|
||||
backgroundColor: controller.bgConfirmAllColor.value,
|
||||
);
|
||||
} else {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
}, controller.waitingProduct),*/
|
||||
);
|
||||
}
|
||||
|
||||
Row itemListWidget(WaitingArrivalModel 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.steward?.user?.fullname ?? 'N/A',
|
||||
textAlign: TextAlign.start,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
Text(
|
||||
item.date?.formattedJalaliDate ?? '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: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
spacing: 6,
|
||||
children: [
|
||||
Visibility(
|
||||
visible: item.product?.name?.contains('مرغ گرم') ?? false,
|
||||
child: Assets.vec.hotChickenSvg.svg(
|
||||
width: 24,
|
||||
height: 24,
|
||||
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${item.weightOfCarcasses?.separatedByComma}kg',
|
||||
textAlign: TextAlign.left,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Text(
|
||||
item.steward?.guildsName ?? 'N/A',
|
||||
textAlign: TextAlign.start,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.bgDark),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Assets.vec.scanSvg.svg(
|
||||
width: 32.w,
|
||||
height: 32.h,
|
||||
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Container itemListExpandedWidget(WaitingArrivalModel 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.steward?.user?.fullname ?? 'N/A',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan16.copyWith(color: AppColor.greenDark),
|
||||
),
|
||||
Spacer(),
|
||||
Text(
|
||||
'در انتظار',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan10.copyWith(color: AppColor.darkGreyDark),
|
||||
),
|
||||
SizedBox(width: 7),
|
||||
Assets.vec.clockSvg.svg(width: 16.w, height: 16.h),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
height: 32,
|
||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||
decoration: ShapeDecoration(
|
||||
color: AppColor.blueLight,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(width: 1, color: AppColor.blueLightHover),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 3,
|
||||
children: [
|
||||
Text(
|
||||
item.date?.toJalali.formatter.wN ?? 'N/A',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
|
||||
Text(
|
||||
'${item.date?.toJalali.formatter.d} ${item.date?.toJalali.formatter.mN ?? 'N/A'}',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Text(
|
||||
'${item.date?.toJalali.formatter.y}',
|
||||
style: AppFonts.yekan20.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
|
||||
Text(
|
||||
'${item.date?.toJalali.formatter.tHH}:${item.date?.toJalali.formatter.tMM ?? 'N/A'}',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
buildRow(
|
||||
title: 'مشخصات فروشنده',
|
||||
value: item.steward?.user?.fullname ?? 'N/A',
|
||||
),
|
||||
buildRow(
|
||||
title: 'تلفن فروشنده',
|
||||
value: item.steward?.user?.mobile ?? 'N/A',
|
||||
valueStyle: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
buildRow(
|
||||
title: 'نوع تخصیص',
|
||||
value: item.allocationType?.faAllocationType ?? 'N/A',
|
||||
),
|
||||
buildRow(title: 'محصول', value: item.product?.name ?? 'N/A'),
|
||||
buildRow(
|
||||
title: 'وزن خریداری شده',
|
||||
value: '${item.weightOfCarcasses?.separatedByComma} کیلوگرم',
|
||||
),
|
||||
buildRow(title: 'قیمت کل', value: '${item.totalAmount?.separatedByComma} ریال'),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 16.w,
|
||||
children: [
|
||||
ObxValue((data) {
|
||||
return RElevated(
|
||||
text: 'تایید',
|
||||
width: 150.w,
|
||||
height: 40.h,
|
||||
isLoading: data[item.key!] ?? false,
|
||||
onPressed: () async {
|
||||
data[item.key!] = !(data[item.key!] ?? false);
|
||||
await controller.acceptEntries(item);
|
||||
data.remove(item.key!);
|
||||
},
|
||||
textStyle: AppFonts.yekan20.copyWith(color: Colors.white),
|
||||
backgroundColor: AppColor.greenNormal,
|
||||
);
|
||||
}, controller.isLoadingConfirmMap),
|
||||
ROutlinedElevated(
|
||||
text: 'رد',
|
||||
textStyle: AppFonts.yekan20.copyWith(color: AppColor.redNormal),
|
||||
width: 150.w,
|
||||
height: 40.h,
|
||||
onPressed: () {
|
||||
buildWarningDialog(
|
||||
title: 'اخطار',
|
||||
middleText: 'آیا از رد شدن این مورد اطمینان دارید؟',
|
||||
onConfirm: () => controller.denyEntries(item),
|
||||
onRefresh: () => controller.getWaitingArrivals(),
|
||||
);
|
||||
},
|
||||
borderColor: AppColor.redNormal,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
import 'package:rasadyar_chicken/data/models/request/create_steward_free_bar/create_steward_free_bar.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/iran_province_city/iran_province_city_model.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/roles_products/roles_products.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/steward_free_bar/steward_free_bar.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/buy/logic.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/root/logic.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/sale/logic.dart';
|
||||
import 'package:rasadyar_chicken/presentation/utils/utils.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
class BuyOutOfProvinceLogic extends GetxController {
|
||||
late List<String> routesName;
|
||||
RxBool isExpanded = false.obs;
|
||||
RxBool isSubmitButtonEnabled = false.obs;
|
||||
RxList<int> isExpandedList = <int>[].obs;
|
||||
final RxInt currentPage = 1.obs;
|
||||
final RxBool isLoadingMoreAllocationsMade = false.obs;
|
||||
final RxBool isOnLoadingSubmitOrEdit = false.obs;
|
||||
|
||||
//TODO add this to Di
|
||||
ImagePicker imagePicker = ImagePicker();
|
||||
|
||||
Rx<Resource<PaginationModel<StewardFreeBar>>> purchaseOutOfProvinceList =
|
||||
Resource<PaginationModel<StewardFreeBar>>.loading().obs;
|
||||
Rxn<ProductModel> selectedProduct = Rxn();
|
||||
|
||||
RxList<IranProvinceCityModel> cites = <IranProvinceCityModel>[].obs;
|
||||
Rxn<IranProvinceCityModel> selectedProvince = Rxn();
|
||||
Rxn<IranProvinceCityModel> selectedCity = Rxn();
|
||||
Rxn<XFile> selectedImage = Rxn<XFile>();
|
||||
RxnString _base64Image = RxnString();
|
||||
RxnString editImageUrl = RxnString();
|
||||
|
||||
StewardRootLogic get rootLogic => Get.find<StewardRootLogic>();
|
||||
|
||||
BuyLogic get buyLogic => Get.find<BuyLogic>();
|
||||
|
||||
SaleLogic get outOfTheProvinceLogic => Get.find<SaleLogic>();
|
||||
|
||||
GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
TextEditingController sellerNameController = TextEditingController();
|
||||
TextEditingController sellerPhoneController = TextEditingController();
|
||||
TextEditingController carcassWeightController = TextEditingController();
|
||||
|
||||
Rx<Jalali> fromDateFilter = Jalali.now().obs;
|
||||
Rx<Jalali> toDateFilter = Jalali.now().obs;
|
||||
RxnString searchedValue = RxnString();
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
routesName = [...buyLogic.routesName, 'خارج استان'].toList();
|
||||
}
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
getStewardPurchaseOutOfProvince();
|
||||
selectedProvince.listen((p0) => getCites());
|
||||
|
||||
selectedProduct.value = rootLogic.rolesProductsModel.first;
|
||||
setupListeners();
|
||||
|
||||
debounce(
|
||||
searchedValue,
|
||||
(callback) => getStewardPurchaseOutOfProvince(),
|
||||
time: Duration(milliseconds: timeDebounce),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
sellerNameController.dispose();
|
||||
sellerPhoneController.dispose();
|
||||
carcassWeightController.dispose();
|
||||
isExpandedList.clear();
|
||||
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
void setSearchValue(String? data) {
|
||||
searchedValue.value = data?.trim();
|
||||
}
|
||||
|
||||
Future<void> getStewardPurchaseOutOfProvince([bool isLoadingMore = false]) async {
|
||||
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(
|
||||
call: () => rootLogic.chickenRepository.getStewardPurchasesOutSideOfTheProvince(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
queryParameters: buildQueryParams(
|
||||
pageSize: 20,
|
||||
page: currentPage.value,
|
||||
search: 'filter',
|
||||
role: 'Steward',
|
||||
value: searchedValue.value,
|
||||
fromDate: fromDateFilter.value.toDateTime(),
|
||||
toDate: toDateFilter.value.toDateTime(),
|
||||
),
|
||||
),
|
||||
onSuccess: (res) async {
|
||||
await Future.delayed(Duration(milliseconds: 500));
|
||||
if ((res?.count ?? 0) == 0) {
|
||||
purchaseOutOfProvinceList.value = Resource<PaginationModel<StewardFreeBar>>.empty();
|
||||
} else {
|
||||
purchaseOutOfProvinceList.value = Resource<PaginationModel<StewardFreeBar>>.success(
|
||||
PaginationModel<StewardFreeBar>(
|
||||
count: res?.count ?? 0,
|
||||
next: res?.next,
|
||||
previous: res?.previous,
|
||||
results: [
|
||||
...(purchaseOutOfProvinceList.value.data?.results ?? []),
|
||||
...(res?.results ?? []),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> getCites() async {
|
||||
await safeCall(
|
||||
call: () =>
|
||||
rootLogic.chickenRepository.getCity(provinceName: selectedProvince.value?.name ?? ''),
|
||||
onSuccess: (result) {
|
||||
if (result != null && result.isNotEmpty) {
|
||||
cites.value = result;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void setupListeners() {
|
||||
sellerNameController.addListener(checkFormValid);
|
||||
sellerPhoneController.addListener(checkFormValid);
|
||||
carcassWeightController.addListener(checkFormValid);
|
||||
|
||||
ever(selectedProvince, (_) => checkFormValid());
|
||||
ever(selectedCity, (_) => checkFormValid());
|
||||
ever(selectedProduct, (_) => checkFormValid());
|
||||
ever(selectedImage, (data) async {
|
||||
checkFormValid();
|
||||
if (data?.path != null) {
|
||||
_base64Image.value = await convertImageToBase64(data!.path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void checkFormValid() {
|
||||
isSubmitButtonEnabled.value =
|
||||
sellerNameController.text.isNotEmpty &&
|
||||
sellerPhoneController.text.isNotEmpty &&
|
||||
carcassWeightController.text.isNotEmpty &&
|
||||
selectedProvince.value != null &&
|
||||
selectedCity.value != null &&
|
||||
selectedProduct.value != null &&
|
||||
selectedImage.value != null;
|
||||
}
|
||||
|
||||
Future<bool> createStewardPurchaseOutOfProvince() async {
|
||||
bool res = false;
|
||||
isOnLoadingSubmitOrEdit.value = true;
|
||||
if (!(formKey.currentState?.validate() ?? false)) {
|
||||
return res;
|
||||
}
|
||||
await safeCall(
|
||||
call: () async {
|
||||
CreateStewardFreeBar createStewardFreeBar = CreateStewardFreeBar(
|
||||
productKey: selectedProduct.value!.key,
|
||||
killHouseName: sellerNameController.text,
|
||||
killHouseMobile: sellerPhoneController.text,
|
||||
province: selectedProvince.value!.name,
|
||||
city: selectedCity.value!.name,
|
||||
weightOfCarcasses: int.parse(carcassWeightController.text.clearComma),
|
||||
barImage: _base64Image.value,
|
||||
date: DateTime.now().formattedYHMS,
|
||||
);
|
||||
final res = await rootLogic.chickenRepository.createStewardPurchasesOutSideOfTheProvince(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
body: createStewardFreeBar,
|
||||
);
|
||||
},
|
||||
onSuccess: (result) {
|
||||
getStewardPurchaseOutOfProvince();
|
||||
resetSubmitForm();
|
||||
res = true;
|
||||
},
|
||||
);
|
||||
isOnLoadingSubmitOrEdit.value = false;
|
||||
return res;
|
||||
}
|
||||
|
||||
void resetSubmitForm() {
|
||||
sellerNameController.clear();
|
||||
sellerPhoneController.clear();
|
||||
carcassWeightController.clear();
|
||||
selectedProvince.value = null;
|
||||
selectedCity.value = null;
|
||||
selectedImage.value = null;
|
||||
_base64Image.value = null;
|
||||
editImageUrl.value = null;
|
||||
|
||||
isSubmitButtonEnabled.value = false;
|
||||
}
|
||||
|
||||
void setEditData(StewardFreeBar item) {
|
||||
editImageUrl.value = item.barImage;
|
||||
sellerNameController.text = item.killHouseName ?? '';
|
||||
sellerPhoneController.text = item.killHouseMobile ?? '';
|
||||
carcassWeightController.text = item.weightOfCarcasses?.toInt().toString() ?? '';
|
||||
selectedProvince.value = IranProvinceCityModel(name: item.province);
|
||||
selectedCity.value = IranProvinceCityModel(name: item.city);
|
||||
selectedProduct.value = rootLogic.rolesProductsModel.firstWhere(
|
||||
(element) => element.key == item.product!.key,
|
||||
);
|
||||
isSubmitButtonEnabled.value = true;
|
||||
}
|
||||
|
||||
Future<void> deleteStewardPurchaseOutOfProvince(String key) async {
|
||||
await safeCall(
|
||||
call: () => rootLogic.chickenRepository.deleteStewardPurchasesOutSideOfTheProvince(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
stewardFreeBarKey: key,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,596 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/iran_province_city/iran_province_city_model.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/roles_products/roles_products.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/steward_free_bar/steward_free_bar.dart';
|
||||
import 'package:rasadyar_chicken/presentation/widget/base_page/view.dart';
|
||||
import 'package:rasadyar_chicken/presentation/widget/steward/inventory_widget.dart';
|
||||
|
||||
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
import 'logic.dart';
|
||||
|
||||
class BuyOutOfProvincePage extends GetView<BuyOutOfProvinceLogic> {
|
||||
const BuyOutOfProvincePage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BasePage(
|
||||
routes: controller.routesName,
|
||||
onBackPressed: () => Get.back(id: 0),
|
||||
onSearchChanged: (data) => controller.setSearchValue(data),
|
||||
filteringWidget: filterBottomSheet(),
|
||||
widgets: [
|
||||
inventoryWidget(controller.rootLogic),
|
||||
Expanded(
|
||||
child: ObxValue((data) {
|
||||
return RPaginatedListView(
|
||||
listType: ListType.separated,
|
||||
resource: data.value,
|
||||
hasMore: data.value.data?.next != null,
|
||||
padding: EdgeInsets.fromLTRB(8, 8, 8, 80),
|
||||
itemBuilder: (context, index) {
|
||||
var item = data.value.data!.results![index];
|
||||
return ObxValue((val) {
|
||||
return ExpandableListItem2(
|
||||
selected: val.contains(index),
|
||||
onTap: () => controller.isExpandedList.toggle(index),
|
||||
index: index,
|
||||
child: itemListWidget(item),
|
||||
secondChild: itemListExpandedWidget(item),
|
||||
labelColor: AppColor.blueLight,
|
||||
labelIcon: Assets.vec.truckFastOutlinedSvg.path,
|
||||
);
|
||||
}, controller.isExpandedList);
|
||||
},
|
||||
itemCount: data.value.data?.results?.length ?? 0,
|
||||
separatorBuilder: (context, index) => SizedBox(height: 8.h),
|
||||
onLoadMore: () async => controller.getStewardPurchaseOutOfProvince(true),
|
||||
onRefresh: () async {
|
||||
controller.currentPage.value = 1;
|
||||
await controller.getStewardPurchaseOutOfProvince();
|
||||
},
|
||||
);
|
||||
}, controller.purchaseOutOfProvinceList),
|
||||
),
|
||||
],
|
||||
floatingActionButton: RFab.add(
|
||||
onPressed: () {
|
||||
Get.bottomSheet(
|
||||
addPurchasedInformationBottomSheet(),
|
||||
isScrollControlled: true,
|
||||
ignoreSafeArea: false,
|
||||
).whenComplete(() {
|
||||
controller.resetSubmitForm();
|
||||
|
||||
},);
|
||||
},
|
||||
),
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.startFloat,
|
||||
);
|
||||
}
|
||||
|
||||
Container itemListExpandedWidget(StewardFreeBar 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.province}-${item.city}',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan16.copyWith(color: AppColor.greenDark),
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
height: 32,
|
||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||
decoration: ShapeDecoration(
|
||||
color: AppColor.blueLight,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(width: 1, color: AppColor.blueLightHover),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 3,
|
||||
children: [
|
||||
Text(
|
||||
item.date?.toJalali.formatter.wN ?? 'N/A',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
|
||||
Text(
|
||||
'${item.date?.toJalali.formatter.d} ${item.date?.toJalali.formatter.mN ?? 'N/A'}',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Text(
|
||||
'${item.date?.toJalali.formatter.y}',
|
||||
style: AppFonts.yekan20.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
|
||||
Text(
|
||||
'${item.date?.toJalali.formatter.tHH}:${item.date?.toJalali.formatter.tMM ?? 'N/A'}',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
buildRow(title: 'مشخصات فروشنده', value: item.killHouseName ?? 'N/A'),
|
||||
|
||||
buildRow(
|
||||
title: 'تلفن فروشنده',
|
||||
value: item.killHouseMobile ?? 'N/A',
|
||||
valueStyle: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
buildRow(title: 'محصول', value: item.product?.name ?? 'N/A'),
|
||||
buildRow(
|
||||
title: 'وزن خریداری شده',
|
||||
value: '${item.weightOfCarcasses?.separatedByComma} کیلوگرم',
|
||||
),
|
||||
buildRowOnTapped(
|
||||
title: 'مشاهده بارنامه',
|
||||
titleStyle: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
|
||||
valueWidget: Assets.vec.clipboardEyeSvg.svg(
|
||||
width: 20,
|
||||
height: 24,
|
||||
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
|
||||
),
|
||||
onTap: () {
|
||||
Get.bottomSheet(
|
||||
BaseBottomSheet(
|
||||
height: 400,
|
||||
child: Column(
|
||||
spacing: 16,
|
||||
children: [
|
||||
Text(
|
||||
'بارنامه',
|
||||
style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
|
||||
Image.network(
|
||||
item.barImage ?? '',
|
||||
fit: BoxFit.cover,
|
||||
height: 300,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
eLog(error.toString());
|
||||
return Center(child: Text('خطایی پیش آمده!'));
|
||||
},
|
||||
loadingBuilder: (context, child, loadingProgress) {
|
||||
if (loadingProgress == null) return child;
|
||||
return CupertinoActivityIndicator();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 16.w,
|
||||
children: [
|
||||
RElevated(
|
||||
text: 'ویرایش',
|
||||
width: 150.w,
|
||||
height: 40.h,
|
||||
onPressed: () {
|
||||
controller.setEditData(item);
|
||||
Get.bottomSheet(
|
||||
addPurchasedInformationBottomSheet(true),
|
||||
isScrollControlled: true,
|
||||
).whenComplete(() {
|
||||
|
||||
controller.resetSubmitForm();
|
||||
});
|
||||
},
|
||||
textStyle: AppFonts.yekan20.copyWith(color: Colors.white),
|
||||
backgroundColor: AppColor.greenNormal,
|
||||
),
|
||||
ROutlinedElevated(
|
||||
text: 'حذف',
|
||||
textStyle: AppFonts.yekan20.copyWith(color: AppColor.redNormal),
|
||||
width: 150.w,
|
||||
height: 40.h,
|
||||
onPressed: () {
|
||||
buildDeleteDialog(
|
||||
onConfirm: () => controller.deleteStewardPurchaseOutOfProvince(item.key!),
|
||||
onRefresh: () => controller.getStewardPurchaseOutOfProvince(),
|
||||
);
|
||||
},
|
||||
borderColor: AppColor.redNormal,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Row itemListWidget(StewardFreeBar 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.killHouseName ?? 'N/A',
|
||||
textAlign: TextAlign.start,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
Text(
|
||||
item.date?.formattedJalaliDate ?? 'N/A',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.bgDark),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
spacing: 3,
|
||||
children: [
|
||||
Visibility(
|
||||
visible: item.product?.name?.contains('مرغ گرم') ?? false,
|
||||
child: Assets.vec.hotChickenSvg.svg(
|
||||
width: 24,
|
||||
height: 24,
|
||||
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${item.weightOfCarcasses?.separatedByComma}kg',
|
||||
textAlign: TextAlign.left,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(height: 2),
|
||||
|
||||
Text(
|
||||
'${item.province}',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.bgDark),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Assets.vec.scanSvg.svg(
|
||||
width: 32.w,
|
||||
height: 32.h,
|
||||
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget addPurchasedInformationBottomSheet([bool isOnEdit = false]) {
|
||||
return BaseBottomSheet(
|
||||
child: Form(
|
||||
key: controller.formKey,
|
||||
child: Column(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Text(
|
||||
isOnEdit ? 'ویرایش اطلاعات خرید' : 'ثبت اطلاعات خرید',
|
||||
style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
_productDropDown(),
|
||||
|
||||
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.sellerNameController,
|
||||
label: 'نام فروشنده',
|
||||
borderColor: AppColor.darkGreyLight,
|
||||
filled: true,
|
||||
filledColor: AppColor.bgLight,
|
||||
),
|
||||
RTextField(
|
||||
controller: controller.sellerPhoneController,
|
||||
label: 'تلفن فروشنده',
|
||||
keyboardType: TextInputType.phone,
|
||||
borderColor: AppColor.darkGreyLight,
|
||||
maxLength: 11,
|
||||
filled: true,
|
||||
filledColor: AppColor.bgLight,
|
||||
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;
|
||||
},
|
||||
),
|
||||
|
||||
UnitTextField(
|
||||
controller: controller.carcassWeightController,
|
||||
hint: 'وزن',
|
||||
unit: 'کیلوگرم',
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
SeparatorInputFormatter(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
_imageCarcasesWidget(isOnEdit),
|
||||
submitButtonWidget(isOnEdit),
|
||||
SizedBox(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget submitButtonWidget(bool isOnEdit) {
|
||||
return Obx(() {
|
||||
return RElevated(
|
||||
text: isOnEdit ? 'ویرایش' : 'ثبت',
|
||||
width: Get.width,
|
||||
backgroundColor: AppColor.greenNormal,
|
||||
isLoading: controller.isOnLoadingSubmitOrEdit.value,
|
||||
onPressed: controller.isSubmitButtonEnabled.value
|
||||
? () async {
|
||||
var res = await controller.createStewardPurchaseOutOfProvince();
|
||||
if (res) {
|
||||
Get.back();
|
||||
}
|
||||
}
|
||||
: null,
|
||||
height: 40,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _productDropDown() {
|
||||
return Obx(() {
|
||||
return OverlayDropdownWidget<ProductModel>(
|
||||
items: controller.rootLogic.rolesProductsModel,
|
||||
height: 56,
|
||||
hasDropIcon: false,
|
||||
background: Colors.white,
|
||||
onChanged: (value) {
|
||||
controller.selectedProduct.value = value;
|
||||
},
|
||||
selectedItem: controller.selectedProduct.value,
|
||||
initialValue: controller.selectedProduct.value,
|
||||
itemBuilder: (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}',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _provinceWidget() {
|
||||
return Obx(() {
|
||||
return OverlayDropdownWidget<IranProvinceCityModel>(
|
||||
items: controller.rootLogic.provinces,
|
||||
onChanged: (value) {
|
||||
controller.selectedProvince.value = value;
|
||||
},
|
||||
selectedItem: controller.selectedProvince.value,
|
||||
itemBuilder: (item) => Text(
|
||||
item.name ?? 'بدون نام',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.lightGreyDarker),
|
||||
),
|
||||
labelBuilder: (item) => item?.name != null
|
||||
? Text(item!.name!, style: AppFonts.yekan14.copyWith(color: AppColor.textColor))
|
||||
: Text(
|
||||
'انتخاب استان',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.textColorLight),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _cityWidget() {
|
||||
return ObxValue((data) {
|
||||
return OverlayDropdownWidget<IranProvinceCityModel>(
|
||||
items: data,
|
||||
onChanged: (value) {
|
||||
controller.selectedCity.value = value;
|
||||
},
|
||||
selectedItem: controller.selectedCity.value,
|
||||
itemBuilder: (item) => Text(
|
||||
item.name ?? 'بدون نام',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.lightGreyDarker),
|
||||
),
|
||||
labelBuilder: (item) => item?.name != null
|
||||
? Text(item!.name!, style: AppFonts.yekan14.copyWith(color: AppColor.textColor))
|
||||
: Text('انتخاب شهر', style: AppFonts.yekan14.copyWith(color: AppColor.textColorLight)),
|
||||
);
|
||||
}, controller.cites);
|
||||
}
|
||||
|
||||
SizedBox _imageCarcasesWidget(bool isOnEdit) {
|
||||
return SizedBox(
|
||||
width: Get.width,
|
||||
height: 370,
|
||||
child: Card(
|
||||
color: Colors.white,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: Column(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Text('بارنامه', style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal)),
|
||||
Expanded(
|
||||
child: ObxValue((data) {
|
||||
return Container(
|
||||
width: Get.width,
|
||||
height: 270,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.lightGreyNormal,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(width: 1, color: AppColor.blackLight),
|
||||
),
|
||||
child: Center(
|
||||
child: isOnEdit
|
||||
? Image.network(controller.editImageUrl.value ?? '')
|
||||
: data.value == null
|
||||
? Padding(
|
||||
padding: const EdgeInsets.fromLTRB(30, 10, 10, 30),
|
||||
child: Assets.vec.placeHolderSvg.svg(width: 200.w, height: 150.h),
|
||||
)
|
||||
: Image.file(File(data.value!.path), fit: BoxFit.cover),
|
||||
),
|
||||
);
|
||||
}, controller.selectedImage),
|
||||
),
|
||||
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
RElevated(
|
||||
text: 'گالری',
|
||||
width: 150.w,
|
||||
height: 40.h,
|
||||
textStyle: AppFonts.yekan20.copyWith(color: Colors.white),
|
||||
onPressed: () async {
|
||||
controller.selectedImage.value = await controller.imagePicker.pickImage(
|
||||
source: ImageSource.gallery,
|
||||
imageQuality: 60,
|
||||
maxWidth: 1080,
|
||||
maxHeight: 720,
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
ROutlinedElevated(
|
||||
text: 'دوربین',
|
||||
width: 150.w,
|
||||
height: 40.h,
|
||||
textStyle: AppFonts.yekan20.copyWith(color: AppColor.blueNormal),
|
||||
onPressed: () async {
|
||||
controller.selectedImage.value = await controller.imagePicker.pickImage(
|
||||
source: ImageSource.camera,
|
||||
imageQuality: 60,
|
||||
maxWidth: 1080,
|
||||
maxHeight: 720,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget filterBottomSheet() {
|
||||
return BaseBottomSheet(
|
||||
height: 200,
|
||||
child: Column(
|
||||
spacing: 16,
|
||||
children: [
|
||||
Text('فیلترها', style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal)),
|
||||
Row(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Expanded(
|
||||
child: dateFilterWidget(
|
||||
date: controller.fromDateFilter,
|
||||
onChanged: (jalali) => controller.fromDateFilter.value = jalali,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: dateFilterWidget(
|
||||
isFrom: false,
|
||||
date: controller.toDateFilter,
|
||||
onChanged: (jalali) => controller.toDateFilter.value = jalali,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
RElevated(
|
||||
text: 'اعمال فیلتر',
|
||||
isFullWidth: true,
|
||||
backgroundColor: AppColor.greenNormal,
|
||||
onPressed: () {
|
||||
controller.getStewardPurchaseOutOfProvince();
|
||||
Get.back();
|
||||
},
|
||||
height: 40,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
|
||||
import 'package:rasadyar_chicken/chicken.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/bar_information/bar_information.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/kill_house_distribution_info/kill_house_distribution_info.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/root/logic.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
class HomeLogic extends GetxController {
|
||||
StewardRootLogic rootLogic = Get.find<StewardRootLogic>();
|
||||
RxnInt totalWeightTodayBars = RxnInt();
|
||||
Rxn<KillHouseDistributionInfo> killHouseDistributionInfo = Rxn<KillHouseDistributionInfo>();
|
||||
Rxn<BarInformation> barInformation = Rxn();
|
||||
|
||||
RxBool isExpanded = false.obs;
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
getGeneralBarsInformation();
|
||||
getTodayBars();
|
||||
getDistributionInformation();
|
||||
}
|
||||
|
||||
Future<void> getGeneralBarsInformation() async {
|
||||
await safeCall<BarInformation?>(
|
||||
call: () async => await rootLogic.chickenRepository.getGeneralBarInformation(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
queryParameters: buildQueryParams(role: 'Steward'),
|
||||
),
|
||||
onSuccess: (result) {
|
||||
if (result != null) {
|
||||
barInformation.value = result;
|
||||
}
|
||||
},
|
||||
onError: (error, stackTrace) {},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> getTodayBars() async {
|
||||
await safeCall<BarInformation?>(
|
||||
call: () async => await rootLogic.chickenRepository.getGeneralBarInformation(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
queryParameters: buildQueryParams(
|
||||
fromDate: DateTime.now(),
|
||||
toDate: DateTime.now(),
|
||||
role: 'Steward',
|
||||
),
|
||||
),
|
||||
onSuccess: (result) {
|
||||
if (result != null) {
|
||||
totalWeightTodayBars.value = result.totalBarsWeight?.toInt();
|
||||
}
|
||||
},
|
||||
onError: (error, stackTrace) {},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> getDistributionInformation() async {
|
||||
await safeCall<KillHouseDistributionInfo?>(
|
||||
call: () async => await rootLogic.chickenRepository.getKillHouseDistributionInfo(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
),
|
||||
onSuccess: (result) {
|
||||
if (result != null) {
|
||||
killHouseDistributionInfo.value = result;
|
||||
}
|
||||
},
|
||||
onError: (error, stackTrace) {},
|
||||
);
|
||||
}
|
||||
}
|
||||
636
packages/chicken/lib/presentation/pages/steward/home/view.dart
Normal file
636
packages/chicken/lib/presentation/pages/steward/home/view.dart
Normal file
@@ -0,0 +1,636 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/inventory/inventory_model.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/kill_house_distribution_info/kill_house_distribution_info.dart';
|
||||
import 'package:rasadyar_chicken/presentation/widget/app_bar.dart';
|
||||
import 'package:rasadyar_chicken/presentation/widget/steward/widely_used/view.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
import 'logic.dart';
|
||||
|
||||
class HomePage extends GetView<HomeLogic> {
|
||||
const HomePage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColor.bgLight,
|
||||
appBar: chickenAppBar(hasBack: false, hasFilter: false, hasSearch: false),
|
||||
body: SingleChildScrollView(
|
||||
physics: BouncingScrollPhysics(),
|
||||
child: Column(
|
||||
spacing: 8,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
controller.isExpanded.value = !controller.isExpanded.value;
|
||||
},
|
||||
child: Card(
|
||||
margin: EdgeInsetsGeometry.all(6),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(width: 0.50, color: const Color(0xFFA9A9A9)),
|
||||
),
|
||||
|
||||
child: ObxValue((data) {
|
||||
return AnimatedSize(
|
||||
duration: Duration(milliseconds: 300),
|
||||
child: data.value
|
||||
? Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 8,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: ShapeDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage(Assets.images.chicken.path),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(
|
||||
width: 0.25,
|
||||
color: const Color(0xFFB0B0B0),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'مرغ گرم',
|
||||
textAlign: TextAlign.right,
|
||||
style: AppFonts.yekan16.copyWith(
|
||||
color: AppColor.darkGreyDarkActive,
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
AnimatedRotation(
|
||||
turns: 180,
|
||||
duration: Duration(milliseconds: 3000),
|
||||
child: Icon(CupertinoIcons.chevron_up, size: 18),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
_todayShipmentWidget(),
|
||||
|
||||
_inventoryWidget(),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'اطلاعات بارها',
|
||||
textAlign: TextAlign.right,
|
||||
style: AppFonts.yekan16,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
_informationShipment(),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'اطلاعات توزیع',
|
||||
textAlign: TextAlign.right,
|
||||
style: AppFonts.yekan16,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
distributionInformationWidget(),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 8,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: ShapeDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage(Assets.images.chicken.path),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(
|
||||
width: 0.25,
|
||||
color: const Color(0xFFB0B0B0),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'مرغ گرم',
|
||||
textAlign: TextAlign.right,
|
||||
style: AppFonts.yekan16.copyWith(
|
||||
color: AppColor.darkGreyDarkActive,
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
Icon(CupertinoIcons.chevron_down, size: 18),
|
||||
],
|
||||
),
|
||||
_todayShipmentWidget(),
|
||||
_inventoryWidget(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}, controller.isExpanded),
|
||||
),
|
||||
),
|
||||
|
||||
WidelyUsedWidget(),
|
||||
SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget distributionInformationWidget() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 8, 0, 13),
|
||||
child: ObxValue((data) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 8,
|
||||
children: [
|
||||
Expanded(
|
||||
child: _informationIconCard(
|
||||
title: 'توزیع داخل استان',
|
||||
isLoading: data.value == null,
|
||||
description: data.value?.freeSalesWeight.separatedByComma ?? '0',
|
||||
iconPath: Assets.vec.truckSvg.path,
|
||||
iconColor: const Color.fromRGBO(85, 97, 93, 1),
|
||||
bgDescriptionColor: const Color(0xFFE6FAF5),
|
||||
bgLabelColor: const Color(0xFFB0EFDF),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: _informationIconCard(
|
||||
title: 'توزیع خارج استان',
|
||||
isLoading: data.value == null,
|
||||
description: data.value?.stewardAllocationsWeight.separatedByComma ?? '0',
|
||||
iconPath: Assets.vec.truckFastSvg.path,
|
||||
iconColor: Color(0xFF647379),
|
||||
bgDescriptionColor: const Color(0xFFEAEFFF),
|
||||
bgLabelColor: const Color(0xFFD4DEFF),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: _informationIconCard(
|
||||
title: 'قطعه بندی',
|
||||
description: '2،225،256',
|
||||
iconPath: Assets.vec.convertCubeSvg.path,
|
||||
iconColor: const Color(0xFF6F6164),
|
||||
bgDescriptionColor: const Color(0xFFEDDCE0),
|
||||
bgLabelColor: const Color(0xFFE0BCC5),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}, controller.killHouseDistributionInfo),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _informationShipment() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 8, 0, 13),
|
||||
child: ObxValue((data) {
|
||||
return Row(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Expanded(
|
||||
child: _informationLabelCard(
|
||||
title: 'داخل استان',
|
||||
isLoading: data.value == null,
|
||||
description: data.value != null
|
||||
? ((data.value?.provinceGovernmentalCarcassesWeight ?? 0) +
|
||||
(data.value?.provinceFreeCarcassesWeight ?? 0))
|
||||
.separatedByComma
|
||||
: '0',
|
||||
iconPath: Assets.vec.a3dCubeSquareSvg.path,
|
||||
iconColor: const Color(0xFF6C5D60),
|
||||
bgDescriptionColor: const Color(0xFFEDDCE0),
|
||||
bgLabelColor: const Color(0xFFDDC0C7),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: _informationLabelCard(
|
||||
title: 'خارج استان',
|
||||
isLoading: data.value == null,
|
||||
description: data.value?.freeBuyingCarcassesWeight.separatedByComma ?? '0',
|
||||
iconPath: Assets.vec.cubeSearchSvg.path,
|
||||
iconColor: Color(0xFF2D5FFF),
|
||||
bgLabelColor: const Color(0xFFAFCBFF),
|
||||
bgDescriptionColor: const Color(0xFFCEDFFF),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}, controller.rootLogic.inventoryModel),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _inventoryWidget() {
|
||||
return ObxValue((data) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 10, 0, 13),
|
||||
child: Row(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Expanded(
|
||||
child: _informationLabelCard(
|
||||
title: 'مانده انبار',
|
||||
isLoading: data.value == null,
|
||||
description: data.value?.totalRemainWeight.separatedByComma ?? '0',
|
||||
iconPath: Assets.vec.cubeSearchSvg.path,
|
||||
iconColor: const Color(0xFF426060),
|
||||
bgDescriptionColor: const Color(0xFFC7DFE0),
|
||||
bgLabelColor: const Color(0xFFA5D1D2),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: _informationLabelCard(
|
||||
title: 'توزیع شده',
|
||||
isLoading: data.value == null,
|
||||
description: data.value?.realAllocatedWeight.separatedByComma ?? '0',
|
||||
iconPath: Assets.vec.cubeRotateSvg.path,
|
||||
iconColor: Color(0xFF5C4D64),
|
||||
bgLabelColor: Color(0xFFC8B8D1),
|
||||
bgDescriptionColor: Color(0xFFDAD4DD),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}, controller.rootLogic.inventoryModel);
|
||||
}
|
||||
|
||||
Widget _todayShipmentWidget() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 10, 0, 13),
|
||||
child: Row(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Expanded(
|
||||
child: ObxValue(
|
||||
(data) => _informationLabelCard(
|
||||
title: 'بارهای امروز',
|
||||
titleColor: AppColor.blueNormal,
|
||||
isLoading: data.value == null,
|
||||
description: data.value?.separatedByComma ?? '0',
|
||||
iconPath: Assets.vec.cubeSearchSvg.path,
|
||||
iconColor: AppColor.blueNormal,
|
||||
bgDescriptionColor: Colors.white,
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [AppColor.blueLight, Colors.white],
|
||||
),
|
||||
),
|
||||
controller.totalWeightTodayBars,
|
||||
),
|
||||
),
|
||||
|
||||
Expanded(
|
||||
child: ObxValue((data) {
|
||||
return _informationLabelCard(
|
||||
title: 'درانتظار تایید',
|
||||
isLoading: data.value == null,
|
||||
description: data.value?.totalNotEnteredBars.separatedByComma ?? '0',
|
||||
unit:
|
||||
'(${data.value?.totalNotEnteredKillHouseRequestsWeight.separatedByComma})\nکیلوگرم',
|
||||
iconPath: Assets.vec.cubeWattingSvg.path,
|
||||
bgDescriptionColor: Colors.white,
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [const Color(0xFFFFE7BB), Colors.white],
|
||||
),
|
||||
);
|
||||
}, controller.barInformation),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Container _informationLabelCard({
|
||||
required String title,
|
||||
required String description,
|
||||
required String iconPath,
|
||||
required Color bgDescriptionColor,
|
||||
String unit = 'کیلوگرم',
|
||||
bool isLoading = false,
|
||||
Color? iconColor,
|
||||
Color? titleColor,
|
||||
Color? bgLabelColor,
|
||||
LinearGradient? gradient,
|
||||
}) {
|
||||
return Container(
|
||||
height: 82,
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: Row(
|
||||
children: [
|
||||
// Left side with icon and title
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 82,
|
||||
decoration: BoxDecoration(
|
||||
color: gradient == null ? bgLabelColor : null,
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(8),
|
||||
bottomRight: Radius.circular(8),
|
||||
),
|
||||
gradient: gradient,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 4,
|
||||
children: [
|
||||
SvgGenImage.vec(iconPath).svg(
|
||||
width: 24,
|
||||
height: 24,
|
||||
colorFilter: iconColor != null
|
||||
? ColorFilter.mode(iconColor, BlendMode.srcIn)
|
||||
: null,
|
||||
),
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.right,
|
||||
style: AppFonts.yekan14.copyWith(
|
||||
color: titleColor ?? AppColor.mediumGreyDarkActive,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// Right side with description and unit
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: bgDescriptionColor,
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(8),
|
||||
bottomLeft: Radius.circular(8),
|
||||
),
|
||||
),
|
||||
child: isLoading
|
||||
? Center(child: CupertinoActivityIndicator())
|
||||
: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 4,
|
||||
children: [
|
||||
Text(
|
||||
description,
|
||||
textAlign: TextAlign.right,
|
||||
style: AppFonts.yekan16.copyWith(color: AppColor.mediumGreyDarkActive),
|
||||
),
|
||||
Text(
|
||||
unit,
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.mediumGreyDarkActive),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Container _informationIconCard({
|
||||
required String title,
|
||||
required String description,
|
||||
String unit = 'کیلوگرم',
|
||||
bool isLoading = false,
|
||||
required String iconPath,
|
||||
required Color iconColor,
|
||||
required Color bgDescriptionColor,
|
||||
required Color bgLabelColor,
|
||||
}) {
|
||||
return Container(
|
||||
height: 110,
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: Stack(
|
||||
alignment: Alignment.topCenter,
|
||||
children: [
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
left: 0,
|
||||
child: Container(
|
||||
height: 91,
|
||||
decoration: BoxDecoration(
|
||||
color: bgDescriptionColor,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(width: 0.25, color: const Color(0xFFB4B4B4)),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 4,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.right,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.mediumGreyDarkActive),
|
||||
),
|
||||
|
||||
isLoading
|
||||
? Center(child: CupertinoActivityIndicator())
|
||||
: Text(
|
||||
description,
|
||||
textAlign: TextAlign.right,
|
||||
style: AppFonts.yekan16.copyWith(color: AppColor.mediumGreyDarkActive),
|
||||
),
|
||||
Text(
|
||||
unit,
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.mediumGreyDarkActive),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: ShapeDecoration(
|
||||
color: bgLabelColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
side: BorderSide(width: 0.25, color: const Color(0xFFD5D5D5)),
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: SvgGenImage.vec(iconPath).svg(
|
||||
width: 24,
|
||||
height: 24,
|
||||
colorFilter: ColorFilter.mode(iconColor, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget inventoryItem({
|
||||
required bool isExpanded,
|
||||
required int index,
|
||||
required InventoryModel model,
|
||||
}) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 8,
|
||||
children: [
|
||||
buildRow('نام محصول', model.name ?? ''),
|
||||
Visibility(
|
||||
visible: isExpanded,
|
||||
child: Column(
|
||||
spacing: 8,
|
||||
children: [
|
||||
buildRow('وزن خریدهای دولتی داخل استان (کیلوگرم)', '0326598653'),
|
||||
buildRow(
|
||||
'وزن خریدهای آزاد داخل استان (کیلوگرم)',
|
||||
model.receiveFreeCarcassesWeight.toString(),
|
||||
),
|
||||
buildRow(
|
||||
'وزن خریدهای خارج استان (کیلوگرم)',
|
||||
model.freeBuyingCarcassesWeight.toString(),
|
||||
),
|
||||
buildRow(
|
||||
'کل ورودی به انبار (کیلوگرم)',
|
||||
model.totalFreeBarsCarcassesWeight.toString(),
|
||||
),
|
||||
buildRow('کل فروش (کیلوگرم)', model.realAllocatedWeight.toString()),
|
||||
buildRow('مانده انبار (کیلوگرم)', model.totalRemainWeight.toString()),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildRow(String title, String value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
title,
|
||||
textAlign: TextAlign.right,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDarkHover),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
flex: 1,
|
||||
child: Text(
|
||||
value,
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDarkHover),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget broadcastInformationWidget(KillHouseDistributionInfo? model) {
|
||||
return Container(
|
||||
height: 140,
|
||||
margin: const EdgeInsets.all(8),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: AppColor.blueNormal, width: 1),
|
||||
),
|
||||
child: model != null
|
||||
? Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 10,
|
||||
children: [
|
||||
Text(
|
||||
'اطلاعات ارسالی',
|
||||
textAlign: TextAlign.right,
|
||||
style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
buildRow(
|
||||
'فروش و توزیع داخل استان (کیلوگرم)',
|
||||
model.stewardAllocationsWeight!.toInt().toString(),
|
||||
),
|
||||
buildRow(
|
||||
'فروش و توزیع خارج استان (کیلوگرم)',
|
||||
model.freeSalesWeight!.toInt().toString(),
|
||||
),
|
||||
],
|
||||
)
|
||||
: const Center(child: CircularProgressIndicator()),
|
||||
);
|
||||
}
|
||||
|
||||
Widget cardWidget({
|
||||
required String title,
|
||||
required String iconPath,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
return Container(
|
||||
width: Get.width / 4,
|
||||
height: 130,
|
||||
child: GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Card(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(width: 1, color: AppColor.blueNormal),
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SvgGenImage(iconPath).svg(width: 50, height: 50),
|
||||
SizedBox(height: 4),
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:rasadyar_chicken/data/models/request/change_password/change_password_request_model.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/iran_province_city/iran_province_city_model.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/user_profile/user_profile.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/root/logic.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
class ProfileLogic extends GetxController {
|
||||
StewardRootLogic rootLogic = Get.find<StewardRootLogic>();
|
||||
RxInt selectedInformationType = 0.obs;
|
||||
Rxn<Jalali> birthDate = Rxn<Jalali>();
|
||||
|
||||
Rx<Resource<UserProfile>> userProfile = Rx<Resource<UserProfile>>(Resource.loading());
|
||||
|
||||
TextEditingController nameController = TextEditingController();
|
||||
TextEditingController lastNameController = TextEditingController();
|
||||
TextEditingController nationalCodeController = TextEditingController();
|
||||
TextEditingController nationalIdController = TextEditingController();
|
||||
TextEditingController birthdayController = TextEditingController();
|
||||
|
||||
TextEditingController oldPasswordController = TextEditingController();
|
||||
TextEditingController newPasswordController = TextEditingController();
|
||||
TextEditingController retryNewPasswordController = TextEditingController();
|
||||
|
||||
RxList<IranProvinceCityModel> cites = <IranProvinceCityModel>[].obs;
|
||||
Rxn<IranProvinceCityModel> selectedProvince = Rxn();
|
||||
Rxn<IranProvinceCityModel> selectedCity = Rxn();
|
||||
|
||||
GlobalKey<FormState> formKey = GlobalKey();
|
||||
ImagePicker imagePicker = ImagePicker();
|
||||
Rxn<XFile> selectedImage = Rxn<XFile>();
|
||||
RxnString _base64Image = RxnString();
|
||||
RxBool isOnLoading = false.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
ever(selectedImage, (data) async {
|
||||
if (data?.path != null) {
|
||||
_base64Image.value = await convertImageToBase64(data!.path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
getUserProfile();
|
||||
selectedProvince.listen((p0) => getCites());
|
||||
userProfile.listen((data) {
|
||||
nameController.text = data.data?.firstName ?? '';
|
||||
lastNameController.text = data.data?.lastName ?? '';
|
||||
nationalCodeController.text = data.data?.nationalCode ?? '';
|
||||
nationalIdController.text = data.data?.nationalId ?? '';
|
||||
birthdayController.text = data.data?.birthday?.toJalali.formatCompactDate() ?? '';
|
||||
birthDate.value = data.data?.birthday?.toJalali;
|
||||
selectedProvince.value = IranProvinceCityModel(
|
||||
name: data.data?.province ?? '',
|
||||
id: data.data?.provinceNumber ?? 0,
|
||||
);
|
||||
|
||||
selectedCity.value = IranProvinceCityModel(
|
||||
name: data.data?.city ?? '',
|
||||
id: data.data?.cityNumber ?? 0,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
Future<void> getUserProfile() async {
|
||||
userProfile.value = Resource.loading();
|
||||
await safeCall<UserProfile?>(
|
||||
call: () async => await rootLogic.chickenRepository.getUserProfile(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
),
|
||||
onSuccess: (result) {
|
||||
if (result != null) {
|
||||
userProfile.value = Resource.success(result);
|
||||
}
|
||||
},
|
||||
onError: (error, stackTrace) {},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> getCites() async {
|
||||
await safeCall(
|
||||
call: () =>
|
||||
rootLogic.chickenRepository.getCity(provinceName: selectedProvince.value?.name ?? ''),
|
||||
onSuccess: (result) {
|
||||
if (result != null && result.isNotEmpty) {
|
||||
cites.value = result;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> updateUserProfile() async {
|
||||
UserProfile userProfile = UserProfile(
|
||||
firstName: nameController.text,
|
||||
lastName: lastNameController.text,
|
||||
nationalCode: nationalCodeController.text,
|
||||
nationalId: nationalIdController.text,
|
||||
birthday: birthDate.value?.toDateTime().formattedDashedGregorian.toString(),
|
||||
image: _base64Image.value,
|
||||
personType: 'self',
|
||||
type: 'self_profile',
|
||||
);
|
||||
isOnLoading.value = true;
|
||||
await safeCall(
|
||||
call: () async => await rootLogic.chickenRepository.updateUserProfile(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
userProfile: userProfile,
|
||||
),
|
||||
onSuccess: (result) {
|
||||
isOnLoading.value = false;
|
||||
},
|
||||
onError: (error, stackTrace) {
|
||||
isOnLoading.value = false;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> updatePassword() async {
|
||||
if (formKey.currentState?.validate() ?? false) {
|
||||
ChangePasswordRequestModel model = ChangePasswordRequestModel(
|
||||
username: userProfile.value.data?.mobile,
|
||||
password: newPasswordController.text,
|
||||
);
|
||||
|
||||
await safeCall(
|
||||
call: () async => await rootLogic.chickenRepository.updatePassword(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
model: model,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void clearPasswordForm() {
|
||||
oldPasswordController.clear();
|
||||
newPasswordController.clear();
|
||||
retryNewPasswordController.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,638 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_chicken/chicken.dart';
|
||||
import 'package:rasadyar_chicken/data/di/chicken_di.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/iran_province_city/iran_province_city_model.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/user_profile/user_profile.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
import 'logic.dart';
|
||||
|
||||
class ProfilePage extends GetView<ProfileLogic> {
|
||||
const ProfilePage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
spacing: 30,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Container(
|
||||
color: AppColor.blueNormal,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Row(),
|
||||
ObxValue((data) {
|
||||
final status = data.value.status;
|
||||
|
||||
if (status == ResourceStatus.loading) {
|
||||
return Container(
|
||||
width: 128.w,
|
||||
height: 128.h,
|
||||
child: Center(child: CupertinoActivityIndicator(color: AppColor.greenNormal)),
|
||||
);
|
||||
}
|
||||
|
||||
if (status == ResourceStatus.error) {
|
||||
return Container(
|
||||
width: 128.w,
|
||||
height: 128.h,
|
||||
child: Center(child: Text('خطا در دریافت اطلاعات')),
|
||||
);
|
||||
}
|
||||
|
||||
// Default UI
|
||||
return Container(
|
||||
width: 128.w,
|
||||
height: 128.h,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: AppColor.blueLightActive,
|
||||
),
|
||||
child: Center(
|
||||
child: CircleAvatar(
|
||||
radius: 64.w,
|
||||
backgroundImage: NetworkImage(data.value.data!.image!),
|
||||
),
|
||||
),
|
||||
);
|
||||
}, controller.userProfile),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 16,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 10),
|
||||
child: userProfileInformation(),
|
||||
),
|
||||
),
|
||||
|
||||
Center(
|
||||
child: Wrap(
|
||||
alignment: WrapAlignment.center,
|
||||
spacing: 20,
|
||||
runSpacing: 10,
|
||||
children: [
|
||||
cardActionWidget(
|
||||
title: 'تغییر رمز عبور',
|
||||
selected: true,
|
||||
onPressed: () {
|
||||
Get.bottomSheet(changePasswordBottomSheet(), isScrollControlled: true);
|
||||
},
|
||||
icon: Assets.vec.lockSvg.path,
|
||||
),
|
||||
cardActionWidget(
|
||||
title: 'خروج',
|
||||
selected: true,
|
||||
color: ColorFilter.mode(Colors.redAccent, BlendMode.srcIn),
|
||||
cardColor: Color(0xFFEFEFEF),
|
||||
textColor: AppColor.redDarkerText,
|
||||
onPressed: () {
|
||||
Get.bottomSheet(exitBottomSheet(), isScrollControlled: true);
|
||||
},
|
||||
icon: Assets.vec.logoutSvg.path,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 100),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Container invoiceIssuanceInformation() => Container();
|
||||
|
||||
Widget bankInformationWidget() => Column(
|
||||
spacing: 16,
|
||||
children: [
|
||||
itemList(title: 'نام بانک', content: 'سامان'),
|
||||
itemList(title: 'نام صاحب حساب', content: 'رضا رضایی'),
|
||||
itemList(title: 'شماره کارت ', content: '54154545415'),
|
||||
itemList(title: 'شماره حساب', content: '62565263263652'),
|
||||
itemList(title: 'شماره شبا', content: '62565263263652'),
|
||||
],
|
||||
);
|
||||
|
||||
Widget userProfileInformation() {
|
||||
return ObxValue((data) {
|
||||
if (data.value.status == ResourceStatus.loading) {
|
||||
return LoadingWidget();
|
||||
} else if (data.value.status == ResourceStatus.error) {
|
||||
return ErrorWidget('خطا در دریافت اطلاعات کاربر');
|
||||
} else if (data.value.status == ResourceStatus.success) {
|
||||
UserProfile item = data.value.data!;
|
||||
return Column(
|
||||
spacing: 6,
|
||||
children: [
|
||||
buildRowOnTapped(
|
||||
onTap: () {
|
||||
Get.bottomSheet(
|
||||
userInformationBottomSheet(),
|
||||
isScrollControlled: true,
|
||||
ignoreSafeArea: false,
|
||||
);
|
||||
},
|
||||
titleWidget: Column(
|
||||
spacing: 3,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'اطلاعات هویتی',
|
||||
style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
Container(width: 37.w, height: 1.h, color: AppColor.greenNormal),
|
||||
],
|
||||
),
|
||||
valueWidget: Assets.vec.editSvg.svg(
|
||||
width: 24.w,
|
||||
height: 24.h,
|
||||
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
itemList(
|
||||
title: 'نام و نام خانوادگی',
|
||||
content: item.fullname ?? 'نامشخص',
|
||||
icon: Assets.vec.userSvg.path,
|
||||
hasColoredBox: true,
|
||||
),
|
||||
itemList(
|
||||
title: 'موبایل',
|
||||
content: item.mobile ?? 'نامشخص',
|
||||
icon: Assets.vec.callSvg.path,
|
||||
),
|
||||
itemList(
|
||||
title: 'کدملی',
|
||||
content: item.nationalId ?? 'نامشخص',
|
||||
icon: Assets.vec.tagUserSvg.path,
|
||||
),
|
||||
itemList(
|
||||
title: 'شماره شناسنامه',
|
||||
content: item.nationalCode ?? 'نامشخص',
|
||||
icon: Assets.vec.userSquareSvg.path,
|
||||
),
|
||||
itemList(
|
||||
title: 'تاریخ تولد',
|
||||
content: item.birthday?.toJalali.formatCompactDate() ?? 'نامشخص',
|
||||
icon: Assets.vec.calendarSvg.path,
|
||||
),
|
||||
itemList(
|
||||
title: 'استان',
|
||||
content: item.province ?? 'نامشخص',
|
||||
icon: Assets.vec.pictureFrameSvg.path,
|
||||
),
|
||||
itemList(title: 'شهر', content: item.city ?? 'نامشخص', icon: Assets.vec.mapSvg.path),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
}, controller.userProfile);
|
||||
}
|
||||
|
||||
Widget itemList({
|
||||
required String title,
|
||||
required String content,
|
||||
String? icon,
|
||||
bool hasColoredBox = false,
|
||||
}) => Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.h, vertical: 6.h),
|
||||
decoration: BoxDecoration(
|
||||
color: hasColoredBox ? AppColor.greenLight : Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: hasColoredBox
|
||||
? Border.all(width: 0.25, color: AppColor.bgDark)
|
||||
: Border.all(width: 0, color: Colors.transparent),
|
||||
),
|
||||
child: Row(
|
||||
spacing: 4,
|
||||
children: [
|
||||
if (icon != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: SvgGenImage.vec(icon).svg(
|
||||
width: 20.w,
|
||||
height: 20.h,
|
||||
colorFilter: ColorFilter.mode(AppColor.mediumGreyNormalActive, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
Text(title, style: AppFonts.yekan12.copyWith(color: AppColor.mediumGreyNormalActive)),
|
||||
Spacer(),
|
||||
Text(content, style: AppFonts.yekan13.copyWith(color: AppColor.mediumGreyNormalHover)),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Widget cardActionWidget({
|
||||
required String title,
|
||||
required VoidCallback onPressed,
|
||||
required String icon,
|
||||
bool selected = false,
|
||||
ColorFilter? color,
|
||||
Color? cardColor,
|
||||
Color? textColor,
|
||||
}) {
|
||||
return GestureDetector(
|
||||
onTap: onPressed,
|
||||
child: Column(
|
||||
spacing: 4,
|
||||
children: [
|
||||
Container(
|
||||
width: 52,
|
||||
height: 52,
|
||||
padding: EdgeInsets.all(8),
|
||||
decoration: ShapeDecoration(
|
||||
color: cardColor ?? AppColor.blueLight,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
),
|
||||
child: SvgGenImage.vec(icon).svg(
|
||||
width: 40,
|
||||
height: 40,
|
||||
colorFilter:
|
||||
color ??
|
||||
ColorFilter.mode(
|
||||
selected ? AppColor.blueNormal : AppColor.whiteLight,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 2),
|
||||
Text(
|
||||
title,
|
||||
style: AppFonts.yekan10.copyWith(
|
||||
color: textColor ?? (selected ? AppColor.blueNormal : AppColor.blueLightActive),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget userInformationBottomSheet() {
|
||||
return BaseBottomSheet(
|
||||
height: 750.h,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Text(
|
||||
'ویرایش اطلاعات هویتی',
|
||||
style: AppFonts.yekan16Bold.copyWith(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: [
|
||||
RTextField(
|
||||
controller: controller.nameController,
|
||||
label: 'نام',
|
||||
borderColor: AppColor.darkGreyLight,
|
||||
filledColor: AppColor.bgLight,
|
||||
filled: true,
|
||||
),
|
||||
RTextField(
|
||||
controller: controller.lastNameController,
|
||||
label: 'نام خانوادگی',
|
||||
borderColor: AppColor.darkGreyLight,
|
||||
filledColor: AppColor.bgLight,
|
||||
filled: true,
|
||||
),
|
||||
RTextField(
|
||||
controller: controller.nationalCodeController,
|
||||
label: 'شماره شناسنامه',
|
||||
borderColor: AppColor.darkGreyLight,
|
||||
filledColor: AppColor.bgLight,
|
||||
filled: true,
|
||||
),
|
||||
RTextField(
|
||||
controller: controller.nationalIdController,
|
||||
label: 'کد ملی',
|
||||
borderColor: AppColor.darkGreyLight,
|
||||
filledColor: AppColor.bgLight,
|
||||
filled: true,
|
||||
),
|
||||
|
||||
ObxValue((data) {
|
||||
return RTextField(
|
||||
controller: controller.birthdayController,
|
||||
label: 'تاریخ تولد',
|
||||
initText: data.value?.formatCompactDate() ?? '',
|
||||
borderColor: AppColor.darkGreyLight,
|
||||
filledColor: AppColor.bgLight,
|
||||
filled: true,
|
||||
onTap: () {},
|
||||
);
|
||||
}, controller.birthDate),
|
||||
|
||||
SizedBox(),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(),
|
||||
|
||||
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: 8,
|
||||
children: [
|
||||
Text(
|
||||
'عکس پروفایل',
|
||||
style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
ObxValue((data) {
|
||||
return Container(
|
||||
width: Get.width,
|
||||
height: 270,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.lightGreyNormal,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(width: 1, color: AppColor.blackLight),
|
||||
),
|
||||
child: Center(
|
||||
child: data.value == null
|
||||
? Padding(
|
||||
padding: const EdgeInsets.fromLTRB(30, 10, 10, 30),
|
||||
child: Image.network(
|
||||
controller.userProfile.value.data?.image ?? '',
|
||||
),
|
||||
)
|
||||
: Image.file(File(data.value!.path), fit: BoxFit.cover),
|
||||
),
|
||||
);
|
||||
}, controller.selectedImage),
|
||||
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
RElevated(
|
||||
text: 'گالری',
|
||||
width: 150.w,
|
||||
height: 40.h,
|
||||
textStyle: AppFonts.yekan20.copyWith(color: Colors.white),
|
||||
onPressed: () async {
|
||||
controller.selectedImage.value = await controller.imagePicker.pickImage(
|
||||
source: ImageSource.gallery,
|
||||
imageQuality: 60,
|
||||
maxWidth: 1080,
|
||||
maxHeight: 720,
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
ROutlinedElevated(
|
||||
text: 'دوربین',
|
||||
width: 150.w,
|
||||
height: 40.h,
|
||||
textStyle: AppFonts.yekan20.copyWith(color: AppColor.blueNormal),
|
||||
onPressed: () async {
|
||||
controller.selectedImage.value = await controller.imagePicker.pickImage(
|
||||
source: ImageSource.camera,
|
||||
imageQuality: 60,
|
||||
maxWidth: 1080,
|
||||
maxHeight: 720,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Row(
|
||||
spacing: 16,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
ObxValue((data) {
|
||||
return RElevated(
|
||||
height: 40.h,
|
||||
text: 'ویرایش',
|
||||
isLoading: data.value,
|
||||
onPressed: () async {
|
||||
await controller.updateUserProfile();
|
||||
controller.getUserProfile();
|
||||
Get.back();
|
||||
},
|
||||
);
|
||||
}, controller.isOnLoading),
|
||||
ROutlinedElevated(
|
||||
height: 40.h,
|
||||
text: 'انصراف',
|
||||
borderColor: AppColor.blueNormal,
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _provinceWidget() {
|
||||
return Obx(() {
|
||||
return OverlayDropdownWidget<IranProvinceCityModel>(
|
||||
items: controller.rootLogic.provinces,
|
||||
onChanged: (value) {
|
||||
controller.selectedProvince.value = value;
|
||||
},
|
||||
selectedItem: controller.selectedProvince.value,
|
||||
itemBuilder: (item) => Text(item.name ?? 'بدون نام'),
|
||||
labelBuilder: (item) => Text(item?.name ?? 'انتخاب استان'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _cityWidget() {
|
||||
return ObxValue((data) {
|
||||
return OverlayDropdownWidget<IranProvinceCityModel>(
|
||||
items: data,
|
||||
onChanged: (value) {
|
||||
controller.selectedCity.value = value;
|
||||
},
|
||||
selectedItem: controller.selectedCity.value,
|
||||
itemBuilder: (item) => Text(item.name ?? 'بدون نام'),
|
||||
labelBuilder: (item) => Text(item?.name ?? 'انتخاب شهر'),
|
||||
);
|
||||
}, controller.cites);
|
||||
}
|
||||
|
||||
Widget changePasswordBottomSheet() {
|
||||
return BaseBottomSheet(
|
||||
height: 400.h,
|
||||
child: SingleChildScrollView(
|
||||
child: Form(
|
||||
key: controller.formKey,
|
||||
child: Column(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Text(
|
||||
'تغییر رمز عبور',
|
||||
style: AppFonts.yekan16Bold.copyWith(color: AppColor.darkGreyDarkHover),
|
||||
),
|
||||
SizedBox(),
|
||||
RTextField(
|
||||
controller: controller.oldPasswordController,
|
||||
hintText: 'رمز عبور قبلی',
|
||||
borderColor: AppColor.darkGreyLight,
|
||||
filledColor: AppColor.bgLight,
|
||||
filled: true,
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'رمز عبور را وارد کنید';
|
||||
} else if (controller.userProfile.value.data?.password != value) {
|
||||
return 'رمز عبور صحیح نیست';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
RTextField(
|
||||
controller: controller.newPasswordController,
|
||||
hintText: 'رمز عبور جدید',
|
||||
borderColor: AppColor.darkGreyLight,
|
||||
filledColor: AppColor.bgLight,
|
||||
filled: true,
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'رمز عبور را وارد کنید';
|
||||
} else if (value.length < 6) {
|
||||
return 'رمز عبور باید بیش از 6 کارکتر باشد.';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
RTextField(
|
||||
controller: controller.retryNewPasswordController,
|
||||
hintText: 'تکرار رمز عبور جدید',
|
||||
borderColor: AppColor.darkGreyLight,
|
||||
filledColor: AppColor.bgLight,
|
||||
filled: true,
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'رمز عبور را وارد کنید';
|
||||
} else if (value.length < 6) {
|
||||
return 'رمز عبور باید بیش از 6 کارکتر باشد.';
|
||||
} else if (controller.newPasswordController.text != value) {
|
||||
return 'رمز عبور جدید یکسان نیست';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
|
||||
SizedBox(),
|
||||
|
||||
Row(
|
||||
spacing: 16,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
RElevated(
|
||||
height: 40.h,
|
||||
text: 'ویرایش',
|
||||
onPressed: () async {
|
||||
if (controller.formKey.currentState?.validate() != true) {
|
||||
return;
|
||||
}
|
||||
await controller.updatePassword();
|
||||
controller.getUserProfile();
|
||||
controller.clearPasswordForm();
|
||||
Get.back();
|
||||
},
|
||||
),
|
||||
ROutlinedElevated(
|
||||
height: 40.h,
|
||||
text: 'انصراف',
|
||||
borderColor: AppColor.blueNormal,
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget exitBottomSheet() {
|
||||
return BaseBottomSheet(
|
||||
height: 220.h,
|
||||
child: SingleChildScrollView(
|
||||
child: Form(
|
||||
key: controller.formKey,
|
||||
child: Column(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Text('خروج', style: AppFonts.yekan16Bold.copyWith(color: AppColor.error)),
|
||||
SizedBox(),
|
||||
Text(
|
||||
'آیا مطمئن هستید که میخواهید از حساب کاربری خود خارج شوید؟',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan16Bold.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
|
||||
SizedBox(),
|
||||
|
||||
Row(
|
||||
spacing: 16,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
RElevated(
|
||||
height: 40.h,
|
||||
text: 'خروج',
|
||||
backgroundColor: AppColor.error,
|
||||
onPressed: () async {
|
||||
await controller.rootLogic.tokenService.deleteTokens().then((value){
|
||||
Get.back();
|
||||
Get.offAllNamed(ChickenRoutes.auth, arguments: Module.chicken);
|
||||
});
|
||||
},
|
||||
),
|
||||
ROutlinedElevated(
|
||||
height: 40.h,
|
||||
text: 'انصراف',
|
||||
borderColor: AppColor.blueNormal,
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
162
packages/chicken/lib/presentation/pages/steward/root/logic.dart
Normal file
162
packages/chicken/lib/presentation/pages/steward/root/logic.dart
Normal file
@@ -0,0 +1,162 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:rasadyar_chicken/data/data_source/local/chicken_local.dart';
|
||||
import 'package:rasadyar_chicken/data/data_source/local/chicken_local_imp.dart';
|
||||
import 'package:rasadyar_chicken/data/di/chicken_di.dart';
|
||||
import 'package:rasadyar_chicken/data/models/local/widely_used_local_model.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/inventory/inventory_model.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/iran_province_city/iran_province_city_model.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/roles_products/roles_products.dart';
|
||||
import 'package:rasadyar_chicken/data/repositories/chicken/chicken_repository.dart';
|
||||
import 'package:rasadyar_chicken/data/repositories/chicken/chicken_repository_imp.dart';
|
||||
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/buy/view.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/home/view.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/profile/view.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/sale/view.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/segmentation/view.dart';
|
||||
import 'package:rasadyar_chicken/presentation/routes/routes.dart';
|
||||
import 'package:rasadyar_chicken/presentation/utils/utils.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
enum ErrorLocationType { serviceDisabled, permissionDenied, none }
|
||||
|
||||
class StewardRootLogic extends GetxController {
|
||||
RxInt currentPage = 2.obs;
|
||||
List<Widget> pages = [BuyPage(), SalePage(), HomePage(), SegmentationPage(), ProfilePage()];
|
||||
|
||||
final defaultRoutes = <int, String>{0: ChickenRoutes.buySteward, 1: ChickenRoutes.saleSteward};
|
||||
RxList<ProductModel> rolesProductsModel = RxList<ProductModel>();
|
||||
Rxn<WidelyUsedLocalModel> widelyUsedList = Rxn<WidelyUsedLocalModel>();
|
||||
|
||||
late DioRemote dioRemote;
|
||||
var tokenService = Get.find<TokenStorageService>();
|
||||
late ChickenRepository chickenRepository;
|
||||
late ChickenLocalDataSource localDatasource;
|
||||
|
||||
RxList<ErrorLocationType> errorLocationType = RxList();
|
||||
RxMap<int, dynamic> inventoryExpandedList = RxMap();
|
||||
Rxn<InventoryModel> inventoryModel = Rxn<InventoryModel>();
|
||||
RxList<IranProvinceCityModel> provinces = <IranProvinceCityModel>[].obs;
|
||||
|
||||
// Cancel tokens for API calls
|
||||
CancelToken? _inventoryCancelToken;
|
||||
CancelToken? _provincesCancelToken;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
localDatasource = diChicken.get<ChickenLocalDataSource>();
|
||||
chickenRepository = diChicken.get<ChickenRepository>();
|
||||
|
||||
/*localDatasource.openBox().then((value) async {
|
||||
widelyUsedList.value = localDatasource.getAllWidely();
|
||||
});*/
|
||||
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
|
||||
if (provinces.isEmpty) {
|
||||
getProvinces();
|
||||
}
|
||||
if (inventoryModel.value == null) {
|
||||
getInventory();
|
||||
}
|
||||
if (rolesProductsModel.isEmpty) {
|
||||
getRolesProducts();
|
||||
}
|
||||
|
||||
if (widelyUsedList.value?.hasInit != true) {
|
||||
localDatasource.initWidleyUsed().then((value) => localDatasource.getAllWidely());
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
// Cancel any ongoing requests when controller is disposed
|
||||
_inventoryCancelToken?.cancel();
|
||||
_provincesCancelToken?.cancel();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
void toggleExpanded(int index) {
|
||||
if (inventoryExpandedList.keys.contains(index)) {
|
||||
inventoryExpandedList.remove(index);
|
||||
} else {
|
||||
inventoryExpandedList[index] = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getInventory() async {
|
||||
// Cancel previous request if still running
|
||||
_inventoryCancelToken?.cancel();
|
||||
_inventoryCancelToken = CancelToken();
|
||||
|
||||
await safeCall<List<InventoryModel>?>(
|
||||
call: () async => await chickenRepository.getInventory(
|
||||
token: tokenService.accessToken.value!,
|
||||
cancelToken: _inventoryCancelToken,
|
||||
),
|
||||
onSuccess: (result) {
|
||||
if (result != null) {
|
||||
inventoryModel.value = result.first;
|
||||
}
|
||||
},
|
||||
onError: (error, stackTrace) {
|
||||
if (error is DioException && error.type == DioExceptionType.cancel) {
|
||||
// Request was cancelled, ignore the error
|
||||
return;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void rootErrorHandler(DioException error) {
|
||||
handleGeneric(error, () {
|
||||
tokenService.deleteTokens();
|
||||
});
|
||||
}
|
||||
|
||||
void changePage(int index) {
|
||||
currentPage.value = index;
|
||||
}
|
||||
|
||||
Future<void> getProvinces() async {
|
||||
// Cancel previous request if still running
|
||||
_provincesCancelToken?.cancel();
|
||||
_provincesCancelToken = CancelToken();
|
||||
|
||||
try {
|
||||
final res = await chickenRepository.getProvince(cancelToken: _provincesCancelToken);
|
||||
if (res != null) {
|
||||
provinces.clear();
|
||||
provinces.value = res;
|
||||
}
|
||||
} catch (e) {
|
||||
if (e is DioException && e.type == DioExceptionType.cancel) {
|
||||
// Request was cancelled, ignore the error
|
||||
return;
|
||||
}
|
||||
provinces.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getRolesProducts() async {
|
||||
safeCall(
|
||||
call: () async =>
|
||||
await chickenRepository.getRolesProducts(token: tokenService.accessToken.value!),
|
||||
onSuccess: (result) {
|
||||
if (result != null) {
|
||||
rolesProductsModel.value = result;
|
||||
}
|
||||
},
|
||||
onError: (error, stacktrace) {},
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
642
packages/chicken/lib/presentation/pages/steward/root/view.dart
Normal file
642
packages/chicken/lib/presentation/pages/steward/root/view.dart
Normal file
@@ -0,0 +1,642 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:rasadyar_chicken/chicken.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/kill_house_distribution_info/kill_house_distribution_info.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/root/logic.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
class StewardRootPage extends GetView<StewardRootLogic> {
|
||||
StewardRootPage({super.key});
|
||||
|
||||
DateTime? _lastBackPressed;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ObxValue((data) {
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
onPopInvokedWithResult: (didPop, result) async {
|
||||
final nestedKey = Get.nestedKey(controller.currentPage.value);
|
||||
final currentNavigator = nestedKey?.currentState;
|
||||
|
||||
if (currentNavigator?.canPop() ?? false) {
|
||||
currentNavigator?.pop();
|
||||
} else {
|
||||
final now = DateTime.now();
|
||||
if (_lastBackPressed == null ||
|
||||
now.difference(_lastBackPressed!) > Duration(seconds: 2)) {
|
||||
_lastBackPressed = now;
|
||||
Get.snackbar(
|
||||
'خروج از برنامه',
|
||||
'برای خروج دوباره بازگشت را بزنید',
|
||||
snackPosition: SnackPosition.TOP,
|
||||
duration: Duration(seconds: 2),
|
||||
backgroundColor: AppColor.warning,
|
||||
);
|
||||
} else {
|
||||
await SystemNavigator.pop();
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Scaffold(
|
||||
backgroundColor: AppColor.bgLight,
|
||||
body: IndexedStack(
|
||||
children: [
|
||||
Navigator(
|
||||
key: Get.nestedKey(0),
|
||||
onGenerateRoute: (settings) {
|
||||
final page = ChickenPages.pages.firstWhere(
|
||||
(e) => e.name == settings.name,
|
||||
orElse: () => ChickenPages.pages.firstWhere((e) => e.name == ChickenRoutes.buySteward),
|
||||
);
|
||||
|
||||
return buildRouteFromGetPage(page);
|
||||
},
|
||||
),
|
||||
Navigator(
|
||||
key: Get.nestedKey(1),
|
||||
onGenerateRoute: (settings) {
|
||||
final page = ChickenPages.pages.firstWhere(
|
||||
(e) => e.name == settings.name,
|
||||
orElse: () =>
|
||||
ChickenPages.pages.firstWhere((e) => e.name == ChickenRoutes.saleSteward),
|
||||
);
|
||||
|
||||
return buildRouteFromGetPage(page);
|
||||
},
|
||||
),
|
||||
Navigator(
|
||||
key: Get.nestedKey(2),
|
||||
onGenerateRoute: (settings) => GetPageRoute(page: () => controller.pages[2]),
|
||||
),
|
||||
Navigator(
|
||||
key: Get.nestedKey(3),
|
||||
onGenerateRoute: (settings) => GetPageRoute(page: () => controller.pages[3]),
|
||||
),
|
||||
Navigator(
|
||||
key: Get.nestedKey(4),
|
||||
onGenerateRoute: (settings) => GetPageRoute(page: () => controller.pages[4]),
|
||||
),
|
||||
],
|
||||
index: data.value,
|
||||
),
|
||||
|
||||
bottomNavigationBar: RBottomNavigation(
|
||||
items: [
|
||||
RBottomNavigationItem(
|
||||
label: 'خرید',
|
||||
icon: Assets.vec.buySvg.path,
|
||||
isSelected: controller.currentPage.value == 0,
|
||||
onTap: () {
|
||||
Get.nestedKey(1)?.currentState?.popUntil((route) => route.isFirst);
|
||||
|
||||
controller.changePage(0);
|
||||
},
|
||||
),
|
||||
RBottomNavigationItem(
|
||||
label: 'فروش',
|
||||
icon: Assets.vec.saleSvg.path,
|
||||
isSelected: controller.currentPage.value == 1,
|
||||
onTap: () {
|
||||
Get.nestedKey(0)?.currentState?.popUntil((route) => route.isFirst);
|
||||
controller.changePage(1);
|
||||
},
|
||||
),
|
||||
RBottomNavigationItem(
|
||||
label: 'خانه',
|
||||
icon: Assets.vec.homeSvg.path,
|
||||
isSelected: controller.currentPage.value == 2,
|
||||
onTap: () {
|
||||
Get.nestedKey(1)?.currentState?.popUntil((route) => route.isFirst);
|
||||
Get.nestedKey(0)?.currentState?.popUntil((route) => route.isFirst);
|
||||
controller.changePage(2);
|
||||
},
|
||||
),
|
||||
RBottomNavigationItem(
|
||||
label: 'قطعه بندی',
|
||||
icon: Assets.vec.convertCubeSvg.path,
|
||||
isSelected: controller.currentPage.value == 3,
|
||||
onTap: () {
|
||||
Get.nestedKey(1)?.currentState?.popUntil((route) => route.isFirst);
|
||||
Get.nestedKey(0)?.currentState?.popUntil((route) => route.isFirst);
|
||||
controller.changePage(3);
|
||||
},
|
||||
),
|
||||
RBottomNavigationItem(
|
||||
label: 'پروفایل',
|
||||
icon: Assets.vec.profileCircleSvg.path,
|
||||
isSelected: controller.currentPage.value == 4,
|
||||
onTap: () {
|
||||
Get.nestedKey(1)?.currentState?.popUntil((route) => route.isFirst);
|
||||
Get.nestedKey(0)?.currentState?.popUntil((route) => route.isFirst);
|
||||
|
||||
controller.changePage(4);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}, controller.currentPage);
|
||||
}
|
||||
|
||||
Container _todayShipmentWidget() {
|
||||
return Container(
|
||||
height: 70,
|
||||
width: Get.width / 2,
|
||||
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(8)),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [const Color(0xFFEAEFFF), Colors.white],
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 4,
|
||||
children: [
|
||||
Assets.icons.cubeScan.svg(width: 30.w, height: 30),
|
||||
Text(
|
||||
'بارهای امروز',
|
||||
textAlign: TextAlign.right,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 4,
|
||||
children: [
|
||||
Text(
|
||||
'2،225،256',
|
||||
textAlign: TextAlign.right,
|
||||
style: AppFonts.yekan16.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
Text(
|
||||
'کیلوگرم',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Container _informationLabelCard({
|
||||
required String title,
|
||||
required String description,
|
||||
String unit = 'کیلوگرم',
|
||||
required String iconPath,
|
||||
required Color iconColor,
|
||||
required Color bgDescriptionColor,
|
||||
required Color bgLabelColor,
|
||||
}) {
|
||||
return Container(
|
||||
height: 82,
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: Row(
|
||||
children: [
|
||||
// Left side with icon and title
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 82,
|
||||
decoration: BoxDecoration(
|
||||
color: bgLabelColor,
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(8),
|
||||
bottomRight: Radius.circular(8),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 4,
|
||||
children: [
|
||||
SvgGenImage.vec(iconPath).svg(
|
||||
width: 24,
|
||||
height: 24,
|
||||
colorFilter: ColorFilter.mode(iconColor, BlendMode.srcIn),
|
||||
),
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.right,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.mediumGreyDarkActive),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// Right side with description and unit
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: bgDescriptionColor,
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(8),
|
||||
bottomLeft: Radius.circular(8),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 4,
|
||||
children: [
|
||||
Text(
|
||||
description,
|
||||
textAlign: TextAlign.right,
|
||||
style: AppFonts.yekan16.copyWith(color: AppColor.mediumGreyDarkActive),
|
||||
),
|
||||
Text(
|
||||
unit,
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.mediumGreyDarkActive),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Container _informationIconCard({
|
||||
required String title,
|
||||
required String description,
|
||||
String unit = 'کیلوگرم',
|
||||
required String iconPath,
|
||||
required Color iconColor,
|
||||
required Color bgDescriptionColor,
|
||||
required Color bgLabelColor,
|
||||
}) {
|
||||
return Container(
|
||||
height: 110,
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: Stack(
|
||||
alignment: Alignment.topCenter,
|
||||
children: [
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
left: 0,
|
||||
child: Container(
|
||||
height: 91,
|
||||
decoration: BoxDecoration(
|
||||
color: bgDescriptionColor,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(width: 0.25, color: const Color(0xFFB4B4B4)),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 4,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.right,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.mediumGreyDarkActive),
|
||||
),
|
||||
Text(
|
||||
description,
|
||||
textAlign: TextAlign.right,
|
||||
style: AppFonts.yekan16.copyWith(color: AppColor.mediumGreyDarkActive),
|
||||
),
|
||||
Text(
|
||||
unit,
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.mediumGreyDarkActive),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: ShapeDecoration(
|
||||
color: bgLabelColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
side: BorderSide(width: 0.25, color: const Color(0xFFD5D5D5)),
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: SvgGenImage.vec(iconPath).svg(
|
||||
width: 24,
|
||||
height: 24,
|
||||
colorFilter: ColorFilter.mode(iconColor, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget widelyUsed({
|
||||
required String title,
|
||||
required String iconPath,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
spacing: 4,
|
||||
children: [
|
||||
Container(
|
||||
width: 48,
|
||||
height: 48,
|
||||
padding: EdgeInsets.all(4),
|
||||
decoration: ShapeDecoration(
|
||||
color: const Color(0xFFBECDFF),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
),
|
||||
child: Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: ShapeDecoration(
|
||||
color: AppColor.blueNormal,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
),
|
||||
child: SvgGenImage.vec(iconPath).svg(
|
||||
width: 24,
|
||||
height: 24,
|
||||
colorFilter: ColorFilter.mode(Colors.white, BlendMode.srcIn),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(title, style: AppFonts.yekan10.copyWith(color: AppColor.blueNormal)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget addWidelyUsed({required VoidCallback onTap}) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
spacing: 4,
|
||||
children: [
|
||||
Container(
|
||||
width: 48,
|
||||
height: 48,
|
||||
padding: EdgeInsets.all(4),
|
||||
decoration: ShapeDecoration(
|
||||
color: const Color(0xFFD9F7F0),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
),
|
||||
child: Assets.vec.messageAddSvg.svg(
|
||||
width: 40,
|
||||
height: 40,
|
||||
colorFilter: ColorFilter.mode(AppColor.greenNormal, BlendMode.srcIn),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
Text('افزودن', style: AppFonts.yekan10.copyWith(color: AppColor.greenDarkHover)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/*Column oldPage() {
|
||||
return Column(
|
||||
children: [
|
||||
inventoryWidget(),
|
||||
ObxValue((data) => broadcastInformationWidget(data.value), controller.killHouseDistributionInfo),
|
||||
SizedBox(height: 20),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
cardWidget(
|
||||
title: 'ورود به انبار',
|
||||
iconPath: Assets.icons.whareHouse.path,
|
||||
onTap: () {
|
||||
Get.toNamed(ChickenRoutes.enteringTheWarehouse);
|
||||
},
|
||||
),
|
||||
cardWidget(
|
||||
title: 'فروش داخل استان',
|
||||
iconPath: Assets.icons.inside.path,
|
||||
onTap: () {
|
||||
Get.toNamed(ChickenRoutes.salesInProvince);
|
||||
},
|
||||
),
|
||||
cardWidget(
|
||||
title: 'فروش خارج استان',
|
||||
iconPath: Assets.icons.outside.path,
|
||||
onTap: () {
|
||||
Get.toNamed(ChickenRoutes.salesOutOfProvince);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget inventoryWidget() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 20),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Text('موجودی انبار', style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal)),
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
ObxValue(
|
||||
(data) =>
|
||||
data.isEmpty
|
||||
? Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 2),
|
||||
height: 80,
|
||||
padding: EdgeInsets.all(6),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: AppColor.blueNormal, width: 1),
|
||||
),
|
||||
child: Center(child: CircularProgressIndicator()),
|
||||
)
|
||||
: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: controller.inventoryList.length,
|
||||
separatorBuilder: (context, index) => const SizedBox(height: 8),
|
||||
itemBuilder: (context, index) {
|
||||
return ObxValue((expand) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
controller.toggleExpanded(index);
|
||||
},
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: AnimatedContainer(
|
||||
onEnd: () {
|
||||
controller.inventoryExpandedList[index] = !controller.inventoryExpandedList[index]!;
|
||||
},
|
||||
margin: const EdgeInsets.symmetric(vertical: 2),
|
||||
padding: EdgeInsets.all(6),
|
||||
curve: Curves.easeInOut,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: AppColor.blueNormal, width: 1),
|
||||
),
|
||||
duration: const Duration(seconds: 1),
|
||||
height: expand.keys.contains(index) ? 250 : 80,
|
||||
child: inventoryItem(
|
||||
isExpanded: expand.keys.contains(index) && expand[index]!,
|
||||
index: index,
|
||||
model: controller.inventoryList[index],
|
||||
),
|
||||
),
|
||||
);
|
||||
}, controller.inventoryExpandedList);
|
||||
},
|
||||
),
|
||||
controller.inventoryList,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget inventoryItem({required bool isExpanded, required int index, required InventoryModel model}) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 8,
|
||||
children: [
|
||||
buildRow('نام محصول', model.name ?? ''),
|
||||
Visibility(
|
||||
visible: isExpanded,
|
||||
child: Column(
|
||||
spacing: 8,
|
||||
children: [
|
||||
buildRow('وزن خریدهای دولتی داخل استان (کیلوگرم)', '0326598653'),
|
||||
buildRow('وزن خریدهای آزاد داخل استان (کیلوگرم)', model.receiveFreeCarcassesWeight.toString()),
|
||||
buildRow('وزن خریدهای خارج استان (کیلوگرم)', model.freeBuyingCarcassesWeight.toString()),
|
||||
buildRow('کل ورودی به انبار (کیلوگرم)', model.totalFreeBarsCarcassesWeight.toString()),
|
||||
buildRow('کل فروش (کیلوگرم)', model.realAllocatedWeight.toString()),
|
||||
buildRow('مانده انبار (کیلوگرم)', model.totalRemainWeight.toString()),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}*/
|
||||
|
||||
Widget buildRow(String title, String value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
title,
|
||||
textAlign: TextAlign.right,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDarkHover),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
flex: 1,
|
||||
child: Text(
|
||||
value,
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDarkHover),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget broadcastInformationWidget(KillHouseDistributionInfo? model) {
|
||||
return Container(
|
||||
height: 140,
|
||||
margin: const EdgeInsets.all(8),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: AppColor.blueNormal, width: 1),
|
||||
),
|
||||
child: model != null
|
||||
? Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 10,
|
||||
children: [
|
||||
Text(
|
||||
'اطلاعات ارسالی',
|
||||
textAlign: TextAlign.right,
|
||||
style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
buildRow(
|
||||
'فروش و توزیع داخل استان (کیلوگرم)',
|
||||
model.stewardAllocationsWeight!.toInt().toString(),
|
||||
),
|
||||
buildRow(
|
||||
'فروش و توزیع خارج استان (کیلوگرم)',
|
||||
model.freeSalesWeight!.toInt().toString(),
|
||||
),
|
||||
],
|
||||
)
|
||||
: const Center(child: CircularProgressIndicator()),
|
||||
);
|
||||
}
|
||||
|
||||
Widget cardWidget({
|
||||
required String title,
|
||||
required String iconPath,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
return Container(
|
||||
width: Get.width / 4,
|
||||
height: 130,
|
||||
child: GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Card(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(width: 1, color: AppColor.blueNormal),
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SvgGenImage(iconPath).svg(width: 50, height: 50),
|
||||
SizedBox(height: 4),
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
117
packages/chicken/lib/presentation/pages/steward/sale/logic.dart
Normal file
117
packages/chicken/lib/presentation/pages/steward/sale/logic.dart
Normal file
@@ -0,0 +1,117 @@
|
||||
|
||||
import 'package:rasadyar_chicken/data/models/request/conform_allocation/conform_allocation.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/allocated_made/allocated_made.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/guild/guild_model.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/roles_products/roles_products.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/steward_free_bar_dashboard/steward_free_bar_dashboard.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/root/logic.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
class SaleLogic extends GetxController {
|
||||
Rxn<List<AllocatedMadeModel>?> allocatedMadeModel = Rxn<List<AllocatedMadeModel>?>();
|
||||
|
||||
RxList<GuildModel> guildsModel = <GuildModel>[].obs;
|
||||
|
||||
Rxn<StewardFreeBarDashboard> stewardFreeDashboard = Rxn<StewardFreeBarDashboard>();
|
||||
|
||||
StewardRootLogic rootLogic = Get.find<StewardRootLogic>();
|
||||
|
||||
List<String> routesName = ['فروش'];
|
||||
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
getStewardDashBord();
|
||||
|
||||
}
|
||||
|
||||
Future<void> getAllocatedMade() async {
|
||||
safeCall(
|
||||
call: () async => await rootLogic.chickenRepository.getAllocatedMade(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
queryParameters: buildQueryParams(page: 1, pageSize: 20, search: 'filter', role: 'Steward'),
|
||||
),
|
||||
onSuccess: (result) {
|
||||
if (result != null) {
|
||||
allocatedMadeModel.value = result.results;
|
||||
}
|
||||
},
|
||||
onError: (error, stacktrace) {},
|
||||
);
|
||||
}
|
||||
|
||||
void checkVerfication() {}
|
||||
|
||||
void confirmAllocation(ConformAllocation allocation) {
|
||||
safeCall(
|
||||
call: () async => await rootLogic.chickenRepository.confirmAllocation(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
allocation: allocation.toJson(),
|
||||
),
|
||||
onSuccess: (result) {
|
||||
getAllocatedMade();
|
||||
},
|
||||
onError: (error, stacktrace) {},
|
||||
);
|
||||
}
|
||||
|
||||
void denyAllocation(String token) {
|
||||
safeCall(
|
||||
call: () async => await rootLogic.chickenRepository.denyAllocation(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
allocationToken: token,
|
||||
),
|
||||
onSuccess: (result) {
|
||||
getAllocatedMade();
|
||||
},
|
||||
onError: (error, stacktrace) {},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> confirmAllAllocations() async {
|
||||
safeCall(
|
||||
call: () async => await rootLogic.chickenRepository.confirmAllAllocation(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
allocationTokens: allocatedMadeModel.value?.map((e) => e.key!).toList() ?? [],
|
||||
),
|
||||
onSuccess: (result) {
|
||||
getAllocatedMade();
|
||||
},
|
||||
onError: (error, stacktrace) {},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Future<void> getGuilds() async {}
|
||||
|
||||
Future<void> addSale() async {}
|
||||
|
||||
void setSelectedGuild(GuildModel value) {}
|
||||
|
||||
void setSelectedProduct(ProductModel value) {}
|
||||
|
||||
Future<void> getStewardDashBord() async {
|
||||
safeCall(
|
||||
call: () async => await rootLogic.chickenRepository.getStewardDashboard(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
stratDate: DateTime.now().formattedDashedGregorian,
|
||||
endDate: DateTime.now().formattedDashedGregorian,
|
||||
),
|
||||
onSuccess: (result) {
|
||||
if (result != null) {
|
||||
stewardFreeDashboard.value = result;
|
||||
}
|
||||
},
|
||||
onError: (error, stacktrace) {},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> submitAllocation() async {}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
rootLogic.inventoryExpandedList.clear();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
186
packages/chicken/lib/presentation/pages/steward/sale/view.dart
Normal file
186
packages/chicken/lib/presentation/pages/steward/sale/view.dart
Normal file
@@ -0,0 +1,186 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_chicken/chicken.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/steward_free_bar_dashboard/steward_free_bar_dashboard.dart';
|
||||
import 'package:rasadyar_chicken/presentation/widget/base_page/view.dart';
|
||||
import 'package:rasadyar_chicken/presentation/widget/sale_buy_card_item.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
import 'logic.dart';
|
||||
|
||||
class SalePage extends GetView<SaleLogic> {
|
||||
SalePage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BasePage(
|
||||
routes: controller.routesName,
|
||||
isBase: true,
|
||||
widgets: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Wrap(
|
||||
alignment: WrapAlignment.center,
|
||||
spacing: 14.w,
|
||||
children: [
|
||||
saleOrBuyItemCard(
|
||||
title: 'فروش داخل استان',
|
||||
iconPath: Assets.vec.cubeSvg.path,
|
||||
onTap: () {
|
||||
Get.toNamed(ChickenRoutes.salesInProvinceSteward, id: 1);
|
||||
},
|
||||
),
|
||||
saleOrBuyItemCard(
|
||||
title: 'فروش خارج استان',
|
||||
iconPath: Assets.vec.truckFastSvg.path,
|
||||
onTap: () {
|
||||
Get.toNamed(ChickenRoutes.salesOutOfProvinceSteward, id: 1);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget addSaleOutOfTheProvinceBottomSheet() {
|
||||
return BaseBottomSheet(
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 20),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Text(
|
||||
'ثبت فروش خارج استان',
|
||||
style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
RElevated(text: 'ثبت توزیع/ فروش', onPressed: () {}),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _typeOuterInfoCard({
|
||||
required String title,
|
||||
required String iconPath,
|
||||
required Color foregroundColor,
|
||||
VoidCallback? onTap,
|
||||
}) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
height: 180,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(width: 1, color: foregroundColor),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Positioned(
|
||||
top: -41,
|
||||
child: SvgGenImage.vec(iconPath).svg(
|
||||
width: 45,
|
||||
height: 45,
|
||||
colorFilter: ColorFilter.mode(foregroundColor, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
|
||||
Assets.vec.shoppingBasketSvg.svg(
|
||||
width: 55,
|
||||
height: 60,
|
||||
colorFilter: ColorFilter.mode(foregroundColor, BlendMode.srcIn),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.right,
|
||||
style: AppFonts.yekan16Bold.copyWith(color: foregroundColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget summaryOfInformation(StewardFreeBarDashboard? model) {
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
'خلاصه اطلاعات',
|
||||
style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 140,
|
||||
margin: const EdgeInsets.all(8),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: AppColor.blueNormal, width: 1),
|
||||
),
|
||||
child: model == null
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 10,
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
buildRow('تعداد کل بارها', model.totalQuantity?.toString() ?? '0'),
|
||||
buildRow('تعداد کل', model.totalBars?.toString() ?? '0'),
|
||||
buildRow('وزن کل (کیلوگرم)', model.totalWeight?.toString() ?? '0'),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildRow(String title, String value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
title,
|
||||
textAlign: TextAlign.right,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDarkHover),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
value,
|
||||
textAlign: TextAlign.left,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDarkHover),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,419 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:rasadyar_chicken/data/models/request/conform_allocation/conform_allocation.dart';
|
||||
import 'package:rasadyar_chicken/data/models/request/submit_steward_allocation/submit_steward_allocation.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/allocated_made/allocated_made.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/guild/guild_model.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/guild_profile/guild_profile.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/roles_products/roles_products.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/root/logic.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/sale/logic.dart';
|
||||
import 'package:rasadyar_chicken/presentation/utils/string_utils.dart';
|
||||
import 'package:rasadyar_chicken/presentation/utils/utils.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
class SalesInProvinceLogic extends GetxController {
|
||||
StewardRootLogic rootLogic = Get.find<StewardRootLogic>();
|
||||
SaleLogic saleLogic = Get.find<SaleLogic>();
|
||||
RxnString searchedValue = RxnString();
|
||||
RxList<int> isExpandedList = <int>[].obs;
|
||||
RxList<String> routesName = RxList();
|
||||
Rx<Color> bgConfirmAllColor = AppColor.blueNormal.obs;
|
||||
final RxBool isLoadingMoreAllocationsMade = false.obs;
|
||||
Timer? _flashingTimer;
|
||||
|
||||
Rx<Resource<PaginationModel<AllocatedMadeModel>>> allocatedList =
|
||||
Resource<PaginationModel<AllocatedMadeModel>>.loading().obs;
|
||||
|
||||
RxList<ProductModel> rolesProductsModel = RxList<ProductModel>();
|
||||
|
||||
RxList<GuildModel> guildsModel = <GuildModel>[].obs;
|
||||
|
||||
GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
|
||||
Rxn<Jalali> fromDateFilter = Rxn<Jalali>(null);
|
||||
Rxn<Jalali> toDateFilter = Rxn<Jalali>(null);
|
||||
Rxn<ProductModel> selectedProductModel = Rxn<ProductModel>();
|
||||
Rxn<GuildModel> selectedGuildModel = Rxn<GuildModel>();
|
||||
Rxn<GuildProfile> guildProfile = Rxn<GuildProfile>();
|
||||
RxInt saleType = 1.obs;
|
||||
RxInt weight = 0.obs;
|
||||
RxInt pricePerKilo = 0.obs;
|
||||
RxInt totalCost = 0.obs;
|
||||
RxBool isValid = false.obs;
|
||||
final weightController = TextEditingController();
|
||||
final pricePerKiloController = TextEditingController();
|
||||
final totalCostController = TextEditingController();
|
||||
|
||||
final ScrollController scrollControllerAllocationsMade = ScrollController();
|
||||
final RxInt currentPage = 1.obs;
|
||||
final RxBool addPageAllocationsMade = false.obs;
|
||||
final RxBool hasMoreDataAllocationsMade = true.obs;
|
||||
|
||||
Rxn<AllocatedMadeModel> selectedAllocationModelForUpdate = Rxn<AllocatedMadeModel>();
|
||||
SubmitStewardAllocation? tmpStewardAllocation;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
routesName.value = [...saleLogic.routesName, 'داخل استان'].toList();
|
||||
getAllocatedMade();
|
||||
getRolesProducts();
|
||||
getGuilds();
|
||||
getGuildProfile();
|
||||
ever(saleType, (callback) {
|
||||
getGuilds();
|
||||
});
|
||||
debounce(weight, time: Duration(milliseconds: 110), (callback) {
|
||||
totalCost.value = callback * weight.value;
|
||||
});
|
||||
|
||||
debounce(pricePerKilo, time: Duration(milliseconds: 100), (callback) {
|
||||
totalCost.value = callback * weight.value;
|
||||
});
|
||||
|
||||
|
||||
totalCost.listen((data) {
|
||||
totalCostController.text = data
|
||||
.toString()
|
||||
.separatedByComma;
|
||||
|
||||
isValid.value =
|
||||
weight.value > 0 &&
|
||||
pricePerKilo.value > 0 &&
|
||||
totalCost.value > 0 &&
|
||||
selectedProductModel.value != null &&
|
||||
selectedGuildModel.value != null;
|
||||
});
|
||||
everAll([
|
||||
totalCost,
|
||||
weight,
|
||||
pricePerKilo,
|
||||
totalCost,
|
||||
selectedProductModel,
|
||||
selectedGuildModel
|
||||
], (callback) => checkVerification(),);
|
||||
|
||||
scrollControllerAllocationsMade.addListener(() {
|
||||
if (scrollControllerAllocationsMade.position.pixels >=
|
||||
scrollControllerAllocationsMade.position.maxScrollExtent - 100) {
|
||||
addPageAllocationsMade.value = true;
|
||||
getAllocatedMade();
|
||||
}
|
||||
});
|
||||
|
||||
debounce(
|
||||
searchedValue,
|
||||
(callback) => getAllocatedMade(),
|
||||
time: Duration(milliseconds: timeDebounce),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> getAllocatedMade([bool isLoadingMore = false]) async {
|
||||
if (isLoadingMore) {
|
||||
isLoadingMoreAllocationsMade.value = true;
|
||||
} else {
|
||||
allocatedList.value = Resource<PaginationModel<AllocatedMadeModel>>.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(
|
||||
call: () async =>
|
||||
await rootLogic.chickenRepository.getAllocatedMade(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
queryParameters: buildQueryParams(
|
||||
page: currentPage.value,
|
||||
pageSize: 20,
|
||||
search: 'filter',
|
||||
role: 'Steward',
|
||||
value: searchedValue.value,
|
||||
),
|
||||
),
|
||||
onSuccess: (res) async {
|
||||
await Future.delayed(Duration(milliseconds: 200));
|
||||
if ((res?.count ?? 0) == 0) {
|
||||
allocatedList.value = Resource<PaginationModel<AllocatedMadeModel>>.empty();
|
||||
} else {
|
||||
allocatedList.value = Resource<PaginationModel<AllocatedMadeModel>>.success(
|
||||
PaginationModel<AllocatedMadeModel>(
|
||||
count: res?.count ?? 0,
|
||||
next: res?.next,
|
||||
previous: res?.previous,
|
||||
results: isLoadingMore
|
||||
? [...(allocatedList.value.data?.results ?? []), ...(res?.results ?? [])]
|
||||
: res?.results ?? [],
|
||||
),
|
||||
);
|
||||
isLoadingMoreAllocationsMade.value = false;
|
||||
if ((allocatedList.value.data?.results?.length ?? 0) > 1) {
|
||||
flashingFabBgColor();
|
||||
}
|
||||
}
|
||||
},
|
||||
onError: (error, stacktrace) {
|
||||
isLoadingMoreAllocationsMade.value = false;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void checkVerification() {
|
||||
isValid.value =
|
||||
weight.value > 0 &&
|
||||
pricePerKilo.value > 0 &&
|
||||
totalCost.value > 0 &&
|
||||
selectedProductModel.value != null &&
|
||||
selectedGuildModel.value != null;
|
||||
}
|
||||
|
||||
void confirmAllocation(ConformAllocation allocation) {
|
||||
safeCall(
|
||||
call: () async =>
|
||||
await rootLogic.chickenRepository.confirmAllocation(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
allocation: allocation.toJson(),
|
||||
),
|
||||
onSuccess: (result) {
|
||||
getAllocatedMade();
|
||||
},
|
||||
onError: (error, stacktrace) {},
|
||||
);
|
||||
}
|
||||
|
||||
void denyAllocation(String token) {
|
||||
safeCall(
|
||||
call: () async =>
|
||||
await rootLogic.chickenRepository.denyAllocation(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
allocationToken: token,
|
||||
),
|
||||
onSuccess: (result) {
|
||||
getAllocatedMade();
|
||||
},
|
||||
onError: (error, stacktrace) {},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> confirmAllAllocations() async {
|
||||
safeCall(
|
||||
call: () async =>
|
||||
await rootLogic.chickenRepository.confirmAllAllocation(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
allocationTokens: allocatedList.value.data?.results?.map((e) => e.key!).toList() ?? [],
|
||||
),
|
||||
onSuccess: (result) {
|
||||
getAllocatedMade();
|
||||
},
|
||||
onError: (error, stacktrace) {},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> getRolesProducts() async {
|
||||
safeCall(
|
||||
call: () async =>
|
||||
await rootLogic.chickenRepository.getRolesProducts(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
),
|
||||
onSuccess: (result) {
|
||||
if (result != null) {
|
||||
rolesProductsModel.value = result;
|
||||
selectedProductModel.value = result.first;
|
||||
}
|
||||
},
|
||||
onError: (error, stacktrace) {},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> getGuilds() async {
|
||||
safeCall(
|
||||
call: () async =>
|
||||
await rootLogic.chickenRepository.getGuilds(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
queryParameters: buildQueryParams(
|
||||
queryParams: {'free': saleType.value == 2 ? true : false},
|
||||
role: 'Steward',
|
||||
),
|
||||
),
|
||||
onSuccess: (result) {
|
||||
if (result != null) {
|
||||
guildsModel.clear();
|
||||
guildsModel.addAll(result);
|
||||
}
|
||||
},
|
||||
onError: (error, stacktrace) {},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> addSale() async {}
|
||||
|
||||
void setSelectedGuild(GuildModel value) {
|
||||
selectedGuildModel.value = value;
|
||||
update();
|
||||
}
|
||||
|
||||
void setSelectedProduct(ProductModel value) {
|
||||
selectedProductModel.value = value;
|
||||
update();
|
||||
}
|
||||
|
||||
Future<void> getGuildProfile() async {
|
||||
await safeCall(
|
||||
call: () async =>
|
||||
await rootLogic.chickenRepository.getProfile(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
),
|
||||
onError: (error, stackTrace) {},
|
||||
onSuccess: (result) {
|
||||
guildProfile.value = result;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void setSubmitData() {
|
||||
tmpStewardAllocation = SubmitStewardAllocation(
|
||||
approvedPriceStatus: false,
|
||||
allocationType:
|
||||
'${guildProfile.value?.steward == true ? "steward" : "guild"}_${selectedGuildModel.value
|
||||
?.steward == true ? "steward" : "guild"}',
|
||||
sellerType: guildProfile.value?.steward == true ? "Steward" : "Guild",
|
||||
buyerType: selectedGuildModel.value?.steward == true ? "Steward" : "Guild",
|
||||
amount: pricePerKilo.value,
|
||||
totalAmount: totalCost.value,
|
||||
weightOfCarcasses: weight.value,
|
||||
sellType: saleType.value == 2 ? "free" : 'exclusive',
|
||||
numberOfCarcasses: 0,
|
||||
guildKey: selectedGuildModel.value?.key,
|
||||
productKey: selectedProductModel.value?.key,
|
||||
date: DateTime
|
||||
.now()
|
||||
.formattedDashedGregorian,
|
||||
type: "manual",
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> submitAllocation() async {
|
||||
safeCall(
|
||||
call: () async =>
|
||||
await rootLogic.chickenRepository.postSubmitStewardAllocation(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
request: tmpStewardAllocation!,
|
||||
),
|
||||
|
||||
onSuccess: (result) {
|
||||
getAllocatedMade();
|
||||
},
|
||||
onError: (error, stackTrace) {},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> deleteAllocation(AllocatedMadeModel model) async {
|
||||
safeCall(
|
||||
call: () async =>
|
||||
await rootLogic.chickenRepository.deleteStewardAllocation(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
queryParameters: {'steward_allocation_key': model.key},
|
||||
),
|
||||
|
||||
onSuccess: (result) {
|
||||
getAllocatedMade();
|
||||
},
|
||||
onError: (error, stackTrace) {},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
rootLogic.inventoryExpandedList.clear();
|
||||
stopFlashing();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void setEditData(AllocatedMadeModel item) {
|
||||
selectedAllocationModelForUpdate.value = item;
|
||||
selectedProductModel.value = rolesProductsModel.first;
|
||||
selectedGuildModel.value = GuildModel(guildsName: 'tst');
|
||||
weight.value = item.weightOfCarcasses ?? 0;
|
||||
pricePerKilo.value = item.amount ?? 0;
|
||||
totalCost.value = item.totalAmount ?? 0;
|
||||
weightController.text = weight.value
|
||||
.toString()
|
||||
.separatedByComma;
|
||||
pricePerKiloController.text = pricePerKilo.value
|
||||
.toString()
|
||||
.separatedByComma;
|
||||
totalCostController.text = totalCost.value
|
||||
.toString()
|
||||
.separatedByComma;
|
||||
isValid.value = true;
|
||||
}
|
||||
|
||||
void clearForm() {
|
||||
selectedGuildModel.value = null;
|
||||
weight.value = 0;
|
||||
pricePerKilo.value = 0;
|
||||
totalCost.value = 0;
|
||||
weightController.clear();
|
||||
pricePerKiloController.clear();
|
||||
totalCostController.clear();
|
||||
isValid.value = false;
|
||||
}
|
||||
|
||||
Future<void> updateAllocation() async {
|
||||
ConformAllocation updatedAllocationModel = ConformAllocation(
|
||||
allocation_key: selectedAllocationModelForUpdate.value?.key,
|
||||
amount: pricePerKilo.value,
|
||||
total_amount: totalCost.value,
|
||||
number_of_carcasses: 0,
|
||||
weight_of_carcasses: weight.value,
|
||||
);
|
||||
|
||||
safeCall(
|
||||
call: () async =>
|
||||
await rootLogic.chickenRepository.updateStewardAllocation(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
request: updatedAllocationModel,
|
||||
),
|
||||
|
||||
onSuccess: (result) {
|
||||
getAllocatedMade();
|
||||
},
|
||||
onError: (error, stackTrace) {},
|
||||
);
|
||||
}
|
||||
|
||||
void setSearchValue(String? data) {
|
||||
searchedValue.value = data?.trim();
|
||||
}
|
||||
|
||||
void flashingFabBgColor() {
|
||||
_flashingTimer?.cancel();
|
||||
|
||||
_flashingTimer = Timer.periodic(Duration(seconds: 2), (timer) {
|
||||
if (bgConfirmAllColor.value == AppColor.blueNormal) {
|
||||
bgConfirmAllColor.value = AppColor.blueLightHover;
|
||||
} else {
|
||||
bgConfirmAllColor.value = AppColor.blueNormal;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void stopFlashing() {
|
||||
_flashingTimer?.cancel();
|
||||
_flashingTimer = null;
|
||||
bgConfirmAllColor.value = AppColor.blueNormal; // بازگرداندن به رنگ پیشفرض
|
||||
}
|
||||
|
||||
Steward? getBuyerInformation(AllocatedMadeModel model) {
|
||||
if (model.allocationType?.buyerIsGuild) {
|
||||
return model.toGuilds;
|
||||
} else {
|
||||
return model.steward;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,831 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/allocated_made/allocated_made.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/guild/guild_model.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/roles_products/roles_products.dart';
|
||||
import 'package:rasadyar_chicken/presentation/utils/string_utils.dart';
|
||||
import 'package:rasadyar_chicken/presentation/widget/base_page/view.dart';
|
||||
import 'package:rasadyar_chicken/presentation/widget/steward/inventory_widget.dart';
|
||||
|
||||
import 'package:rasadyar_chicken/presentation/widget/page_route.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
import 'logic.dart';
|
||||
|
||||
class SalesInProvincePage extends GetView<SalesInProvinceLogic> {
|
||||
SalesInProvincePage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: BasePage(
|
||||
routesWidget: ObxValue((route) => buildPageRoute(route), controller.routesName),
|
||||
onBackPressed: () => Get.back(id: 1),
|
||||
onSearchChanged: (data) => controller.setSearchValue(data),
|
||||
filteringWidget: filterBottomSheet(),
|
||||
widgets: [
|
||||
inventoryWidget(controller.rootLogic),
|
||||
Expanded(
|
||||
child: ObxValue((data) {
|
||||
return RPaginatedListView(
|
||||
listType: ListType.separated,
|
||||
resource: data.value,
|
||||
hasMore: data.value.data?.next != null,
|
||||
isPaginating: controller.isLoadingMoreAllocationsMade.value,
|
||||
onRefresh: () async {
|
||||
controller.currentPage.value = 1;
|
||||
await controller.getAllocatedMade();
|
||||
},
|
||||
onLoadMore: () async {
|
||||
controller.currentPage.value++;
|
||||
iLog(controller.currentPage.value);
|
||||
await controller.getAllocatedMade(true);
|
||||
},
|
||||
padding: EdgeInsets.fromLTRB(8, 8, 8, 80),
|
||||
itemBuilder: (context, index) {
|
||||
var item = data.value.data!.results![index];
|
||||
return ObxValue((val) {
|
||||
return ExpandableListItem2(
|
||||
selected: val.contains(index),
|
||||
onTap: () => controller.isExpandedList.toggle(index),
|
||||
index: index,
|
||||
child: itemListWidget(item),
|
||||
secondChild: itemListExpandedWidget(item, index),
|
||||
labelColor: AppColor.blueLight,
|
||||
labelIcon: Assets.vec.timerSvg.path,
|
||||
labelIconColor: item.registrationCode == null
|
||||
? AppColor.darkGreyDark
|
||||
: AppColor.error,
|
||||
);
|
||||
}, controller.isExpandedList);
|
||||
},
|
||||
itemCount: data.value.data?.results?.length ?? 0,
|
||||
separatorBuilder: (context, index) => SizedBox(height: 8.h),
|
||||
);
|
||||
}, controller.allocatedList),
|
||||
),
|
||||
],
|
||||
),
|
||||
floatingActionButton: SizedBox(
|
||||
width: Get.width - 30,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
RFab.add(
|
||||
onPressed: () {
|
||||
Get.bottomSheet(
|
||||
addOrEditBottomSheet(),
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
).whenComplete(() {
|
||||
controller.clearForm();
|
||||
});
|
||||
},
|
||||
),
|
||||
|
||||
ObxValue((data) {
|
||||
return Visibility(
|
||||
visible: (data.value.data?.results?.length ?? 0) > 1,
|
||||
child: AnimatedFab(
|
||||
onPressed: () async {
|
||||
Get.defaultDialog(
|
||||
title: 'تایید یکجا',
|
||||
middleText: 'آیا از تایید تمامی تخصیص ها اطمینان دارید؟',
|
||||
confirm: ElevatedButton(
|
||||
onPressed: () async {
|
||||
await controller.confirmAllAllocations();
|
||||
controller.getAllocatedMade();
|
||||
controller.rootLogic.getInventory();
|
||||
Get.back();
|
||||
},
|
||||
child: Text('تایید'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColor.blueNormal,
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
),
|
||||
),
|
||||
cancel: OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: AppColor.error,
|
||||
enableFeedback: true,
|
||||
side: BorderSide(color: AppColor.error, width: 1),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
),
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
child: Text('لغو'),
|
||||
),
|
||||
);
|
||||
},
|
||||
message: 'تایید یکجا',
|
||||
icon: Assets.vec.clipboardTaskSvg.svg(width: 40.w, height: 40.h),
|
||||
backgroundColor: controller.bgConfirmAllColor.value,
|
||||
),
|
||||
);
|
||||
}, controller.allocatedList),
|
||||
],
|
||||
),
|
||||
),
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.startFloat,
|
||||
);
|
||||
}
|
||||
|
||||
Row itemListWidget(AllocatedMadeModel item) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
SizedBox(width: 20),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 4,
|
||||
children: [
|
||||
Text(
|
||||
controller.getBuyerInformation(item)?.user?.fullname ?? 'N/Aشششش',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
|
||||
SizedBox(height: 2),
|
||||
Text(
|
||||
item.createDate?.formattedJalaliDate ?? 'N/A',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.bgDark),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
spacing: 6,
|
||||
children: [
|
||||
Visibility(
|
||||
visible: item.product?.name?.contains('مرغ گرم') ?? false,
|
||||
child: Assets.vec.hotChickenSvg.svg(
|
||||
width: 24,
|
||||
height: 24,
|
||||
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${item.weightOfCarcasses?.separatedByComma}kg',
|
||||
textAlign: TextAlign.left,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 2),
|
||||
Text(
|
||||
'${item.amount.separatedByComma} ریال',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.darkGreyDark),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(width: 8),
|
||||
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Assets.vec.scanSvg.svg(
|
||||
width: 32.w,
|
||||
height: 32.h,
|
||||
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Container itemListExpandedWidget(AllocatedMadeModel item, int index) {
|
||||
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(
|
||||
controller.getBuyerInformation(item)?.user?.fullname ?? 'N/A',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan16.copyWith(color: AppColor.greenDark),
|
||||
),
|
||||
Spacer(),
|
||||
Text(
|
||||
item.registrationCode == null ? 'در انتظار' : 'در انتظار تایید خریدار',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan10.copyWith(
|
||||
color: item.registrationCode == null ? AppColor.darkGreyDark : AppColor.error,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 7),
|
||||
Assets.vec.clockSvg.svg(
|
||||
width: 16.w,
|
||||
height: 16.h,
|
||||
colorFilter: ColorFilter.mode(
|
||||
item.registrationCode == null ? AppColor.darkGreyDark : AppColor.error,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
height: 32,
|
||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||
decoration: ShapeDecoration(
|
||||
color: AppColor.blueLight,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(width: 1, color: AppColor.blueLightHover),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 3,
|
||||
children: [
|
||||
Text(
|
||||
item.date?.toJalali.formatter.wN ?? 'N/A',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
|
||||
Text(
|
||||
'${item.date?.toJalali.formatter.d} ${item.date?.toJalali.formatter.mN ?? 'N/A'}',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Text(
|
||||
'${item.date?.toJalali.formatter.y}',
|
||||
style: AppFonts.yekan20.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
|
||||
Text(
|
||||
'${item.date?.toJalali.formatter.tHH}:${item.date?.toJalali.formatter.tMM ?? 'N/A'}',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
/* buildRow(
|
||||
title: 'مشخصات خریدار',
|
||||
value: controller.getBuyerInformation(item)?.user?.fullname ?? 'N/A',
|
||||
),*/
|
||||
|
||||
buildRow(
|
||||
title: 'تلفن خریدار',
|
||||
value: controller.getBuyerInformation(item)?.user?.mobile ?? 'N/A',
|
||||
valueStyle: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
buildRow(title: 'نوع فروش', value: item.sellType?.faItem ?? 'N/A'),
|
||||
buildRow(title: 'محصول', value: item.product?.name ?? 'N/A'),
|
||||
buildRow(
|
||||
title: 'نوع تخصیص',
|
||||
value: item.allocationType?.faAllocationType ?? 'N/A',
|
||||
),
|
||||
buildRow(
|
||||
title: 'وزن خریداری شده',
|
||||
value: '${item.weightOfCarcasses?.separatedByComma} کیلوگرم',
|
||||
),
|
||||
buildRow(
|
||||
title: 'افت وزن(کیلوگرم)',
|
||||
value: item.weightLossOfCarcasses?.toInt().toString() ?? 'N/A',
|
||||
),
|
||||
buildRow(title: 'قیمت کل', value: '${item.totalAmount?.separatedByComma} ریال'),
|
||||
|
||||
buildRow(title: 'کداحراز', value: item.registrationCode?.toString() ?? 'ندارد'),
|
||||
buildRow(
|
||||
title: 'وضعیت کد احراز',
|
||||
value: item.systemRegistrationCode == true ? "ارسال شده" : "ارسال نشده",
|
||||
),
|
||||
|
||||
Visibility(
|
||||
visible: item.registrationCode == null,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 16.w,
|
||||
children: [
|
||||
RElevated(
|
||||
text: 'ویرایش',
|
||||
width: 150.w,
|
||||
height: 40.h,
|
||||
onPressed: () {
|
||||
controller.setEditData(item);
|
||||
Get.bottomSheet(
|
||||
addOrEditBottomSheet(true),
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
).whenComplete(() {
|
||||
controller.clearForm();
|
||||
});
|
||||
},
|
||||
textStyle: AppFonts.yekan20.copyWith(color: Colors.white),
|
||||
backgroundColor: AppColor.greenNormal,
|
||||
),
|
||||
ROutlinedElevated(
|
||||
text: 'حذف',
|
||||
textStyle: AppFonts.yekan20.copyWith(color: AppColor.redNormal),
|
||||
width: 150.w,
|
||||
height: 40.h,
|
||||
onPressed: () {
|
||||
buildDeleteDialog(
|
||||
onConfirm: () async {
|
||||
controller.isExpandedList.remove(index);
|
||||
// controller.denyAllocation(item.key ?? '');
|
||||
//await controller.deleteAllocation(item);
|
||||
},
|
||||
onRefresh: () => controller.getAllocatedMade(),
|
||||
);
|
||||
},
|
||||
borderColor: AppColor.redNormal,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget addOrEditBottomSheet([bool isEditMode = false]) {
|
||||
return BaseBottomSheet(
|
||||
height: Get.height * (isEditMode ? 0.45 : 0.75),
|
||||
child: Form(
|
||||
key: controller.formKey,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
'${isEditMode ? 'ویرایش' : 'ثبت'} توزیع/ فروش',
|
||||
style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
productDropDown(),
|
||||
const SizedBox(height: 12),
|
||||
Visibility(
|
||||
visible: isEditMode == false,
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: AppColor.darkGreyLight, width: 1),
|
||||
),
|
||||
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
SizedBox(
|
||||
height: 40,
|
||||
child: ObxValue((data) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Radio(
|
||||
value: 1,
|
||||
groupValue: controller.saleType.value,
|
||||
onChanged: (value) {
|
||||
controller.saleType.value = value!;
|
||||
|
||||
controller.selectedGuildModel.value = null;
|
||||
controller.selectedGuildModel.refresh();
|
||||
},
|
||||
),
|
||||
Text('فروش اختصاصی', style: AppFonts.yekan14),
|
||||
SizedBox(width: 12),
|
||||
Radio(
|
||||
value: 2,
|
||||
groupValue: controller.saleType.value,
|
||||
onChanged: (value) {
|
||||
controller.saleType.value = value!;
|
||||
},
|
||||
),
|
||||
Text('فروش آزاد', style: AppFonts.yekan14),
|
||||
],
|
||||
);
|
||||
}, controller.saleType),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
guildsDropDown(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
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: [
|
||||
Visibility(
|
||||
visible: isEditMode == false,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
ObxValue((data) {
|
||||
return RTextField(
|
||||
controller: TextEditingController(),
|
||||
filledColor: AppColor.bgLight,
|
||||
filled: true,
|
||||
label: 'تاریخ',
|
||||
onTap: () {
|
||||
Get.bottomSheet(
|
||||
modalDatePicker((value) {
|
||||
controller.fromDateFilter.value = value;
|
||||
controller.fromDateFilter.refresh();
|
||||
}),
|
||||
);
|
||||
},
|
||||
borderColor: AppColor.darkGreyLight,
|
||||
initText: (data.value ?? Jalali.now()).formatCompactDate(),
|
||||
);
|
||||
}, controller.fromDateFilter),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
RTextField(
|
||||
controller: controller.weightController,
|
||||
keyboardType: TextInputType.number,
|
||||
borderColor: AppColor.darkGreyLight,
|
||||
filledColor: AppColor.bgLight,
|
||||
filled: true,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
SeparatorInputFormatter(),
|
||||
],
|
||||
validator: (value) {
|
||||
if (int.parse(value!.clearComma) >
|
||||
(controller.rootLogic.inventoryModel.value?.totalRemainWeight?.toInt() ??
|
||||
100)) {
|
||||
return 'وزن تخصیصی بیشتر از موجودی انبار است';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onChanged: (p0) {
|
||||
controller.weight.value = int.tryParse(p0.clearComma) ?? 0;
|
||||
},
|
||||
label: 'وزن لاشه',
|
||||
),
|
||||
|
||||
RTextField(
|
||||
controller: controller.pricePerKiloController,
|
||||
borderColor: AppColor.darkGreyLight,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
SeparatorInputFormatter(),
|
||||
],
|
||||
filledColor: AppColor.bgLight,
|
||||
filled: true,
|
||||
onChanged: (p0) {
|
||||
controller.pricePerKilo.value = int.tryParse(p0.clearComma) ?? 0;
|
||||
},
|
||||
keyboardType: TextInputType.number,
|
||||
label: 'قیمت هر کیلو',
|
||||
),
|
||||
|
||||
RTextField(
|
||||
variant: RTextFieldVariant.noBorder,
|
||||
enabled: false,
|
||||
keyboardType: TextInputType.number,
|
||||
filledColor: AppColor.bgLight,
|
||||
filled: true,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
SeparatorInputFormatter(),
|
||||
],
|
||||
controller: controller.totalCostController,
|
||||
label: 'هزینه کل',
|
||||
),
|
||||
|
||||
ObxValue((data) {
|
||||
return RElevated(
|
||||
text: isEditMode ? 'ویرایش' : 'ثبت',
|
||||
isFullWidth: true,
|
||||
textStyle: AppFonts.yekan16.copyWith(color: Colors.white),
|
||||
backgroundColor: AppColor.greenNormal,
|
||||
height: 40,
|
||||
onPressed: data.value
|
||||
? () async {
|
||||
if (isEditMode) {
|
||||
await controller.updateAllocation();
|
||||
controller.clearForm();
|
||||
controller.getAllocatedMade();
|
||||
controller.rootLogic.getInventory();
|
||||
Get.back();
|
||||
} else {
|
||||
if (controller.formKey.currentState?.validate() ?? false) {
|
||||
controller.setSubmitData();
|
||||
await Get.bottomSheet(show2StepAddBottomSheet());
|
||||
}
|
||||
}
|
||||
}
|
||||
: null,
|
||||
);
|
||||
}, controller.isValid),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget guildsDropDown() {
|
||||
return Obx(() {
|
||||
final item = controller.selectedGuildModel.value;
|
||||
return OverlayDropdownWidget<GuildModel>(
|
||||
key: ValueKey(item?.user?.fullname ?? ''),
|
||||
items: controller.guildsModel,
|
||||
onChanged: (value) {
|
||||
controller.selectedGuildModel.value = value;
|
||||
},
|
||||
selectedItem: item,
|
||||
itemBuilder: (item) => Text(
|
||||
item.user != null
|
||||
? '${item.steward == true ? 'مباشر' : 'صنف'} ${item.user!.fullname} (${item.user!.mobile})'
|
||||
: 'بدون نام',
|
||||
),
|
||||
labelBuilder: (item) => Text(
|
||||
item?.user != null
|
||||
? '${item?.steward == true ? 'مباشر' : 'صنف'} ${item?.user!.fullname} (${item?.user!.mobile})'
|
||||
: 'انتخاب مباشر/صنف',
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget productDropDown() {
|
||||
return Obx(() {
|
||||
return OverlayDropdownWidget<ProductModel>(
|
||||
items: controller.rolesProductsModel,
|
||||
height: 56,
|
||||
hasDropIcon: false,
|
||||
background: Colors.white,
|
||||
onChanged: (value) {
|
||||
controller.selectedProductModel.value = value;
|
||||
},
|
||||
selectedItem: controller.selectedProductModel.value,
|
||||
itemBuilder: (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}',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget filterBottomSheet() {
|
||||
return BaseBottomSheet(
|
||||
height: 200,
|
||||
child: Column(
|
||||
spacing: 16,
|
||||
children: [
|
||||
Text('فیلترها', style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal)),
|
||||
Row(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Expanded(
|
||||
child: timeFilterWidget(
|
||||
date: controller.fromDateFilter,
|
||||
onChanged: (jalali) => controller.fromDateFilter.value = jalali,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: timeFilterWidget(
|
||||
isFrom: false,
|
||||
date: controller.toDateFilter,
|
||||
onChanged: (jalali) => controller.toDateFilter.value = jalali,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
RElevated(
|
||||
text: 'اعمال فیلتر',
|
||||
isFullWidth: true,
|
||||
backgroundColor: AppColor.greenNormal,
|
||||
onPressed: () {
|
||||
controller.getAllocatedMade();
|
||||
Get.back();
|
||||
},
|
||||
height: 40,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
GestureDetector timeFilterWidget({
|
||||
isFrom = true,
|
||||
required Rxn<Jalali> date,
|
||||
required Function(Jalali jalali) onChanged,
|
||||
}) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Get.bottomSheet(modalDatePicker((value) => onChanged(value)));
|
||||
},
|
||||
child: Container(
|
||||
height: 35,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(width: 1, color: AppColor.blueNormal),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 11, vertical: 4),
|
||||
child: Row(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Assets.vec.calendarSvg.svg(
|
||||
width: 24,
|
||||
height: 24,
|
||||
colorFilter: const ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
|
||||
),
|
||||
Text(
|
||||
isFrom ? 'از' : 'تا',
|
||||
style: AppFonts.yekan16.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
Expanded(
|
||||
child: ObxValue((data) {
|
||||
return Text(
|
||||
date.value?.formatCompactDate() ?? Jalali.now().formatCompactDate(),
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan16.copyWith(color: AppColor.lightGreyNormalActive),
|
||||
);
|
||||
}, date),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Container modalDatePicker(ValueChanged<Jalali> onDateSelected) {
|
||||
Jalali? tempPickedDate;
|
||||
return Container(
|
||||
height: 250,
|
||||
color: Colors.white,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(width: 20),
|
||||
RElevated(
|
||||
height: 35,
|
||||
width: 70,
|
||||
textStyle: AppFonts.yekan14.copyWith(color: Colors.white),
|
||||
onPressed: () {
|
||||
onDateSelected(tempPickedDate ?? Jalali.now());
|
||||
Get.back();
|
||||
},
|
||||
text: 'تایید',
|
||||
),
|
||||
Spacer(),
|
||||
RElevated(
|
||||
height: 35,
|
||||
width: 70,
|
||||
backgroundColor: AppColor.error,
|
||||
textStyle: AppFonts.yekan14.copyWith(color: Colors.white),
|
||||
onPressed: () {
|
||||
onDateSelected(tempPickedDate ?? Jalali.now());
|
||||
Get.back();
|
||||
},
|
||||
text: 'لغو',
|
||||
),
|
||||
SizedBox(width: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(height: 0, thickness: 1),
|
||||
Expanded(
|
||||
child: Container(
|
||||
child: PersianCupertinoDatePicker(
|
||||
initialDateTime: Jalali.now(),
|
||||
mode: PersianCupertinoDatePickerMode.date,
|
||||
onDateTimeChanged: (dateTime) {
|
||||
tempPickedDate = dateTime;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget show2StepAddBottomSheet() {
|
||||
return BaseBottomSheet(
|
||||
height: Get.height * .39,
|
||||
child: Column(
|
||||
spacing: 8,
|
||||
children: [
|
||||
buildRow(
|
||||
title: 'تاریخ ثبت',
|
||||
value: controller.tmpStewardAllocation?.date?.formattedJalaliDate ?? 'N/A',
|
||||
),
|
||||
buildRow(
|
||||
title: 'نام و نام خانوادگی خریدار',
|
||||
value:
|
||||
controller.guildsModel
|
||||
.firstWhere((p0) => p0.key == controller.tmpStewardAllocation?.guildKey)
|
||||
.user
|
||||
?.fullname ??
|
||||
'N/A',
|
||||
),
|
||||
buildRow(
|
||||
title: 'شماره خریدار',
|
||||
value:
|
||||
controller.guildsModel
|
||||
.firstWhere((p0) => p0.key == controller.tmpStewardAllocation?.guildKey)
|
||||
.user
|
||||
?.mobile ??
|
||||
'N/A',
|
||||
),
|
||||
|
||||
buildRow(
|
||||
title: 'قیمت هر کیلو',
|
||||
value: '${controller.tmpStewardAllocation?.amount.separatedByComma ?? 0} ریال ',
|
||||
),
|
||||
buildRow(
|
||||
title: 'وزن تخصیصی',
|
||||
value:
|
||||
'${controller.tmpStewardAllocation?.weightOfCarcasses?.toInt().separatedByComma ?? 0} کیلوگرم',
|
||||
),
|
||||
buildRow(
|
||||
title: 'قیمت کل',
|
||||
value: '${controller.tmpStewardAllocation?.totalAmount.separatedByComma ?? 0} ریال',
|
||||
),
|
||||
|
||||
Row(
|
||||
spacing: 10,
|
||||
children: [
|
||||
Expanded(
|
||||
child: RElevated(
|
||||
backgroundColor: AppColor.greenNormal,
|
||||
height: 40,
|
||||
text: 'ثبت',
|
||||
textStyle: AppFonts.yekan18.copyWith(color: Colors.white),
|
||||
onPressed: () async {
|
||||
await controller.submitAllocation();
|
||||
Get
|
||||
..back()
|
||||
..back();
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ROutlinedElevated(
|
||||
height: 40,
|
||||
borderColor: AppColor.error,
|
||||
text: ' بازگشت',
|
||||
textStyle: AppFonts.yekan18.copyWith(color: AppColor.error),
|
||||
onPressed: () {
|
||||
Get
|
||||
..back()
|
||||
..back();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:rasadyar_chicken/data/models/request/steward_free_sale_bar/steward_free_sale_bar_request.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/iran_province_city/iran_province_city_model.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/out_province_carcasses_buyer/out_province_carcasses_buyer.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/roles_products/roles_products.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/steward_free_sale_bar/steward_free_sale_bar.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/root/logic.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/sale/logic.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/sales_out_of_province_buyers/logic.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/sales_out_of_province_sales_list/logic.dart';
|
||||
import 'package:rasadyar_chicken/presentation/utils/utils.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
class SalesOutOfProvinceLogic extends GetxController {
|
||||
StewardRootLogic get rootLogic => Get.find<StewardRootLogic>();
|
||||
|
||||
SaleLogic get saleLogic => Get.find<SaleLogic>();
|
||||
|
||||
SalesOutOfProvinceBuyersLogic get buyersLogic => Get.find<SalesOutOfProvinceBuyersLogic>();
|
||||
|
||||
SalesOutOfProvinceSalesListLogic get saleListLogic =>
|
||||
Get.find<SalesOutOfProvinceSalesListLogic>();
|
||||
|
||||
SalesOutOfProvinceBuyersLogic get buyerLogic => Get.find<SalesOutOfProvinceBuyersLogic>();
|
||||
|
||||
RxBool isExpanded = false.obs;
|
||||
RxInt currentPage = 1.obs;
|
||||
RxBool isSaleSubmitButtonEnabled = false.obs;
|
||||
RxList<int> isExpandedList = <int>[].obs;
|
||||
Rx<Jalali> fromDateFilter = Jalali.now().obs;
|
||||
Rx<Jalali> toDateFilter = Jalali.now().obs;
|
||||
RxnString searchedValue = RxnString();
|
||||
RxList<String> routesName = RxList();
|
||||
RxBool isLoadingMoreAllocationsMade = false.obs;
|
||||
Rxn<IranProvinceCityModel> selectedCity = Rxn();
|
||||
|
||||
|
||||
GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
TextEditingController quarantineCodeController = TextEditingController();
|
||||
TextEditingController saleWeightController = TextEditingController();
|
||||
Rx<Jalali> saleDate = Jalali.now().obs;
|
||||
String? key;
|
||||
|
||||
Rx<Resource<PaginationModel<StewardFreeSaleBar>>> salesList =
|
||||
Resource<PaginationModel<StewardFreeSaleBar>>.loading().obs;
|
||||
|
||||
Rxn<ProductModel> selectedProduct = Rxn();
|
||||
Rxn<OutProvinceCarcassesBuyer> selectedBuyer = Rxn();
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
routesName.value = [...saleLogic.routesName, 'خارج استان'].toList();
|
||||
}
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
getOutProvinceSales();
|
||||
selectedProduct.value = rootLogic.rolesProductsModel.first;
|
||||
debounce(
|
||||
searchedValue,
|
||||
(callback) => getOutProvinceSales(),
|
||||
time: Duration(milliseconds: timeDebounce),
|
||||
);
|
||||
setupListeners();
|
||||
}
|
||||
|
||||
void setSearchValue(String? value) {
|
||||
searchedValue.value = value?.trim();
|
||||
}
|
||||
|
||||
void submitFilter() {
|
||||
fromDateFilter.value = fromDateFilter.value;
|
||||
toDateFilter.value = toDateFilter.value;
|
||||
getOutProvinceSales();
|
||||
}
|
||||
|
||||
Future<void> getOutProvinceSales([bool isLoadingMore = false]) async {
|
||||
if (isLoadingMore) {
|
||||
isLoadingMoreAllocationsMade.value = true;
|
||||
} else {
|
||||
salesList.value = Resource<PaginationModel<StewardFreeSaleBar>>.loading();
|
||||
}
|
||||
await safeCall(
|
||||
call: () => rootLogic.chickenRepository.getStewardFreeSaleBar(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
queryParameters: buildQueryParams(
|
||||
pageSize: 20,
|
||||
page: currentPage.value,
|
||||
state: 'buyer-list',
|
||||
search: 'filter',
|
||||
role: 'Steward',
|
||||
value: searchedValue.value ?? '',
|
||||
fromDate: fromDateFilter.value.toDateTime(),
|
||||
toDate: toDateFilter.value.toDateTime(),
|
||||
),
|
||||
),
|
||||
onSuccess: (res) {
|
||||
if ((res?.count ?? 0) == 0) {
|
||||
salesList.value = Resource<PaginationModel<StewardFreeSaleBar>>.empty();
|
||||
} else {
|
||||
salesList.value = Resource<PaginationModel<StewardFreeSaleBar>>.success(
|
||||
PaginationModel<StewardFreeSaleBar>(
|
||||
count: res?.count ?? 0,
|
||||
next: res?.next,
|
||||
previous: res?.previous,
|
||||
results: [...(salesList.value.data?.results ?? []), ...(res?.results ?? [])],
|
||||
),
|
||||
);
|
||||
|
||||
isLoadingMoreAllocationsMade.value = false;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void setupListeners() {
|
||||
saleWeightController.addListener(checkSalesFormValid);
|
||||
quarantineCodeController.addListener(checkSalesFormValid);
|
||||
ever(selectedBuyer, (_) => checkSalesFormValid);
|
||||
ever(selectedProduct, (_) => checkSalesFormValid);
|
||||
ever(saleDate, (_) => checkSalesFormValid());
|
||||
}
|
||||
|
||||
void checkSalesFormValid() {
|
||||
isSaleSubmitButtonEnabled.value =
|
||||
saleDate.value.toString().isNotEmpty &&
|
||||
selectedProduct.value != null &&
|
||||
selectedBuyer.value != null &&
|
||||
saleWeightController.text.isNotEmpty &&
|
||||
quarantineCodeController.text.isNotEmpty;
|
||||
}
|
||||
|
||||
void setEditDataSales(StewardFreeSaleBar item) {
|
||||
quarantineCodeController.text = item.clearanceCode ?? '';
|
||||
saleWeightController.text = item.weightOfCarcasses?.toInt().toString() ?? '';
|
||||
saleDate.value = Jalali.fromDateTime(DateTime.parse(item.date!));
|
||||
selectedCity.value = IranProvinceCityModel(name: item.city);
|
||||
selectedBuyer.value = buyerLogic.buyerList.value.data?.results?.firstWhere(
|
||||
(element) => element.key == item.buyer?.key,
|
||||
);
|
||||
selectedProduct.value = rootLogic.rolesProductsModel.first;
|
||||
key = item.key;
|
||||
isSaleSubmitButtonEnabled.value = true;
|
||||
}
|
||||
|
||||
Future<void> deleteStewardPurchaseOutOfProvince(String key) async {
|
||||
await safeCall(
|
||||
call: () => rootLogic.chickenRepository.deleteStewardPurchasesOutSideOfTheProvince(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
stewardFreeBarKey: key,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> createSale() async {
|
||||
bool res = false;
|
||||
StewardFreeSaleBarRequest requestBody = StewardFreeSaleBarRequest(
|
||||
buyerKey: selectedBuyer.value?.key,
|
||||
numberOfCarcasses: 0,
|
||||
weightOfCarcasses: int.tryParse(saleWeightController.text.clearComma),
|
||||
date: saleDate.value.toDateTime().formattedDashedGregorian,
|
||||
clearanceCode: quarantineCodeController.text,
|
||||
productKey: selectedProduct.value?.key,
|
||||
);
|
||||
await safeCall(
|
||||
call: () => rootLogic.chickenRepository.createOutProvinceStewardFreeBar(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
body: requestBody,
|
||||
),
|
||||
onSuccess: (_) {
|
||||
res = true;
|
||||
},
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
void clearSaleForm() {
|
||||
quarantineCodeController.clear();
|
||||
saleWeightController.clear();
|
||||
saleDate.value = Jalali.now();
|
||||
selectedBuyer.value = null;
|
||||
selectedProduct.value = null;
|
||||
}
|
||||
|
||||
Future<bool> editSale() async {
|
||||
bool res = false;
|
||||
StewardFreeSaleBarRequest requestBody = StewardFreeSaleBarRequest(
|
||||
numberOfCarcasses: 0,
|
||||
weightOfCarcasses: int.tryParse(saleWeightController.text.clearComma),
|
||||
date: saleDate.value.toDateTime().formattedDashedGregorian,
|
||||
clearanceCode: quarantineCodeController.text,
|
||||
key: key,
|
||||
);
|
||||
await safeCall(
|
||||
call: () => rootLogic.chickenRepository.updateOutProvinceStewardFreeBar(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
body: requestBody,
|
||||
),
|
||||
onSuccess: (_) {
|
||||
res = true;
|
||||
},
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
void resetSubmitForm() {
|
||||
selectedCity.value = null;
|
||||
selectedProduct.value = null;
|
||||
key = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,525 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/out_province_carcasses_buyer/out_province_carcasses_buyer.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/roles_products/roles_products.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/steward_free_sale_bar/steward_free_sale_bar.dart';
|
||||
import 'package:rasadyar_chicken/presentation/routes/routes.dart';
|
||||
import 'package:rasadyar_chicken/presentation/widget/base_page/view.dart';
|
||||
import 'package:rasadyar_chicken/presentation/widget/filter_bottom_sheet.dart';
|
||||
import 'package:rasadyar_chicken/presentation/widget/steward/inventory_widget.dart';
|
||||
|
||||
import 'package:rasadyar_chicken/presentation/widget/page_route.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
import 'logic.dart';
|
||||
|
||||
class SalesOutOfProvincePage extends GetView<SalesOutOfProvinceLogic> {
|
||||
const SalesOutOfProvincePage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BasePage(
|
||||
routesWidget: ObxValue((route) => buildPageRoute(route), controller.routesName),
|
||||
onBackPressed: () => Get.back(id: 1),
|
||||
onSearchChanged: (data) => controller.setSearchValue(data),
|
||||
filteringWidget: filterBottomSheet(),
|
||||
widgets: [
|
||||
inventoryWidget(controller.rootLogic),
|
||||
Expanded(
|
||||
child: ObxValue((data) {
|
||||
return RPaginatedListView(
|
||||
onLoadMore: () async => controller.getOutProvinceSales(true),
|
||||
onRefresh: () async {
|
||||
controller.currentPage.value = 1;
|
||||
await controller.getOutProvinceSales();
|
||||
},
|
||||
hasMore: data.value.data?.next != null,
|
||||
listType: ListType.separated,
|
||||
resource: data.value,
|
||||
padding: EdgeInsets.fromLTRB(8, 8, 8, 80),
|
||||
itemBuilder: (context, index) {
|
||||
var item = data.value.data!.results![index];
|
||||
return ObxValue((val) {
|
||||
return ExpandableListItem2(
|
||||
selected: val.contains(index),
|
||||
onTap: () => controller.isExpandedList.toggle(index),
|
||||
index: index,
|
||||
child: itemListWidget(item),
|
||||
secondChild: itemListExpandedWidget(item, index),
|
||||
labelColor: AppColor.blueLight,
|
||||
labelIcon: Assets.vec.timerSvg.path,
|
||||
);
|
||||
}, controller.isExpandedList);
|
||||
},
|
||||
itemCount: data.value.data?.results?.length ?? 0,
|
||||
separatorBuilder: (context, index) => SizedBox(height: 8.h),
|
||||
);
|
||||
}, controller.salesList),
|
||||
),
|
||||
],
|
||||
floatingActionButton: Row(
|
||||
children: [
|
||||
RFab.add(
|
||||
onPressed: () {
|
||||
Get.bottomSheet(addOrEditSaleBottomSheet(), isScrollControlled: true);
|
||||
},
|
||||
),
|
||||
Spacer(),
|
||||
RFab(
|
||||
icon: Icon(CupertinoIcons.person_add_solid, color: Colors.white, size: 35.w),
|
||||
backgroundColor: AppColor.blueNormal,
|
||||
onPressed: () {
|
||||
Get.toNamed(ChickenRoutes.salesOutOfProvinceBuyerSteward, id: 1);
|
||||
},
|
||||
),
|
||||
SizedBox(width: 25),
|
||||
],
|
||||
),
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.startFloat,
|
||||
);
|
||||
}
|
||||
|
||||
/* Padding segmentWidget() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(8, 0, 8, 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: RSegment(
|
||||
children: ['فروش', 'خریداران'],
|
||||
selectedIndex: controller.selectedSegmentIndex.value,
|
||||
borderColor: const Color(0xFFB4B4B4),
|
||||
selectedBorderColor: AppColor.blueNormal,
|
||||
selectedBackgroundColor: AppColor.blueLight,
|
||||
onSegmentSelected: (index) => controller.selectedSegmentIndex.value = index,
|
||||
backgroundColor: AppColor.whiteGreyNormal,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}*/
|
||||
|
||||
Widget filterBottomSheet() => filterBottomSheetWidget(
|
||||
fromDate: controller.fromDateFilter,
|
||||
onChangedFromDate: (jalali) => controller.fromDateFilter.value = jalali,
|
||||
toDate: controller.toDateFilter,
|
||||
onChangedToDate: (jalali) => controller.toDateFilter.value = jalali,
|
||||
onSubmit: () => controller.submitFilter(),
|
||||
);
|
||||
|
||||
itemListWidget(StewardFreeSaleBar item) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Text(
|
||||
item.date?.formattedJalaliDate ?? 'N/A',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.bgDark),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
item.buyer?.fullname ?? 'N/A',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
|
||||
SizedBox(height: 2),
|
||||
Text(
|
||||
item.buyer?.mobile ?? 'N/A',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.bgDark),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 8,
|
||||
children: [
|
||||
Text(
|
||||
item.buyer?.unitName ?? 'N/A',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.bgDark),
|
||||
),
|
||||
Text(
|
||||
'${item.weightOfCarcasses?.separatedByComma ?? 0}KG',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.bgDark),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
'${item.buyer?.province}\n${item.buyer?.city}',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.bgDark),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
itemListExpandedWidget(StewardFreeSaleBar item, int index) {
|
||||
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.province} - ${item.city}',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan16.copyWith(color: AppColor.greenDark),
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
height: 32,
|
||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||
decoration: ShapeDecoration(
|
||||
color: AppColor.blueLight,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(width: 1, color: AppColor.blueLightHover),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 3,
|
||||
children: [
|
||||
Text(
|
||||
item.date?.toJalali.formatter.wN ?? 'N/A',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
|
||||
Text(
|
||||
'${item.date?.toJalali.formatter.d} ${item.date?.toJalali.formatter.mN ?? 'N/A'}',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Text(
|
||||
'${item.date?.toJalali.formatter.y}',
|
||||
style: AppFonts.yekan20.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
|
||||
Text(
|
||||
'${item.date?.toJalali.formatter.tHH}:${item.date?.toJalali.formatter.tMM ?? 'N/A'}',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
buildRow(title: 'مشخصات خریدار', value: item.buyer?.fullname ?? 'N/A'),
|
||||
buildRow(title: 'تلفن خریدار', value: item.buyer?.mobile ?? 'N/A'),
|
||||
buildRow(title: 'نام واحد', value: item.buyer?.unitName ?? 'N/A'),
|
||||
buildRow(title: 'وزن لاشه', value: '${item.weightOfCarcasses?.separatedByComma}'),
|
||||
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 16.w,
|
||||
children: [
|
||||
RElevated(
|
||||
text: 'ویرایش',
|
||||
width: 150.w,
|
||||
height: 40.h,
|
||||
onPressed: () {
|
||||
controller.setEditDataSales(item);
|
||||
Get.bottomSheet(
|
||||
addOrEditSaleBottomSheet(true),
|
||||
isScrollControlled: true,
|
||||
).whenComplete(() {
|
||||
controller.resetSubmitForm();
|
||||
});
|
||||
},
|
||||
textStyle: AppFonts.yekan20.copyWith(color: Colors.white),
|
||||
backgroundColor: AppColor.greenNormal,
|
||||
),
|
||||
ROutlinedElevated(
|
||||
text: 'حذف',
|
||||
textStyle: AppFonts.yekan20.copyWith(color: AppColor.redNormal),
|
||||
width: 150.w,
|
||||
height: 40.h,
|
||||
onPressed: () {
|
||||
buildDeleteDialog(
|
||||
onConfirm: () async {
|
||||
controller.isExpandedList.remove(index);
|
||||
controller.deleteStewardPurchaseOutOfProvince(item.key!);
|
||||
},
|
||||
onRefresh: () => controller.getOutProvinceSales(),
|
||||
);
|
||||
},
|
||||
borderColor: AppColor.redNormal,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget addOrEditSaleBottomSheet([bool isOnEdit = false]) {
|
||||
return BaseBottomSheet(
|
||||
height: 500.h,
|
||||
child: SingleChildScrollView(
|
||||
child: Form(
|
||||
key: controller.formKey,
|
||||
child: Column(
|
||||
spacing: 16,
|
||||
children: [
|
||||
Text(
|
||||
isOnEdit ? 'ویرایش فروش' : 'افزودن فروش',
|
||||
style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
_productDropDown(),
|
||||
|
||||
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: [
|
||||
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(),
|
||||
],
|
||||
|
||||
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(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget submitButtonWidget(bool isOnEdit) {
|
||||
return ObxValue((data) {
|
||||
return RElevated(
|
||||
isFullWidth: true,
|
||||
backgroundColor: AppColor.greenNormal,
|
||||
text: isOnEdit ? 'ویرایش' : 'ثبت',
|
||||
onPressed: data.value
|
||||
? () async {
|
||||
var res = isOnEdit ? await controller.editSale() : await controller.createSale();
|
||||
if (res) {
|
||||
controller.getOutProvinceSales();
|
||||
controller.clearSaleForm();
|
||||
Get.back();
|
||||
}
|
||||
}
|
||||
: null,
|
||||
height: 40,
|
||||
);
|
||||
}, controller.isSaleSubmitButtonEnabled);
|
||||
}
|
||||
|
||||
Widget _buyerWidget() {
|
||||
return Obx(() {
|
||||
return OverlayDropdownWidget<OutProvinceCarcassesBuyer>(
|
||||
items: controller.buyerLogic.buyerList.value.data?.results ?? [],
|
||||
onChanged: (value) {
|
||||
controller.selectedBuyer.value = value;
|
||||
},
|
||||
selectedItem: controller.selectedBuyer.value,
|
||||
itemBuilder: (item) => Text(item.buyer?.fullname ?? 'بدون نام'),
|
||||
labelBuilder: (item) => Text(item?.buyer?.fullname ?? 'انتخاب خریدار'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _productDropDown() {
|
||||
return Obx(() {
|
||||
return OverlayDropdownWidget<ProductModel>(
|
||||
items: controller.rootLogic.rolesProductsModel,
|
||||
height: 56,
|
||||
hasDropIcon: false,
|
||||
background: Colors.white,
|
||||
onChanged: (value) {
|
||||
controller.selectedProduct.value = value;
|
||||
},
|
||||
selectedItem: controller.selectedProduct.value,
|
||||
initialValue: controller.selectedProduct.value,
|
||||
itemBuilder: (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}',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
GestureDetector timeFilterWidget({
|
||||
isFrom = true,
|
||||
required Rx<Jalali> date,
|
||||
required Function(Jalali jalali) onChanged,
|
||||
}) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Get.bottomSheet(modalDatePicker((value) => onChanged(value)));
|
||||
},
|
||||
child: Container(
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.bgLight,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(width: 1, color: AppColor.darkGreyLight),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 11, vertical: 4),
|
||||
child: Row(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Assets.vec.calendarSvg.svg(
|
||||
width: 24,
|
||||
height: 24,
|
||||
colorFilter: const ColorFilter.mode(AppColor.bgDark, BlendMode.srcIn),
|
||||
),
|
||||
Text('تاریخ', style: AppFonts.yekan16.copyWith(color: AppColor.bgDark)),
|
||||
Expanded(
|
||||
child: ObxValue((data) {
|
||||
return Text(
|
||||
date.value.formatCompactDate(),
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan16.copyWith(color: AppColor.darkGreyDark),
|
||||
);
|
||||
}, date),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Container modalDatePicker(ValueChanged<Jalali> onDateSelected) {
|
||||
Jalali? tempPickedDate;
|
||||
return Container(
|
||||
height: 250,
|
||||
color: Colors.white,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(width: 20),
|
||||
RElevated(
|
||||
height: 35,
|
||||
width: 70,
|
||||
textStyle: AppFonts.yekan14.copyWith(color: Colors.white),
|
||||
onPressed: () {
|
||||
onDateSelected(tempPickedDate ?? Jalali.now());
|
||||
Get.back();
|
||||
},
|
||||
text: 'تایید',
|
||||
),
|
||||
Spacer(),
|
||||
RElevated(
|
||||
height: 35,
|
||||
width: 70,
|
||||
backgroundColor: AppColor.error,
|
||||
textStyle: AppFonts.yekan14.copyWith(color: Colors.white),
|
||||
onPressed: () {
|
||||
onDateSelected(tempPickedDate ?? Jalali.now());
|
||||
Get.back();
|
||||
},
|
||||
text: 'لغو',
|
||||
),
|
||||
SizedBox(width: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(height: 0, thickness: 1),
|
||||
Expanded(
|
||||
child: Container(
|
||||
child: PersianCupertinoDatePicker(
|
||||
initialDateTime: controller.saleDate.value,
|
||||
mode: PersianCupertinoDatePickerMode.date,
|
||||
onDateTimeChanged: (dateTime) {
|
||||
tempPickedDate = dateTime;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:rasadyar_chicken/data/models/response/iran_province_city/iran_province_city_model.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/out_province_carcasses_buyer/out_province_carcasses_buyer.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/root/logic.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/sale/logic.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/sales_out_of_province/logic.dart';
|
||||
import 'package:rasadyar_chicken/presentation/utils/utils.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
class SalesOutOfProvinceBuyersLogic extends GetxController {
|
||||
StewardRootLogic get rootLogic => Get.find<StewardRootLogic>();
|
||||
|
||||
SaleLogic get saleLogic => Get.find<SaleLogic>();
|
||||
|
||||
SalesOutOfProvinceLogic get saleOutOfProvince => Get.find<SalesOutOfProvinceLogic>();
|
||||
|
||||
RxInt currentPage = 1.obs;
|
||||
RxList<int> isExpandedList = <int>[].obs;
|
||||
Rx<Jalali> fromDateFilter = Jalali.now().obs;
|
||||
Rx<Jalali> toDateFilter = Jalali.now().obs;
|
||||
RxnString searchedValue = RxnString();
|
||||
|
||||
RxBool isLoadingMoreAllocationsMade = false.obs;
|
||||
RxBool isBuyerSubmitButtonEnabled = false.obs;
|
||||
|
||||
RxList<IranProvinceCityModel> cites = <IranProvinceCityModel>[].obs;
|
||||
Rxn<IranProvinceCityModel> selectedProvince = Rxn();
|
||||
Rxn<IranProvinceCityModel> selectedCity = Rxn();
|
||||
|
||||
GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
TextEditingController buyerNameController = TextEditingController();
|
||||
TextEditingController buyerLastNameController = TextEditingController();
|
||||
TextEditingController buyerPhoneController = TextEditingController();
|
||||
TextEditingController buyerUnitNameController = TextEditingController();
|
||||
String? key;
|
||||
|
||||
Rx<Resource<PaginationModel<OutProvinceCarcassesBuyer>>> buyerList =
|
||||
Resource<PaginationModel<OutProvinceCarcassesBuyer>>.loading().obs;
|
||||
|
||||
RxList<String> routesName = RxList();
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
routesName.value = [...saleLogic.routesName, 'خریداران'].toList();
|
||||
getOutProvinceCarcassesBuyer();
|
||||
}
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
|
||||
selectedProvince.listen((p0) => getCites());
|
||||
|
||||
debounce(
|
||||
searchedValue,
|
||||
(callback) => getOutProvinceCarcassesBuyer(),
|
||||
time: Duration(milliseconds: timeDebounce),
|
||||
);
|
||||
|
||||
setupListeners();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
buyerNameController.dispose();
|
||||
buyerLastNameController.dispose();
|
||||
buyerPhoneController.dispose();
|
||||
buyerUnitNameController.dispose();
|
||||
selectedCity.value = null;
|
||||
selectedProvince.value = null;
|
||||
isExpandedList.clear();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
Future<void> getOutProvinceCarcassesBuyer([bool isLoadingMore = false]) async {
|
||||
if (isLoadingMore) {
|
||||
isLoadingMoreAllocationsMade.value = true;
|
||||
} else {
|
||||
buyerList.value = Resource<PaginationModel<OutProvinceCarcassesBuyer>>.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(
|
||||
call: () => rootLogic.chickenRepository.getOutProvinceCarcassesBuyer(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
queryParameters: buildQueryParams(
|
||||
pageSize: 20,
|
||||
page: currentPage.value,
|
||||
state: 'buyer-list',
|
||||
search: 'filter',
|
||||
role: 'Steward',
|
||||
value: searchedValue.value ?? '',
|
||||
),
|
||||
),
|
||||
onError: (error, stackTrace) => isLoadingMoreAllocationsMade.value = false,
|
||||
onSuccess: (res) {
|
||||
if ((res?.count ?? 0) == 0) {
|
||||
buyerList.value = Resource<PaginationModel<OutProvinceCarcassesBuyer>>.empty();
|
||||
} else {
|
||||
buyerList.value = Resource<PaginationModel<OutProvinceCarcassesBuyer>>.success(
|
||||
PaginationModel<OutProvinceCarcassesBuyer>(
|
||||
count: res?.count ?? 0,
|
||||
next: res?.next,
|
||||
previous: res?.previous,
|
||||
results: [...(buyerList.value.data?.results ?? []), ...(res?.results ?? [])],
|
||||
),
|
||||
);
|
||||
|
||||
isLoadingMoreAllocationsMade.value = false;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void resetSubmitForm() {
|
||||
buyerNameController.clear();
|
||||
buyerLastNameController.clear();
|
||||
buyerPhoneController.clear();
|
||||
buyerUnitNameController.clear();
|
||||
selectedProvince.value = null;
|
||||
selectedCity.value = null;
|
||||
}
|
||||
|
||||
Future<void> getCites() async {
|
||||
await safeCall(
|
||||
call: () =>
|
||||
rootLogic.chickenRepository.getCity(provinceName: selectedProvince.value?.name ?? ''),
|
||||
onSuccess: (result) {
|
||||
if (result != null && result.isNotEmpty) {
|
||||
cites.value = result;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void setupListeners() {
|
||||
buyerNameController.addListener(checkBuyerFormValid);
|
||||
buyerLastNameController.addListener(checkBuyerFormValid);
|
||||
buyerPhoneController.addListener(checkBuyerFormValid);
|
||||
buyerUnitNameController.addListener(checkBuyerFormValid);
|
||||
ever(selectedProvince, (_) => checkBuyerFormValid());
|
||||
ever(selectedCity, (_) => checkBuyerFormValid());
|
||||
}
|
||||
|
||||
void checkBuyerFormValid() {
|
||||
isBuyerSubmitButtonEnabled.value =
|
||||
buyerNameController.text.isNotEmpty &&
|
||||
buyerLastNameController.text.isNotEmpty &&
|
||||
buyerPhoneController.text.isNotEmpty &&
|
||||
buyerUnitNameController.text.isNotEmpty &&
|
||||
selectedProvince.value != null &&
|
||||
selectedCity.value != null;
|
||||
}
|
||||
|
||||
Future<bool> createBuyer() async {
|
||||
bool res = false;
|
||||
if (!(formKey.currentState?.validate() ?? false)) {
|
||||
return res;
|
||||
}
|
||||
await safeCall(
|
||||
call: () async {
|
||||
OutProvinceCarcassesBuyer buyer = OutProvinceCarcassesBuyer(
|
||||
province: selectedProvince.value!.name,
|
||||
city: selectedCity.value!.name,
|
||||
firstName: buyerNameController.text,
|
||||
lastName: buyerLastNameController.text,
|
||||
unitName: buyerUnitNameController.text,
|
||||
mobile: buyerPhoneController.text,
|
||||
role: 'Steward',
|
||||
);
|
||||
final res = await rootLogic.chickenRepository.createOutProvinceCarcassesBuyer(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
body: buyer,
|
||||
);
|
||||
},
|
||||
onSuccess: (result) {
|
||||
getOutProvinceCarcassesBuyer();
|
||||
resetSubmitForm();
|
||||
res = true;
|
||||
},
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
void setEditDataBuyer(OutProvinceCarcassesBuyer item) {
|
||||
buyerNameController.text = item.firstName ?? '';
|
||||
buyerLastNameController.text = item.lastName ?? '';
|
||||
buyerUnitNameController.text = item.unitName ?? '';
|
||||
buyerPhoneController.text = item.mobile ?? '';
|
||||
selectedProvince.value = IranProvinceCityModel(name: item.province);
|
||||
selectedCity.value = IranProvinceCityModel(name: item.city);
|
||||
isBuyerSubmitButtonEnabled.value = true;
|
||||
}
|
||||
|
||||
void setSearchValue(String? value) {
|
||||
searchedValue.value = value?.trim();
|
||||
}
|
||||
|
||||
void submitFilter() {
|
||||
fromDateFilter.value = fromDateFilter.value;
|
||||
toDateFilter.value = toDateFilter.value;
|
||||
getOutProvinceCarcassesBuyer();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/iran_province_city/iran_province_city_model.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/out_province_carcasses_buyer/out_province_carcasses_buyer.dart';
|
||||
import 'package:rasadyar_chicken/presentation/widget/base_page/view.dart';
|
||||
import 'package:rasadyar_chicken/presentation/widget/filter_bottom_sheet.dart';
|
||||
|
||||
|
||||
import 'package:rasadyar_chicken/presentation/widget/page_route.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
import 'logic.dart';
|
||||
|
||||
class SalesOutOfProvinceBuyersPage extends GetView<SalesOutOfProvinceBuyersLogic> {
|
||||
const SalesOutOfProvinceBuyersPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BasePage(
|
||||
routesWidget: ObxValue((route) => buildPageRoute(route), controller.routesName),
|
||||
onBackPressed: () => Get.back(id: 1),
|
||||
onSearchChanged: (data) => controller.setSearchValue(data),
|
||||
filteringWidget: filterBottomSheet(),
|
||||
widgets: [
|
||||
Expanded(
|
||||
child: ObxValue((data) {
|
||||
return RPaginatedListView(
|
||||
onLoadMore: () async => controller.getOutProvinceCarcassesBuyer(true),
|
||||
onRefresh: () async {
|
||||
controller.currentPage.value = 1;
|
||||
await controller.getOutProvinceCarcassesBuyer();
|
||||
},
|
||||
hasMore: data.value.data?.next != null,
|
||||
listType: ListType.separated,
|
||||
resource: data.value,
|
||||
padding: EdgeInsets.fromLTRB(8, 8, 8, 80),
|
||||
itemBuilder: (context, index) {
|
||||
var item = data.value.data!.results![index];
|
||||
return ObxValue((val) {
|
||||
return ExpandableListItem2(
|
||||
selected: val.contains(index),
|
||||
onTap: () => controller.isExpandedList.toggle(index),
|
||||
index: index,
|
||||
child: itemListWidget(item),
|
||||
secondChild: itemListExpandedWidget(item),
|
||||
labelColor: AppColor.blueLight,
|
||||
labelIcon: Assets.vec.userRaduisSvg.path,
|
||||
);
|
||||
}, controller.isExpandedList);
|
||||
},
|
||||
itemCount: data.value.data?.results?.length ?? 0,
|
||||
separatorBuilder: (context, index) => SizedBox(height: 8.h),
|
||||
);
|
||||
}, controller.buyerList),
|
||||
),
|
||||
],
|
||||
floatingActionButton: RFab.add(
|
||||
onPressed: () {
|
||||
Get.bottomSheet(
|
||||
addOrEditBuyerBottomSheet(),
|
||||
isScrollControlled: true,
|
||||
ignoreSafeArea: false,
|
||||
).whenComplete(() => controller.resetSubmitForm());
|
||||
},
|
||||
),
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.startFloat,
|
||||
);
|
||||
}
|
||||
|
||||
Widget addOrEditBuyerBottomSheet([bool isOnEdit = false]) {
|
||||
return BaseBottomSheet(
|
||||
height: 600,
|
||||
child: SingleChildScrollView(
|
||||
child: Form(
|
||||
key: controller.formKey,
|
||||
child: Column(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Text(
|
||||
isOnEdit ? 'ویرایش خریدار' : 'افزودن خریدار',
|
||||
style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
|
||||
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),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget submitButtonWidget(bool isOnEdit) {
|
||||
return ObxValue((data) {
|
||||
return RElevated(
|
||||
isFullWidth: true,
|
||||
backgroundColor: AppColor.greenNormal,
|
||||
text: isOnEdit ? 'ویرایش' : 'ثبت',
|
||||
onPressed: data.value
|
||||
? () async {
|
||||
var res = await controller.createBuyer();
|
||||
if (res) {
|
||||
Get.back();
|
||||
}
|
||||
}
|
||||
: null,
|
||||
height: 40,
|
||||
);
|
||||
}, controller.isBuyerSubmitButtonEnabled);
|
||||
}
|
||||
|
||||
Widget _provinceWidget() {
|
||||
return Obx(() {
|
||||
return OverlayDropdownWidget<IranProvinceCityModel>(
|
||||
items: controller.rootLogic.provinces,
|
||||
onChanged: (value) {
|
||||
controller.selectedProvince.value = value;
|
||||
print('Selected Product: ${value.name}');
|
||||
},
|
||||
selectedItem: controller.selectedProvince.value,
|
||||
itemBuilder: (item) => Text(item.name ?? 'بدون نام'),
|
||||
labelBuilder: (item) => Text(item?.name ?? 'انتخاب استان'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _cityWidget() {
|
||||
return ObxValue((data) {
|
||||
return OverlayDropdownWidget<IranProvinceCityModel>(
|
||||
items: data,
|
||||
onChanged: (value) {
|
||||
controller.selectedCity.value = value;
|
||||
print('Selected Product: ${value.name}');
|
||||
},
|
||||
selectedItem: controller.selectedCity.value,
|
||||
itemBuilder: (item) => Text(item.name ?? 'بدون نام'),
|
||||
labelBuilder: (item) => Text(item?.name ?? 'انتخاب شهر'),
|
||||
);
|
||||
}, controller.cites);
|
||||
}
|
||||
|
||||
itemListWidget(OutProvinceCarcassesBuyer item) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 8),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
SizedBox(width: 8),
|
||||
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
item.buyer?.fullname ?? 'N/A',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
|
||||
SizedBox(height: 2),
|
||||
Text(
|
||||
item.buyer?.mobile ?? 'N/A',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.bgDark),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
'${item.unitName}',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.bgDark),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
'${item.buyer?.province}\n${item.buyer?.city}',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.darkGreyDark),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
itemListExpandedWidget(OutProvinceCarcassesBuyer 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.province}-${item.city}',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan16.copyWith(color: AppColor.greenDark),
|
||||
),
|
||||
|
||||
SizedBox(),
|
||||
],
|
||||
),
|
||||
|
||||
buildRow(title: 'مشخصات خریدار', value: item.fullname ?? 'N/A'),
|
||||
buildRow(title: 'نام واحد', value: item.unitName ?? 'N/A'),
|
||||
buildRow(
|
||||
title: 'تعداد درخواست ها',
|
||||
value: '${item.requestsInfo?.numberOfRequests.separatedByComma}',
|
||||
),
|
||||
buildRow(title: 'وزن', value: '${item.requestsInfo?.totalWeight.separatedByComma}'),
|
||||
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 16.w,
|
||||
children: [
|
||||
RElevated(
|
||||
text: 'ویرایش',
|
||||
width: 150.w,
|
||||
height: 40.h,
|
||||
onPressed: () {
|
||||
controller.setEditDataBuyer(item);
|
||||
Get.bottomSheet(
|
||||
addOrEditBuyerBottomSheet(true),
|
||||
isScrollControlled: true,
|
||||
).whenComplete(() => controller.resetSubmitForm());
|
||||
},
|
||||
textStyle: AppFonts.yekan20.copyWith(color: Colors.white),
|
||||
backgroundColor: AppColor.greenNormal,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget filterBottomSheet() => filterBottomSheetWidget(
|
||||
fromDate: controller.fromDateFilter,
|
||||
onChangedFromDate: (jalali) => controller.fromDateFilter.value = jalali,
|
||||
toDate: controller.toDateFilter,
|
||||
onChangedToDate: (jalali) => controller.toDateFilter.value = jalali,
|
||||
onSubmit: () => controller.submitFilter(),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:rasadyar_chicken/data/models/request/steward_free_sale_bar/steward_free_sale_bar_request.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/iran_province_city/iran_province_city_model.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/out_province_carcasses_buyer/out_province_carcasses_buyer.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/roles_products/roles_products.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/steward_free_sale_bar/steward_free_sale_bar.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/root/logic.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/sale/logic.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/sales_out_of_province_buyers/logic.dart';
|
||||
import 'package:rasadyar_chicken/presentation/utils/utils.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
class SalesOutOfProvinceSalesListLogic extends GetxController {
|
||||
StewardRootLogic get rootLogic => Get.find<StewardRootLogic>();
|
||||
|
||||
SaleLogic get saleLogic => Get.find<SaleLogic>();
|
||||
|
||||
SalesOutOfProvinceBuyersLogic get buyerLogic =>
|
||||
Get.find<SalesOutOfProvinceBuyersLogic>();
|
||||
|
||||
RxInt selectedSegmentIndex = 0.obs;
|
||||
RxBool isExpanded = false.obs;
|
||||
RxInt currentPage = 1.obs;
|
||||
RxBool isSaleSubmitButtonEnabled = false.obs;
|
||||
RxList<int> isExpandedList = <int>[].obs;
|
||||
Rx<Jalali> fromDateFilter = Jalali.now().obs;
|
||||
Rx<Jalali> toDateFilter = Jalali.now().obs;
|
||||
RxnString searchedValue = RxnString();
|
||||
RxList<String> routesName = RxList();
|
||||
RxBool isLoadingMoreAllocationsMade = false.obs;
|
||||
Rxn<IranProvinceCityModel> selectedCity = Rxn();
|
||||
|
||||
//TODO add this to Di
|
||||
ImagePicker imagePicker = ImagePicker();
|
||||
|
||||
GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
TextEditingController quarantineCodeController = TextEditingController();
|
||||
TextEditingController saleWeightController = TextEditingController();
|
||||
Rx<Jalali> saleDate = Jalali.now().obs;
|
||||
String? key;
|
||||
|
||||
Rx<Resource<PaginationModel<StewardFreeSaleBar>>> salesList =
|
||||
Resource<PaginationModel<StewardFreeSaleBar>>.loading().obs;
|
||||
|
||||
Rxn<ProductModel> selectedProduct = Rxn();
|
||||
Rxn<OutProvinceCarcassesBuyer> selectedBuyer = Rxn();
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
getOutProvinceSales();
|
||||
}
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
|
||||
selectedProduct.value = rootLogic.rolesProductsModel.first;
|
||||
debounce(
|
||||
searchedValue,
|
||||
(callback) => getOutProvinceSales(),
|
||||
time: Duration(milliseconds: timeDebounce),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
// TODO: implement onClose
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
Future<void> getOutProvinceSales([bool isLoadingMore = false]) async {
|
||||
if (isLoadingMore) {
|
||||
isLoadingMoreAllocationsMade.value = true;
|
||||
} else {
|
||||
salesList.value = Resource<PaginationModel<StewardFreeSaleBar>>.loading();
|
||||
}
|
||||
await safeCall(
|
||||
call: () => rootLogic.chickenRepository.getStewardFreeSaleBar(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
queryParameters: buildQueryParams(
|
||||
pageSize: 20,
|
||||
page: currentPage.value,
|
||||
state: 'buyer-list',
|
||||
search: 'filter',
|
||||
role: 'Steward',
|
||||
value: searchedValue.value ?? '',
|
||||
fromDate: fromDateFilter.value.toDateTime(),
|
||||
toDate: toDateFilter.value.toDateTime(),
|
||||
),
|
||||
),
|
||||
onSuccess: (res) {
|
||||
if ((res?.count ?? 0) == 0) {
|
||||
salesList.value =
|
||||
Resource<PaginationModel<StewardFreeSaleBar>>.empty();
|
||||
} else {
|
||||
salesList.value =
|
||||
Resource<PaginationModel<StewardFreeSaleBar>>.success(
|
||||
PaginationModel<StewardFreeSaleBar>(
|
||||
count: res?.count ?? 0,
|
||||
next: res?.next,
|
||||
previous: res?.previous,
|
||||
results: [
|
||||
...(salesList.value.data?.results ?? []),
|
||||
...(res?.results ?? []),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
isLoadingMoreAllocationsMade.value = false;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void setupListeners() {
|
||||
saleWeightController.addListener(checkSalesFormValid);
|
||||
quarantineCodeController.addListener(checkSalesFormValid);
|
||||
ever(selectedBuyer, (_) => checkSalesFormValid);
|
||||
ever(selectedProduct, (_) => checkSalesFormValid);
|
||||
ever(saleDate, (_) => checkSalesFormValid());
|
||||
}
|
||||
|
||||
void checkSalesFormValid() {
|
||||
isSaleSubmitButtonEnabled.value =
|
||||
saleDate.value.toString().isNotEmpty &&
|
||||
selectedProduct.value != null &&
|
||||
selectedBuyer.value != null &&
|
||||
saleWeightController.text.isNotEmpty &&
|
||||
quarantineCodeController.text.isNotEmpty;
|
||||
}
|
||||
|
||||
void setEditDataSales(StewardFreeSaleBar item) {
|
||||
quarantineCodeController.text = item.clearanceCode ?? '';
|
||||
saleWeightController.text =
|
||||
item.weightOfCarcasses?.toInt().toString() ?? '';
|
||||
saleDate.value = Jalali.fromDateTime(DateTime.parse(item.date!));
|
||||
selectedCity.value = IranProvinceCityModel(name: item.city);
|
||||
selectedBuyer.value = buyerLogic.buyerList.value.data?.results?.firstWhere(
|
||||
(element) => element.key == item.buyer?.key,
|
||||
);
|
||||
selectedProduct.value = rootLogic.rolesProductsModel.first;
|
||||
key = item.key;
|
||||
isSaleSubmitButtonEnabled.value = true;
|
||||
}
|
||||
|
||||
Future<void> deleteStewardPurchaseOutOfProvince(String key) async {
|
||||
await safeCall(
|
||||
call: () => rootLogic.chickenRepository
|
||||
.deleteStewardPurchasesOutSideOfTheProvince(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
stewardFreeBarKey: key,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> createSale() async {
|
||||
bool res = false;
|
||||
StewardFreeSaleBarRequest requestBody = StewardFreeSaleBarRequest(
|
||||
buyerKey: selectedBuyer.value?.key,
|
||||
numberOfCarcasses: 0,
|
||||
weightOfCarcasses: int.tryParse(saleWeightController.text.clearComma),
|
||||
date: saleDate.value.toDateTime().formattedDashedGregorian,
|
||||
clearanceCode: quarantineCodeController.text,
|
||||
productKey: selectedProduct.value?.key,
|
||||
);
|
||||
await safeCall(
|
||||
call: () => rootLogic.chickenRepository.createOutProvinceStewardFreeBar(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
body: requestBody,
|
||||
),
|
||||
onSuccess: (_) {
|
||||
res = true;
|
||||
},
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
void clearSaleForm() {
|
||||
quarantineCodeController.clear();
|
||||
saleWeightController.clear();
|
||||
saleDate.value = Jalali.now();
|
||||
selectedBuyer.value = null;
|
||||
selectedProduct.value = null;
|
||||
}
|
||||
|
||||
Future<bool> editSale() async {
|
||||
bool res = false;
|
||||
StewardFreeSaleBarRequest requestBody = StewardFreeSaleBarRequest(
|
||||
numberOfCarcasses: 0,
|
||||
weightOfCarcasses: int.tryParse(saleWeightController.text.clearComma),
|
||||
date: saleDate.value.toDateTime().formattedDashedGregorian,
|
||||
clearanceCode: quarantineCodeController.text,
|
||||
key: key,
|
||||
);
|
||||
await safeCall(
|
||||
call: () => rootLogic.chickenRepository.updateOutProvinceStewardFreeBar(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
body: requestBody,
|
||||
),
|
||||
onSuccess: (_) {
|
||||
res = true;
|
||||
},
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
void resetSubmitForm() {
|
||||
selectedCity.value = null;
|
||||
selectedProduct.value = null;
|
||||
key = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,490 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/out_province_carcasses_buyer/out_province_carcasses_buyer.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/roles_products/roles_products.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/steward_free_sale_bar/steward_free_sale_bar.dart';
|
||||
import 'package:rasadyar_chicken/presentation/routes/routes.dart';
|
||||
import 'package:rasadyar_chicken/presentation/widget/list_item/list_item.dart' hide ListItem2;
|
||||
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
import 'logic.dart';
|
||||
|
||||
class SalesOutOfProvinceSalesListPage extends GetView<SalesOutOfProvinceSalesListLogic> {
|
||||
const SalesOutOfProvinceSalesListPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: ObxValue((data) {
|
||||
return RPaginatedListView(
|
||||
onLoadMore: () async => controller.getOutProvinceSales(true),
|
||||
onRefresh: () async {
|
||||
controller.currentPage.value = 1;
|
||||
await controller.getOutProvinceSales();
|
||||
},
|
||||
hasMore: data.value.data?.next != null,
|
||||
listType: ListType.separated,
|
||||
resource: data.value,
|
||||
padding: EdgeInsets.fromLTRB(8, 8, 8, 80),
|
||||
itemBuilder: (context, index) {
|
||||
var item = data.value.data!.results![index];
|
||||
return ObxValue((val) {
|
||||
return ExpandableListItem2(
|
||||
selected: val.contains(index),
|
||||
onTap: () => controller.isExpandedList.toggle(index),
|
||||
index: index,
|
||||
child: itemListWidget(item),
|
||||
secondChild: itemListExpandedWidget(item, index),
|
||||
labelColor: AppColor.blueLight,
|
||||
labelIcon: Assets.vec.timerSvg.path,
|
||||
);
|
||||
}, controller.isExpandedList);
|
||||
},
|
||||
itemCount: data.value.data?.results?.length ?? 0,
|
||||
separatorBuilder: (context, index) => SizedBox(height: 8.h),
|
||||
);
|
||||
}, controller.salesList),
|
||||
floatingActionButton: Row(
|
||||
children: [
|
||||
RFab.add(
|
||||
onPressed: () {
|
||||
Get.bottomSheet(
|
||||
addOrEditSaleBottomSheet(),
|
||||
ignoreSafeArea: false,
|
||||
isScrollControlled: true,
|
||||
).whenComplete(() {
|
||||
controller.clearSaleForm();
|
||||
});
|
||||
},
|
||||
),
|
||||
Spacer(),
|
||||
RFab(
|
||||
icon: Icon(CupertinoIcons.person_add_solid, color: Colors.white, size: 35.w),
|
||||
backgroundColor: AppColor.blueNormal,
|
||||
onPressed: () {
|
||||
Get.toNamed(ChickenRoutes.salesOutOfProvinceBuyerSteward, id: 1);
|
||||
},
|
||||
),
|
||||
SizedBox(width: 25),
|
||||
],
|
||||
),
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.startFloat,
|
||||
);
|
||||
}
|
||||
|
||||
itemListWidget(StewardFreeSaleBar item) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Text(
|
||||
item.date?.formattedJalaliDate ?? 'N/A',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.bgDark),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
item.buyer?.fullname ?? 'N/A',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
|
||||
SizedBox(height: 2),
|
||||
Text(
|
||||
item.buyer?.mobile ?? 'N/A',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.bgDark),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 8,
|
||||
children: [
|
||||
Text(
|
||||
item.buyer?.unitName ?? 'N/A',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.bgDark),
|
||||
),
|
||||
Text(
|
||||
'${item.weightOfCarcasses?.separatedByComma ?? 0}KG',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.bgDark),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
'${item.buyer?.province}\n${item.buyer?.city}',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.bgDark),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
itemListExpandedWidget(StewardFreeSaleBar item, int index) {
|
||||
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.province}-${item.city}',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan16.copyWith(color: AppColor.greenDark),
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
height: 32,
|
||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||
decoration: ShapeDecoration(
|
||||
color: AppColor.blueLight,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(width: 1, color: AppColor.blueLightHover),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 3,
|
||||
children: [
|
||||
Text(
|
||||
item.date?.toJalali.formatter.wN ?? 'N/A',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
|
||||
Text(
|
||||
'${item.date?.toJalali.formatter.d} ${item.date?.toJalali.formatter.mN ?? 'N/A'}',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Text(
|
||||
'${item.date?.toJalali.formatter.y}',
|
||||
style: AppFonts.yekan20.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
|
||||
Text(
|
||||
'${item.date?.toJalali.formatter.tHH}:${item.date?.toJalali.formatter.tMM ?? 'N/A'}',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
buildRow(title: 'مشخصات خریدار', value: item.buyer?.fullname ?? 'N/A'),
|
||||
buildRow(title: 'تلفن خریدار', value: item.buyer?.mobile ?? 'N/A'),
|
||||
buildRow(title: 'نام واحد', value: item.buyer?.unitName ?? 'N/A'),
|
||||
buildRow(title: 'وزن لاشه', value: '${item.weightOfCarcasses?.separatedByComma}'),
|
||||
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 16.w,
|
||||
children: [
|
||||
RElevated(
|
||||
text: 'ویرایش',
|
||||
width: 150.w,
|
||||
height: 40.h,
|
||||
onPressed: () {
|
||||
controller.setEditDataSales(item);
|
||||
Get.bottomSheet(
|
||||
addOrEditSaleBottomSheet(true),
|
||||
isScrollControlled: true,
|
||||
).whenComplete(() {
|
||||
controller.resetSubmitForm();
|
||||
});
|
||||
},
|
||||
textStyle: AppFonts.yekan20.copyWith(color: Colors.white),
|
||||
backgroundColor: AppColor.greenNormal,
|
||||
),
|
||||
ROutlinedElevated(
|
||||
text: 'حذف',
|
||||
textStyle: AppFonts.yekan20.copyWith(color: AppColor.redNormal),
|
||||
width: 150.w,
|
||||
height: 40.h,
|
||||
onPressed: () {
|
||||
buildDeleteDialog(
|
||||
onConfirm: () async {
|
||||
controller.isExpandedList.remove(index);
|
||||
controller.deleteStewardPurchaseOutOfProvince(item.key!);
|
||||
},
|
||||
onRefresh: () => controller.getOutProvinceSales(),
|
||||
);
|
||||
},
|
||||
borderColor: AppColor.redNormal,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget addOrEditSaleBottomSheet([bool isOnEdit = false]) {
|
||||
return BaseBottomSheet(
|
||||
height: 500.h,
|
||||
child: SingleChildScrollView(
|
||||
child: Form(
|
||||
key: controller.formKey,
|
||||
child: Column(
|
||||
spacing: 16,
|
||||
children: [
|
||||
Text(
|
||||
isOnEdit ? 'ویرایش فروش' : 'افزودن فروش',
|
||||
style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
_productDropDown(),
|
||||
|
||||
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: [
|
||||
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(),
|
||||
],
|
||||
|
||||
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(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget submitButtonWidget(bool isOnEdit) {
|
||||
return ObxValue((data) {
|
||||
return RElevated(
|
||||
isFullWidth: true,
|
||||
backgroundColor: AppColor.greenNormal,
|
||||
text: isOnEdit ? 'ویرایش' : 'ثبت',
|
||||
onPressed: data.value
|
||||
? () async {
|
||||
var res = isOnEdit ? await controller.editSale() : await controller.createSale();
|
||||
if (res) {
|
||||
controller.getOutProvinceSales();
|
||||
controller.clearSaleForm();
|
||||
Get.back();
|
||||
}
|
||||
}
|
||||
: null,
|
||||
height: 40,
|
||||
);
|
||||
}, controller.isSaleSubmitButtonEnabled);
|
||||
}
|
||||
|
||||
Widget _buyerWidget() {
|
||||
return Obx(() {
|
||||
return OverlayDropdownWidget<OutProvinceCarcassesBuyer>(
|
||||
items: controller.buyerLogic.buyerList.value.data?.results ?? [],
|
||||
onChanged: (value) {
|
||||
controller.selectedBuyer.value = value;
|
||||
},
|
||||
selectedItem: controller.selectedBuyer.value,
|
||||
itemBuilder: (item) => Text(item.buyer?.fullname ?? 'بدون نام'),
|
||||
labelBuilder: (item) => Text(item?.buyer?.fullname ?? 'انتخاب خریدار'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _productDropDown() {
|
||||
return Obx(() {
|
||||
return OverlayDropdownWidget<ProductModel>(
|
||||
items: controller.rootLogic.rolesProductsModel,
|
||||
height: 56,
|
||||
hasDropIcon: false,
|
||||
background: Colors.white,
|
||||
onChanged: (value) {
|
||||
controller.selectedProduct.value = value;
|
||||
},
|
||||
selectedItem: controller.selectedProduct.value,
|
||||
initialValue: controller.selectedProduct.value,
|
||||
itemBuilder: (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}',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
GestureDetector timeFilterWidget({
|
||||
isFrom = true,
|
||||
required Rx<Jalali> date,
|
||||
required Function(Jalali jalali) onChanged,
|
||||
}) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Get.bottomSheet(modalDatePicker((value) => onChanged(value)));
|
||||
},
|
||||
child: Container(
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.bgLight,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(width: 1, color: AppColor.darkGreyLight),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 11, vertical: 4),
|
||||
child: Row(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Assets.vec.calendarSvg.svg(
|
||||
width: 24,
|
||||
height: 24,
|
||||
colorFilter: const ColorFilter.mode(AppColor.bgDark, BlendMode.srcIn),
|
||||
),
|
||||
Text('تاریخ', style: AppFonts.yekan16.copyWith(color: AppColor.bgDark)),
|
||||
Expanded(
|
||||
child: ObxValue((data) {
|
||||
return Text(
|
||||
date.value.formatCompactDate(),
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan16.copyWith(color: AppColor.darkGreyDark),
|
||||
);
|
||||
}, date),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Container modalDatePicker(ValueChanged<Jalali> onDateSelected) {
|
||||
Jalali? tempPickedDate;
|
||||
return Container(
|
||||
height: 250,
|
||||
color: Colors.white,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(width: 20),
|
||||
RElevated(
|
||||
height: 35,
|
||||
width: 70,
|
||||
textStyle: AppFonts.yekan14.copyWith(color: Colors.white),
|
||||
onPressed: () {
|
||||
onDateSelected(tempPickedDate ?? Jalali.now());
|
||||
Get.back();
|
||||
},
|
||||
text: 'تایید',
|
||||
),
|
||||
Spacer(),
|
||||
RElevated(
|
||||
height: 35,
|
||||
width: 70,
|
||||
backgroundColor: AppColor.error,
|
||||
textStyle: AppFonts.yekan14.copyWith(color: Colors.white),
|
||||
onPressed: () {
|
||||
onDateSelected(tempPickedDate ?? Jalali.now());
|
||||
Get.back();
|
||||
},
|
||||
text: 'لغو',
|
||||
),
|
||||
SizedBox(width: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(height: 0, thickness: 1),
|
||||
Expanded(
|
||||
child: Container(
|
||||
child: PersianCupertinoDatePicker(
|
||||
initialDateTime: controller.saleDate.value,
|
||||
mode: PersianCupertinoDatePickerMode.date,
|
||||
onDateTimeChanged: (dateTime) {
|
||||
tempPickedDate = dateTime;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:rasadyar_chicken/data/models/response/guild/guild_model.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/roles_products/roles_products.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/segmentation_model/segmentation_model.dart';
|
||||
import 'package:rasadyar_chicken/presentation/pages/steward/root/logic.dart';
|
||||
import 'package:rasadyar_chicken/presentation/utils/utils.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
class SegmentationLogic extends GetxController {
|
||||
StewardRootLogic rootLogic = Get.find<StewardRootLogic>();
|
||||
|
||||
RxBool isLoadingMoreAllocationsMade = false.obs;
|
||||
RxInt currentPage = 1.obs;
|
||||
|
||||
late List<String> routesName;
|
||||
RxInt selectedSegmentIndex = 0.obs;
|
||||
RxBool isExpanded = false.obs;
|
||||
|
||||
RxList<int> isExpandedList = <int>[].obs;
|
||||
Rx<Jalali> fromDateFilter = Jalali.now().obs;
|
||||
Rx<Jalali> toDateFilter = Jalali.now().obs;
|
||||
RxnString searchedValue = RxnString();
|
||||
RxInt saleType = 1.obs;
|
||||
GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
TextEditingController weightController = TextEditingController(text: '0');
|
||||
RxBool isSubmitButtonEnabled = false.obs;
|
||||
Rxn<GuildModel> selectedGuildModel = Rxn<GuildModel>();
|
||||
Rxn<ProductModel> selectedProduct = Rxn<ProductModel>();
|
||||
Rxn<SegmentationModel> selectedSegment = Rxn<SegmentationModel>();
|
||||
|
||||
Rx<Resource<PaginationModel<SegmentationModel>>> segmentationList =
|
||||
Resource<PaginationModel<SegmentationModel>>.loading().obs;
|
||||
|
||||
RxList<GuildModel> guildsModel = <GuildModel>[].obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
routesName = ['قطعهبندی'].toList();
|
||||
once(rootLogic.rolesProductsModel, (callback) => selectedProduct.value = callback.first);
|
||||
getAllSegmentation();
|
||||
getGuilds();
|
||||
}
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
setUpListener();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
// TODO: implement onClose
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
void setSearchValue(String? value) {
|
||||
searchedValue.value = value?.trim();
|
||||
}
|
||||
|
||||
void setUpListener() {
|
||||
debounce(
|
||||
searchedValue,
|
||||
(callback) => getAllSegmentation(),
|
||||
time: Duration(milliseconds: timeDebounce),
|
||||
);
|
||||
ever(selectedSegment, (_) {
|
||||
validateForm();
|
||||
});
|
||||
|
||||
weightController.addListener(() => validateForm());
|
||||
}
|
||||
|
||||
void setEditData(SegmentationModel item) {
|
||||
selectedSegment.value = item;
|
||||
weightController.text = item.weight.toString();
|
||||
}
|
||||
|
||||
void clearForm() {
|
||||
weightController.text = '0';
|
||||
selectedSegment.value = null;
|
||||
selectedGuildModel.value = null;
|
||||
}
|
||||
|
||||
void validateForm() {
|
||||
var weight = int.tryParse(weightController.text.clearComma.trim());
|
||||
isSubmitButtonEnabled.value =
|
||||
selectedProduct.value != null &&
|
||||
weightController.text.isNotEmpty &&
|
||||
weight! > 0 &&
|
||||
(saleType.value == 1 || (saleType.value == 2 && selectedGuildModel.value != null));
|
||||
}
|
||||
|
||||
Future<void> getAllSegmentation([bool isLoadingMore = false]) async {
|
||||
if (isLoadingMore) {
|
||||
isLoadingMoreAllocationsMade.value = true;
|
||||
} else {
|
||||
segmentationList.value = Resource<PaginationModel<SegmentationModel>>.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(
|
||||
call: () async => await rootLogic.chickenRepository.getSegmentation(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
queryParameters: buildQueryParams(
|
||||
pageSize: 20,
|
||||
page: currentPage.value,
|
||||
search: 'filter',
|
||||
role: 'Steward',
|
||||
value: searchedValue.value,
|
||||
fromDate: fromDateFilter.value.toDateTime(),
|
||||
toDate: toDateFilter.value.toDateTime(),
|
||||
),
|
||||
),
|
||||
|
||||
onSuccess: (result) {
|
||||
if ((result?.count ?? 0) == 0) {
|
||||
segmentationList.value = Resource<PaginationModel<SegmentationModel>>.empty();
|
||||
} else {
|
||||
segmentationList.value = Resource<PaginationModel<SegmentationModel>>.success(
|
||||
PaginationModel<SegmentationModel>(
|
||||
count: result?.count ?? 0,
|
||||
next: result?.next,
|
||||
previous: result?.previous,
|
||||
results: [
|
||||
...(segmentationList.value.data?.results ?? []),
|
||||
...(result?.results ?? []),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
isLoadingMoreAllocationsMade.value = false;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> deleteSegmentation(String key) async {
|
||||
await safeCall(
|
||||
showError: false,
|
||||
call: () => rootLogic.chickenRepository.deleteSegmentation(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
key: key,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> editSegment() async {
|
||||
var res = true;
|
||||
safeCall(
|
||||
showError: true,
|
||||
call: () async => await rootLogic.chickenRepository.editSegmentation(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
model: SegmentationModel(
|
||||
key: selectedSegment.value?.key,
|
||||
weight: int.tryParse(weightController.text.clearComma) ?? 0,
|
||||
),
|
||||
),
|
||||
onSuccess: (result) {
|
||||
res = true;
|
||||
},
|
||||
onError: (error, stacktrace) {
|
||||
res = false;
|
||||
},
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<bool> createSegment() async {
|
||||
var res = true;
|
||||
SegmentationModel segmentationModel = SegmentationModel(
|
||||
productKey: selectedProduct.value?.key,
|
||||
weight: int.tryParse(weightController.text.clearComma) ?? 0,
|
||||
);
|
||||
if (saleType.value == 2) {
|
||||
segmentationModel = segmentationModel.copyWith(guildKey: selectedGuildModel.value?.key);
|
||||
}
|
||||
await safeCall(
|
||||
call: () async => await rootLogic.chickenRepository.createSegmentation(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
model: segmentationModel,
|
||||
),
|
||||
onSuccess: (result) {
|
||||
res = true;
|
||||
},
|
||||
onError: (error, stacktrace) {
|
||||
res = false;
|
||||
},
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<void> getGuilds() async {
|
||||
safeCall(
|
||||
call: () async => await rootLogic.chickenRepository.getGuilds(
|
||||
token: rootLogic.tokenService.accessToken.value!,
|
||||
queryParameters: buildQueryParams(queryParams: {'all': true}, role: 'Steward'),
|
||||
),
|
||||
onSuccess: (result) {
|
||||
if (result != null) {
|
||||
guildsModel.clear();
|
||||
guildsModel.addAll(result);
|
||||
}
|
||||
},
|
||||
onError: (error, stacktrace) {},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,430 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/guild/guild_model.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/roles_products/roles_products.dart';
|
||||
import 'package:rasadyar_chicken/data/models/response/segmentation_model/segmentation_model.dart';
|
||||
import 'package:rasadyar_chicken/presentation/widget/base_page/view.dart';
|
||||
import 'package:rasadyar_chicken/presentation/widget/filter_bottom_sheet.dart';
|
||||
|
||||
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
import 'logic.dart';
|
||||
|
||||
class SegmentationPage extends GetView<SegmentationLogic> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BasePage(
|
||||
routes: controller.routesName,
|
||||
onSearchChanged: (data) => controller.setSearchValue(data),
|
||||
filteringWidget: filterBottomSheet(),
|
||||
hasBack: false,
|
||||
widgets: [
|
||||
Expanded(
|
||||
child: ObxValue((data) {
|
||||
return RPaginatedListView(
|
||||
onLoadMore: () async => controller.getAllSegmentation(true),
|
||||
onRefresh: () async {
|
||||
controller.currentPage.value = 1;
|
||||
await controller.getAllSegmentation();
|
||||
},
|
||||
hasMore: data.value.data?.next != null,
|
||||
listType: ListType.separated,
|
||||
resource: data.value,
|
||||
padding: EdgeInsets.fromLTRB(8, 8, 8, 80),
|
||||
itemBuilder: (context, index) {
|
||||
var item = data.value.data!.results![index];
|
||||
return ObxValue((val) {
|
||||
return ExpandableListItem2(
|
||||
selected: val.contains(index),
|
||||
onTap: () => controller.isExpandedList.toggle(index),
|
||||
index: index,
|
||||
child: itemListWidget(item),
|
||||
secondChild: itemListExpandedWidget(item, index),
|
||||
labelColor: AppColor.blueLight,
|
||||
labelIconColor: AppColor.customGrey,
|
||||
labelIcon: Assets.vec.convertCubeSvg.path,
|
||||
);
|
||||
}, controller.isExpandedList);
|
||||
},
|
||||
itemCount: data.value.data?.results?.length ?? 0,
|
||||
separatorBuilder: (context, index) => SizedBox(height: 8.h),
|
||||
);
|
||||
}, controller.segmentationList),
|
||||
),
|
||||
],
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.startFloat,
|
||||
floatingActionButton: RFab.add(
|
||||
onPressed: () {
|
||||
Get.bottomSheet(
|
||||
addOrEditBottomSheet(),
|
||||
isScrollControlled: true,
|
||||
ignoreSafeArea: false,
|
||||
).whenComplete(() {
|
||||
controller.clearForm();
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget filterBottomSheet() => filterBottomSheetWidget(
|
||||
fromDate: controller.fromDateFilter,
|
||||
onChangedFromDate: (jalali) => controller.fromDateFilter.value = jalali,
|
||||
toDate: controller.toDateFilter,
|
||||
onChangedToDate: (jalali) => controller.toDateFilter.value = jalali,
|
||||
onSubmit: () => controller.getAllSegmentation(),
|
||||
);
|
||||
|
||||
itemListWidget(SegmentationModel item) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 4,
|
||||
children: [
|
||||
Text(
|
||||
item.toGuild != null ? 'مباشر' : 'قطعهبند',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
Text(
|
||||
item.date?.formattedJalaliDate ?? 'N/A',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.bgDark),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
item.toGuild != null
|
||||
? item.toGuild?.user?.fullname ?? 'N/A'
|
||||
: item.buyer?.fullname ?? 'N/A',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
|
||||
SizedBox(height: 2),
|
||||
Text(
|
||||
item.toGuild != null
|
||||
? item.toGuild?.guildsName ?? 'N/A'
|
||||
: item.buyer?.shop ?? 'N/A',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.bgDark),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
'${item.weight.separatedByComma} KG',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.bgDark),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
itemListExpandedWidget(SegmentationModel item, int index) {
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(8)),
|
||||
child: Column(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Container(
|
||||
height: 32,
|
||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||
decoration: ShapeDecoration(
|
||||
color: AppColor.blueLight,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(width: 1, color: AppColor.blueLightHover),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 3,
|
||||
children: [
|
||||
Text(
|
||||
DateTimeExtensions(item.date)?.toJalali().formatter.wN ?? 'N/A',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
|
||||
Text(
|
||||
'${DateTimeExtensions(item.date)?.toJalali().formatter.d} ${DateTimeExtensions(item.date)?.toJalali().formatter.mN ?? 'N/A'}',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Text(
|
||||
'${DateTimeExtensions(item.date)?.toJalali().formatter.y}',
|
||||
style: AppFonts.yekan20.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
|
||||
Text(
|
||||
'${DateTimeExtensions(item.date)?.toJalali().formatter.tHH}:${DateTimeExtensions(item.date)?.toJalali().formatter.tMM ?? 'N/A'}',
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
buildRow(
|
||||
title: 'مشخصات خریدار',
|
||||
value: item.toGuild != null
|
||||
? item.toGuild?.user?.fullname ?? 'N/A'
|
||||
: item.buyer?.fullname ?? 'N/A',
|
||||
),
|
||||
buildRow(
|
||||
title: 'تلفن خریدار',
|
||||
value: item.toGuild != null
|
||||
? item.toGuild?.user?.mobile ?? 'N/A'
|
||||
: item.buyer?.mobile ?? 'N/A',
|
||||
),
|
||||
buildRow(
|
||||
title: 'نام واحد',
|
||||
value: item.toGuild != null
|
||||
? item.toGuild?.guildsName ?? 'N/A'
|
||||
: item.buyer?.shop ?? 'N/A',
|
||||
),
|
||||
buildRow(title: 'ماهیت', value: item.toGuild != null ? 'مباشر' : 'قطعهبند'),
|
||||
buildRow(title: 'وزن قطعهبندی', value: '${item.weight?.separatedByComma}'),
|
||||
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 16.w,
|
||||
children: [
|
||||
RElevated(
|
||||
text: 'ویرایش',
|
||||
width: 150.w,
|
||||
height: 40.h,
|
||||
onPressed: () {
|
||||
controller.setEditData(item);
|
||||
Get.bottomSheet(
|
||||
addOrEditBottomSheet(true),
|
||||
isScrollControlled: true,
|
||||
ignoreSafeArea: false,
|
||||
).whenComplete(() {
|
||||
controller.clearForm();
|
||||
});
|
||||
},
|
||||
textStyle: AppFonts.yekan20.copyWith(color: Colors.white),
|
||||
backgroundColor: AppColor.greenNormal,
|
||||
),
|
||||
ROutlinedElevated(
|
||||
text: 'حذف',
|
||||
textStyle: AppFonts.yekan20.copyWith(color: AppColor.redNormal),
|
||||
width: 150.w,
|
||||
height: 40.h,
|
||||
onPressed: () {
|
||||
buildDeleteDialog(
|
||||
onConfirm: () async {
|
||||
controller.isExpandedList.remove(index);
|
||||
controller.deleteSegmentation(item.key!);
|
||||
},
|
||||
onRefresh: () => controller.getAllSegmentation(),
|
||||
);
|
||||
},
|
||||
borderColor: AppColor.redNormal,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget addOrEditBottomSheet([bool isOnEdit = false]) {
|
||||
return BaseBottomSheet(
|
||||
height: 430.h,
|
||||
child: SingleChildScrollView(
|
||||
child: Form(
|
||||
key: controller.formKey,
|
||||
child: Column(
|
||||
spacing: 16,
|
||||
children: [
|
||||
Text(
|
||||
isOnEdit ? 'ویرایش قطعهبندی' : 'افزودن قطعهبندی',
|
||||
style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
_productDropDown(),
|
||||
Container(
|
||||
padding: EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: AppColor.darkGreyLight, width: 1),
|
||||
),
|
||||
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
SizedBox(
|
||||
height: 40,
|
||||
child: ObxValue((data) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Radio(
|
||||
value: 1,
|
||||
groupValue: controller.saleType.value,
|
||||
onChanged: (value) {
|
||||
controller.saleType.value = value!;
|
||||
|
||||
controller.selectedGuildModel.value = null;
|
||||
controller.selectedGuildModel.refresh();
|
||||
},
|
||||
),
|
||||
Text('قطعهبندی(مباشر)', style: AppFonts.yekan14),
|
||||
SizedBox(width: 12),
|
||||
Radio(
|
||||
value: 2,
|
||||
groupValue: controller.saleType.value,
|
||||
onChanged: (value) {
|
||||
controller.saleType.value = value!;
|
||||
},
|
||||
),
|
||||
Text('تخصیص به قطعهبند', style: AppFonts.yekan14),
|
||||
],
|
||||
);
|
||||
}, controller.saleType),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
guildsDropDown(),
|
||||
],
|
||||
),
|
||||
),
|
||||
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: [
|
||||
UnitTextField(
|
||||
hint: 'وزن',
|
||||
unit: 'کیلوگرم',
|
||||
controller: controller.weightController,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
SeparatorInputFormatter(),
|
||||
],
|
||||
validator: (value) {
|
||||
if (value == null) {
|
||||
return 'لطفاً وزن لاشه را وارد کنید';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
submitButtonWidget(isOnEdit),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget submitButtonWidget(bool isOnEdit) {
|
||||
return ObxValue((data) {
|
||||
return RElevated(
|
||||
isFullWidth: true,
|
||||
backgroundColor: AppColor.greenNormal,
|
||||
text: isOnEdit ? 'ویرایش' : 'ثبت',
|
||||
onPressed: data.value
|
||||
? () async {
|
||||
var res = isOnEdit
|
||||
? await controller.editSegment()
|
||||
: await controller.createSegment();
|
||||
if (res) {
|
||||
await controller.getAllSegmentation();
|
||||
controller.clearForm();
|
||||
Get.back();
|
||||
}
|
||||
}
|
||||
: null,
|
||||
height: 40,
|
||||
);
|
||||
}, controller.isSubmitButtonEnabled);
|
||||
}
|
||||
|
||||
Widget _productDropDown() {
|
||||
return Obx(() {
|
||||
return OverlayDropdownWidget<ProductModel>(
|
||||
items: controller.rootLogic.rolesProductsModel,
|
||||
height: 56,
|
||||
hasDropIcon: false,
|
||||
background: Colors.white,
|
||||
onChanged: (value) {
|
||||
controller.selectedProduct.value = value;
|
||||
},
|
||||
selectedItem: controller.selectedProduct.value,
|
||||
initialValue: controller.selectedProduct.value,
|
||||
itemBuilder: (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} کیلوگرم',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget guildsDropDown() {
|
||||
return Obx(() {
|
||||
final item = controller.selectedGuildModel.value;
|
||||
return OverlayDropdownWidget<GuildModel>(
|
||||
key: ValueKey(item?.user?.fullname ?? ''),
|
||||
items: controller.guildsModel,
|
||||
isDisabled: controller.saleType.value == 1,
|
||||
onChanged: (value) {
|
||||
controller.selectedGuildModel.value = value;
|
||||
},
|
||||
selectedItem: item,
|
||||
|
||||
itemBuilder: (item) => Text(
|
||||
item.user != null
|
||||
? '${item.steward == true ? 'مباشر' : 'صنف'} ${item.user!.fullname} (${item.user!.mobile})'
|
||||
: 'بدون نام',
|
||||
),
|
||||
labelBuilder: (item) => Text(
|
||||
item?.user != null
|
||||
? '${item?.steward == true ? 'مباشر' : 'صنف'} ${item?.user!.fullname} (${item?.user!.mobile})'
|
||||
: 'انتخاب مباشر/صنف',
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
28
packages/chicken/lib/presentation/pages/steward/steward.dart
Normal file
28
packages/chicken/lib/presentation/pages/steward/steward.dart
Normal file
@@ -0,0 +1,28 @@
|
||||
export 'buy/logic.dart';
|
||||
export 'buy/view.dart';
|
||||
export 'buy_in_province/logic.dart';
|
||||
export 'buy_in_province/view.dart';
|
||||
export 'buy_in_province_all/logic.dart';
|
||||
export 'buy_in_province_all/view.dart';
|
||||
export 'buy_in_province_waiting/logic.dart';
|
||||
export 'buy_in_province_waiting/view.dart';
|
||||
export 'buy_out_of_province/logic.dart';
|
||||
export 'buy_out_of_province/view.dart';
|
||||
export 'home/logic.dart';
|
||||
export 'home/view.dart';
|
||||
export 'profile/logic.dart';
|
||||
export 'profile/view.dart';
|
||||
export 'root/logic.dart';
|
||||
export 'root/view.dart';
|
||||
export 'sale/logic.dart';
|
||||
export 'sale/view.dart';
|
||||
export 'sales_in_province/logic.dart';
|
||||
export 'sales_in_province/view.dart';
|
||||
export 'sales_out_of_province/logic.dart';
|
||||
export 'sales_out_of_province/view.dart';
|
||||
export 'sales_out_of_province_buyers/logic.dart';
|
||||
export 'sales_out_of_province_buyers/view.dart';
|
||||
export 'sales_out_of_province_sales_list/logic.dart';
|
||||
export 'sales_out_of_province_sales_list/view.dart';
|
||||
export 'segmentation/logic.dart';
|
||||
export 'segmentation/view.dart';
|
||||
Reference in New Issue
Block a user