feat : segmentation page

This commit is contained in:
2025-07-13 16:17:17 +03:30
parent d78a604d03
commit 2ae5f805af
16 changed files with 600 additions and 56 deletions

View File

@@ -6,7 +6,7 @@ part 'segmentation_model.g.dart';
@freezed
abstract class SegmentationModel with _$SegmentationModel {
const factory SegmentationModel({String? key, Buyer? buyer, DateTime? date, double? weight}) =
const factory SegmentationModel({String? key, Buyer? buyer, DateTime? date, int? weight}) =
_SegmentationModel;
factory SegmentationModel.fromJson(Map<String, dynamic> json) =>

View File

@@ -16,7 +16,7 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$SegmentationModel {
String? get key; Buyer? get buyer; DateTime? get date; double? get weight;
String? get key; Buyer? get buyer; DateTime? get date; int? get weight;
/// Create a copy of SegmentationModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -49,7 +49,7 @@ abstract mixin class $SegmentationModelCopyWith<$Res> {
factory $SegmentationModelCopyWith(SegmentationModel value, $Res Function(SegmentationModel) _then) = _$SegmentationModelCopyWithImpl;
@useResult
$Res call({
String? key, Buyer? buyer, DateTime? date, double? weight
String? key, Buyer? buyer, DateTime? date, int? weight
});
@@ -72,7 +72,7 @@ key: freezed == key ? _self.key : key // ignore: cast_nullable_to_non_nullable
as String?,buyer: freezed == buyer ? _self.buyer : buyer // ignore: cast_nullable_to_non_nullable
as Buyer?,date: freezed == date ? _self.date : date // ignore: cast_nullable_to_non_nullable
as DateTime?,weight: freezed == weight ? _self.weight : weight // ignore: cast_nullable_to_non_nullable
as double?,
as int?,
));
}
/// Create a copy of SegmentationModel
@@ -101,7 +101,7 @@ class _SegmentationModel implements SegmentationModel {
@override final String? key;
@override final Buyer? buyer;
@override final DateTime? date;
@override final double? weight;
@override final int? weight;
/// Create a copy of SegmentationModel
/// with the given fields replaced by the non-null parameter values.
@@ -136,7 +136,7 @@ abstract mixin class _$SegmentationModelCopyWith<$Res> implements $SegmentationM
factory _$SegmentationModelCopyWith(_SegmentationModel value, $Res Function(_SegmentationModel) _then) = __$SegmentationModelCopyWithImpl;
@override @useResult
$Res call({
String? key, Buyer? buyer, DateTime? date, double? weight
String? key, Buyer? buyer, DateTime? date, int? weight
});
@@ -159,7 +159,7 @@ key: freezed == key ? _self.key : key // ignore: cast_nullable_to_non_nullable
as String?,buyer: freezed == buyer ? _self.buyer : buyer // ignore: cast_nullable_to_non_nullable
as Buyer?,date: freezed == date ? _self.date : date // ignore: cast_nullable_to_non_nullable
as DateTime?,weight: freezed == weight ? _self.weight : weight // ignore: cast_nullable_to_non_nullable
as double?,
as int?,
));
}

View File

@@ -15,7 +15,7 @@ _SegmentationModel _$SegmentationModelFromJson(Map<String, dynamic> json) =>
date: json['date'] == null
? null
: DateTime.parse(json['date'] as String),
weight: (json['weight'] as num?)?.toDouble(),
weight: (json['weight'] as num?)?.toInt(),
);
Map<String, dynamic> _$SegmentationModelToJson(_SegmentationModel instance) =>

View File

@@ -148,6 +148,8 @@ abstract class ChickenRepository {
Map<String, dynamic>? queryParameters,
});
Future<void> createSegmentation({required String token, required SegmentationModel model});
Future<void> editSegmentation({required String token, required SegmentationModel model});
Future<void> deleteSegmentation({required String token, required String key});

View File

@@ -450,6 +450,15 @@ class ChickenRepositoryImpl implements ChickenRepository {
return res.data;
}
@override
Future<void> createSegmentation({required String token, required SegmentationModel model}) async {
await _httpClient.post(
'/app-segmentation/',
data: model.toJson()..removeWhere((key, value) => value == null),
headers: {'Authorization': 'Bearer $token'},
);
}
@override
Future<void> editSegmentation({required String token, required SegmentationModel model}) async {
await _httpClient.put(

View File

@@ -1,6 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart' show Colors;
import 'package:flutter/widgets.dart';
import 'package:rasadyar_auth/data/services/token_storage_service.dart';
import 'package:rasadyar_auth/data/utils/safe_call.dart';
@@ -13,6 +12,7 @@ import 'package:rasadyar_chicken/presentation/pages/buy/view.dart';
import 'package:rasadyar_chicken/presentation/pages/home/view.dart';
import 'package:rasadyar_chicken/presentation/pages/profile/view.dart';
import 'package:rasadyar_chicken/presentation/pages/sale/view.dart';
import 'package:rasadyar_chicken/presentation/pages/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';
@@ -21,13 +21,7 @@ enum ErrorLocationType { serviceDisabled, permissionDenied, none }
class RootLogic extends GetxController {
RxInt currentPage = 2.obs;
List<Widget> pages = [
BuyPage(),
SalePage(),
HomePage(),
Container(color: Colors.blue),
ProfilePage(),
];
List<Widget> pages = [BuyPage(), SalePage(), HomePage(), SegmentationPage(), ProfilePage()];
final defaultRoutes = <int, String>{0: ChickenRoutes.buy, 1: ChickenRoutes.sale};

View File

@@ -0,0 +1,192 @@
import 'package:flutter/cupertino.dart';
import 'package:rasadyar_auth/data/utils/safe_call.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/root/logic.dart';
import 'package:rasadyar_chicken/presentation/utils/utils.dart';
import 'package:rasadyar_core/core.dart';
class SegmentationLogic extends GetxController {
RootLogic rootLogic = Get.find<RootLogic>();
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();
GlobalKey<FormState> formKey = GlobalKey<FormState>();
TextEditingController weightController = TextEditingController();
RxBool isSaleSubmitButtonEnabled = false.obs;
RxList<ProductModel> rolesProductsModel = RxList<ProductModel>();
Rxn<ProductModel> selectedProduct = Rxn<ProductModel>();
Rxn<SegmentationModel> selectedSegment = Rxn<SegmentationModel>();
Rx<Resource<PaginationModel<SegmentationModel>>> segmentationList =
Resource<PaginationModel<SegmentationModel>>.loading().obs;
@override
void onInit() {
super.onInit();
routesName = ['قطعه‌بندی'].toList();
getAllSegmentation();
}
@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),
);
}
void setEditData(SegmentationModel item) {
selectedSegment.value = item;
weightController.text = item.weight.toString();
}
void clearForm() {
weightController.clear();
isSaleSubmitButtonEnabled.value = false;
selectedProduct.value = null;
selectedSegment.value = null;
formKey.currentState?.reset();
}
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(
call: () => rootLogic.chickenRepository.deleteSegmentation(
token: rootLogic.tokenService.accessToken.value!,
key: key,
),
);
}
Future<void> getRolesProducts() async {
safeCall(
call: () async => await rootLogic.chickenRepository.getRolesProducts(
token: rootLogic.tokenService.accessToken.value!,
),
onSuccess: (result) {
if (result != null) {
rolesProductsModel.value = result;
selectedProduct.value = rolesProductsModel.first;
}
},
onError: (error, stacktrace) {},
);
}
Future<bool> editSegment() async {
var res = true;
safeCall(
call: () async => await rootLogic.chickenRepository.editSegmentation(
token: rootLogic.tokenService.accessToken.value!,
model: SegmentationModel(
key: selectedSegment.value?.key,
weight: int.tryParse(weightController.text) ?? 0,
),
),
onSuccess: (result) {
res = true;
},
onError: (error, stacktrace) {
res = false;
},
);
return res;
}
Future<bool> createSegment() async {
var res = true;
safeCall(
call: () async => await rootLogic.chickenRepository.createSegmentation(
token: rootLogic.tokenService.accessToken.value!,
model: SegmentationModel(
key: selectedProduct.value?.key,
weight: int.tryParse(weightController.text) ?? 0,
),
),
onSuccess: (result) {
res = true;
},
onError: (error, stacktrace) {
res = false;
},
);
return res;
}
}

View File

@@ -0,0 +1,331 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.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_chicken/presentation/widget/list_item/list_item.dart';
import 'package:rasadyar_chicken/presentation/widget/list_row_item.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(),
isBase: true,
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 ListItem2(
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.segmentationList),
),
],
floatingActionButton: RFab.add(
onPressed: () {
//TODO
//Get.bottomSheet(addOrEditSaleBottomSheet(), isScrollControlled: true);
},
),
);
}
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: 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?.shop ?? 'N/A',
textAlign: TextAlign.center,
style: AppFonts.yekan14.copyWith(color: AppColor.bgDark),
),
],
),
),
SizedBox(width: 4),
Expanded(
flex: 4,
child: Text(
item.date?.formattedJalaliDate,
textAlign: TextAlign.center,
style: AppFonts.yekan12.copyWith(color: AppColor.bgDark),
),
),
Expanded(
flex: 2,
child: Text(
'${item.weight} 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.buyer?.fullname ?? 'N/A'),
buildRow(title: 'تلفن خریدار', value: item.buyer?.mobile ?? 'N/A'),
buildRow(title: 'نام واحد', value: item.buyer?.shop ?? 'N/A'),
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,
).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: 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: [
RTextField(
controller: controller.weightController,
label: 'وزن',
keyboardType: TextInputType.number,
borderColor: AppColor.darkGreyLight,
filledColor: AppColor.bgLight,
filled: true,
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) {
controller.getAllSegmentation();
controller.clearForm();
Get.back();
}
}
: null,
height: 40,
);
}, controller.isSaleSubmitButtonEnabled);
}
Widget _productDropDown() {
return Obx(() {
return OverlayDropdownWidget<ProductModel>(
items: controller.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}',
),
],
),
);
});
}
}

View File

@@ -20,6 +20,7 @@ import 'package:rasadyar_chicken/presentation/pages/sales_out_of_province/logic.
import 'package:rasadyar_chicken/presentation/pages/sales_out_of_province/view.dart';
import 'package:rasadyar_chicken/presentation/pages/sales_out_of_province_buyers/logic.dart';
import 'package:rasadyar_chicken/presentation/pages/sales_out_of_province_sales_list/logic.dart';
import 'package:rasadyar_chicken/presentation/pages/segmentation/logic.dart';
import 'package:rasadyar_chicken/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/presentation/widget/base_page/logic.dart';
import 'package:rasadyar_chicken/presentation/widget/search/logic.dart';
@@ -40,6 +41,7 @@ sealed class ChickenPages {
Get.lazyPut(() => BuyLogic());
Get.lazyPut(() => SaleLogic());
Get.lazyPut(() => ProfileLogic());
Get.lazyPut(() => SegmentationLogic());
}),
),
@@ -53,7 +55,6 @@ sealed class ChickenPages {
}),
),
//sales
GetPage(
name: ChickenRoutes.sale,
@@ -61,9 +62,9 @@ sealed class ChickenPages {
middlewares: [AuthMiddleware()],
binding: BindingsBuilder(() {
Get.lazyPut(() => SaleLogic());
Get.lazyPut(() => BaseLogic());
Get.lazyPut(() => SalesOutOfProvinceLogic());
Get.lazyPut(() => RootLogic());
Get.lazyPut(() => BaseLogic());
Get.lazyPut(() => SalesOutOfProvinceLogic());
Get.lazyPut(() => RootLogic());
}),
),
GetPage(
@@ -83,7 +84,7 @@ sealed class ChickenPages {
middlewares: [AuthMiddleware()],
binding: BindingsBuilder(() {
Get.lazyPut(() => BaseLogic());
Get.lazyPut(() => SalesInProvinceLogic());
Get.lazyPut(() => SalesInProvinceLogic());
Get.lazyPut(() => SearchLogic());
}),
),
@@ -98,7 +99,6 @@ sealed class ChickenPages {
Get.lazyPut(() => BuyLogic());
}),
),
GetPage(
name: ChickenRoutes.buysOutOfProvince,
page: () => BuyOutOfProvincePage(),
@@ -106,10 +106,9 @@ sealed class ChickenPages {
binding: BindingsBuilder(() {
Get.lazyPut(() => BaseLogic());
Get.lazyPut(() => SearchLogic());
Get.lazyPut(() => BuyOutOfProvinceLogic());
Get.lazyPut(() => BuyOutOfProvinceLogic());
}),
),
GetPage(
name: ChickenRoutes.buysInProvince,
page: () => BuyInProvincePage(),
@@ -117,9 +116,9 @@ sealed class ChickenPages {
binding: BindingsBuilder(() {
Get.lazyPut(() => BaseLogic());
Get.lazyPut(() => SearchLogic());
Get.lazyPut(() => BuyInProvinceLogic());
Get.lazyPut(() => BuyInProvinceWaitingLogic());
Get.lazyPut(() => BuyInProvinceAllLogic());
Get.lazyPut(() => BuyInProvinceLogic());
Get.lazyPut(() => BuyInProvinceWaitingLogic());
Get.lazyPut(() => BuyInProvinceAllLogic());
}),
),
];

View File

@@ -6,6 +6,7 @@ sealed class ChickenRoutes {
static const home = '$_base/home';
static const buy = '$_base/buy';
static const sale = '$_base/sale';
static const segmentation = '$_base/segmentation';
//buys
static const buysOutOfProvince = '$buy/buyOutOfProvince';
@@ -14,4 +15,6 @@ sealed class ChickenRoutes {
//sales
static const salesInProvince = '$sale/SalesInProvince';
static const salesOutOfProvince = '$sale/saleOutOfProvince';
}

View File

@@ -48,9 +48,9 @@ export 'package:image_picker/image_picker.dart';
//utils
export 'utils/logger_utils.dart';
export 'utils/network/network.dart';
export 'utils/date_time_utils.dart';
export 'utils/num_utils.dart';
export 'utils/extension/date_time_utils.dart';
export 'utils/extension/num_utils.dart';
export 'utils/map_utils.dart';
export 'utils/route_utils.dart';
export 'utils/string_utils.dart';
export 'utils/extension/string_utils.dart';
export 'utils/separator_input_formatter.dart';

View File

@@ -0,0 +1,13 @@
import 'package:intl/intl.dart';
import 'package:persian_datetime_picker/persian_datetime_picker.dart';
extension XDateTime2 on DateTime {
get formattedJalaliDate {
final jalaliDate = Jalali.fromDateTime(this);
return "${jalaliDate.year}/${jalaliDate.month.toString().padLeft(2, '0')}/${jalaliDate.day.toString().padLeft(2, '0')}";
}
get formattedYHMS {
return DateFormat('yyyy-MM-dd HH:mm:ss').format(this);
}
}

View File

@@ -1,7 +1,18 @@
import 'package:intl/intl.dart';
import 'package:persian_datetime_picker/persian_datetime_picker.dart';
extension XDateTime on String {
extension XString on String {
String get separatedByComma {
final formatter = NumberFormat('#,###');
final number = num.tryParse(this);
return number != null ? formatter.format(number) : this;
}
String get clearComma {
return replaceAll(RegExp(r'\D'), '');
}
get toDateTime => DateTime.parse(this);
String get formattedJalaliDate {
@@ -26,15 +37,4 @@ extension XDateTime on String {
final dateTime = DateTime.parse(this);
return Jalali.fromDateTime(dateTime);
}
}
extension XDateTime2 on DateTime {
get formattedJalaliDate {
final jalaliDate = Jalali.fromDateTime(this);
return "${jalaliDate.year}/${jalaliDate.month.toString().padLeft(2, '0')}/${jalaliDate.day.toString().padLeft(2, '0')}";
}
get formattedYHMS {
return DateFormat('yyyy-MM-dd HH:mm:ss').format(this);
}
}
}

View File

@@ -1,13 +0,0 @@
import 'package:intl/intl.dart';
extension XString on String {
String get separatedByComma {
final formatter = NumberFormat('#,###');
final number = num.tryParse(this);
return number != null ? formatter.format(number) : this;
}
String get clearComma {
return replaceAll(RegExp(r'\D'), '');
}
}

View File

@@ -0,0 +1,14 @@
export 'mixins/pagination_controller_mixin.dart';
export 'network/network.dart';
export 'extension/date_time_utils.dart';
export 'extension/num_utils.dart';
export 'extension/string_utils.dart';
export 'apk_updater.dart';
export 'logger_utils.dart';
export 'map_utils.dart';
export 'route_utils.dart';
export 'separator_input_formatter.dart';