From 078fe9a022a63a2aaf9b6bb846b38bf256159574 Mon Sep 17 00:00:00 2001 From: "mr.mojtaba" Date: Mon, 23 Jun 2025 16:49:12 +0330 Subject: [PATCH] feat : buyer in sale out of the province --- .../out_province_carcasses_buyer.dart | 60 ++ .../out_province_carcasses_buyer.freezed.dart | 543 ++++++++++++ .../out_province_carcasses_buyer.g.dart | 95 +++ .../data/repositories/chicken_repository.dart | 11 + .../repositories/chicken_repository_imp.dart | 27 + .../pages/buys_out_of_province/logic.dart | 191 ++++- .../pages/buys_out_of_province/view.dart | 792 +++++++++++++++++- .../pages/out_of_province/view.dart | 11 +- .../pages/sales_out_of_province/logic.dart | 119 ++- .../pages/sales_out_of_province/view.dart | 246 +++--- .../widgets/buyers_page.dart | 346 ++++++++ .../widgets/empty_widget.dart | 8 + packages/core/lib/utils/map_utils.dart | 17 +- 13 files changed, 2268 insertions(+), 198 deletions(-) create mode 100644 packages/chicken/lib/data/models/response/out_province_carcasses_buyer/out_province_carcasses_buyer.dart create mode 100644 packages/chicken/lib/data/models/response/out_province_carcasses_buyer/out_province_carcasses_buyer.freezed.dart create mode 100644 packages/chicken/lib/data/models/response/out_province_carcasses_buyer/out_province_carcasses_buyer.g.dart create mode 100644 packages/chicken/lib/presentation/pages/sales_out_of_province/widgets/buyers_page.dart create mode 100644 packages/chicken/lib/presentation/pages/sales_out_of_province/widgets/empty_widget.dart diff --git a/packages/chicken/lib/data/models/response/out_province_carcasses_buyer/out_province_carcasses_buyer.dart b/packages/chicken/lib/data/models/response/out_province_carcasses_buyer/out_province_carcasses_buyer.dart new file mode 100644 index 0000000..ea48447 --- /dev/null +++ b/packages/chicken/lib/data/models/response/out_province_carcasses_buyer/out_province_carcasses_buyer.dart @@ -0,0 +1,60 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'out_province_carcasses_buyer.freezed.dart'; +part 'out_province_carcasses_buyer.g.dart'; + +@freezed +abstract class OutProvinceCarcassesBuyer with _$OutProvinceCarcassesBuyer { + const factory OutProvinceCarcassesBuyer({ + Buyer? buyer, + RequestsInfo? requestsInfo, + String? key, + bool? trash, + String? fullname, + String? firstName, + String? lastName, + String? mobile, + String? unitName, + String? city, + String? province, + String? role, + bool? active, + String? typeActivity, + dynamic killHouse, + int? steward, + }) = _OutProvinceCarcassesBuyer; + + factory OutProvinceCarcassesBuyer.fromJson(Map json) => + _$OutProvinceCarcassesBuyerFromJson(json); +} + +@freezed +abstract class Buyer with _$Buyer { + const factory Buyer({ + String? key, + bool? trash, + String? fullname, + String? firstName, + String? lastName, + String? mobile, + String? unitName, + String? city, + String? province, + bool? active, + int? user, + }) = _Buyer; + + factory Buyer.fromJson(Map json) => _$BuyerFromJson(json); +} + +@freezed +abstract class RequestsInfo with _$RequestsInfo { + const factory RequestsInfo({ + int? numberOfRequests, + int? totalQuantity, + double? totalWeight, + }) = _RequestsInfo; + + factory RequestsInfo.fromJson(Map json) => + _$RequestsInfoFromJson(json); +} diff --git a/packages/chicken/lib/data/models/response/out_province_carcasses_buyer/out_province_carcasses_buyer.freezed.dart b/packages/chicken/lib/data/models/response/out_province_carcasses_buyer/out_province_carcasses_buyer.freezed.dart new file mode 100644 index 0000000..9019d66 --- /dev/null +++ b/packages/chicken/lib/data/models/response/out_province_carcasses_buyer/out_province_carcasses_buyer.freezed.dart @@ -0,0 +1,543 @@ +// dart format width=80 +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'out_province_carcasses_buyer.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$OutProvinceCarcassesBuyer { + + Buyer? get buyer; RequestsInfo? get requestsInfo; String? get key; bool? get trash; String? get fullname; String? get firstName; String? get lastName; String? get mobile; String? get unitName; String? get city; String? get province; String? get role; bool? get active; String? get typeActivity; dynamic get killHouse; int? get steward; +/// Create a copy of OutProvinceCarcassesBuyer +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$OutProvinceCarcassesBuyerCopyWith get copyWith => _$OutProvinceCarcassesBuyerCopyWithImpl(this as OutProvinceCarcassesBuyer, _$identity); + + /// Serializes this OutProvinceCarcassesBuyer to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is OutProvinceCarcassesBuyer&&(identical(other.buyer, buyer) || other.buyer == buyer)&&(identical(other.requestsInfo, requestsInfo) || other.requestsInfo == requestsInfo)&&(identical(other.key, key) || other.key == key)&&(identical(other.trash, trash) || other.trash == trash)&&(identical(other.fullname, fullname) || other.fullname == fullname)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.mobile, mobile) || other.mobile == mobile)&&(identical(other.unitName, unitName) || other.unitName == unitName)&&(identical(other.city, city) || other.city == city)&&(identical(other.province, province) || other.province == province)&&(identical(other.role, role) || other.role == role)&&(identical(other.active, active) || other.active == active)&&(identical(other.typeActivity, typeActivity) || other.typeActivity == typeActivity)&&const DeepCollectionEquality().equals(other.killHouse, killHouse)&&(identical(other.steward, steward) || other.steward == steward)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,buyer,requestsInfo,key,trash,fullname,firstName,lastName,mobile,unitName,city,province,role,active,typeActivity,const DeepCollectionEquality().hash(killHouse),steward); + +@override +String toString() { + return 'OutProvinceCarcassesBuyer(buyer: $buyer, requestsInfo: $requestsInfo, key: $key, trash: $trash, fullname: $fullname, firstName: $firstName, lastName: $lastName, mobile: $mobile, unitName: $unitName, city: $city, province: $province, role: $role, active: $active, typeActivity: $typeActivity, killHouse: $killHouse, steward: $steward)'; +} + + +} + +/// @nodoc +abstract mixin class $OutProvinceCarcassesBuyerCopyWith<$Res> { + factory $OutProvinceCarcassesBuyerCopyWith(OutProvinceCarcassesBuyer value, $Res Function(OutProvinceCarcassesBuyer) _then) = _$OutProvinceCarcassesBuyerCopyWithImpl; +@useResult +$Res call({ + Buyer? buyer, RequestsInfo? requestsInfo, String? key, bool? trash, String? fullname, String? firstName, String? lastName, String? mobile, String? unitName, String? city, String? province, String? role, bool? active, String? typeActivity, dynamic killHouse, int? steward +}); + + +$BuyerCopyWith<$Res>? get buyer;$RequestsInfoCopyWith<$Res>? get requestsInfo; + +} +/// @nodoc +class _$OutProvinceCarcassesBuyerCopyWithImpl<$Res> + implements $OutProvinceCarcassesBuyerCopyWith<$Res> { + _$OutProvinceCarcassesBuyerCopyWithImpl(this._self, this._then); + + final OutProvinceCarcassesBuyer _self; + final $Res Function(OutProvinceCarcassesBuyer) _then; + +/// Create a copy of OutProvinceCarcassesBuyer +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? buyer = freezed,Object? requestsInfo = freezed,Object? key = freezed,Object? trash = freezed,Object? fullname = freezed,Object? firstName = freezed,Object? lastName = freezed,Object? mobile = freezed,Object? unitName = freezed,Object? city = freezed,Object? province = freezed,Object? role = freezed,Object? active = freezed,Object? typeActivity = freezed,Object? killHouse = freezed,Object? steward = freezed,}) { + return _then(_self.copyWith( +buyer: freezed == buyer ? _self.buyer : buyer // ignore: cast_nullable_to_non_nullable +as Buyer?,requestsInfo: freezed == requestsInfo ? _self.requestsInfo : requestsInfo // ignore: cast_nullable_to_non_nullable +as RequestsInfo?,key: freezed == key ? _self.key : key // ignore: cast_nullable_to_non_nullable +as String?,trash: freezed == trash ? _self.trash : trash // ignore: cast_nullable_to_non_nullable +as bool?,fullname: freezed == fullname ? _self.fullname : fullname // ignore: cast_nullable_to_non_nullable +as String?,firstName: freezed == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable +as String?,lastName: freezed == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable +as String?,mobile: freezed == mobile ? _self.mobile : mobile // ignore: cast_nullable_to_non_nullable +as String?,unitName: freezed == unitName ? _self.unitName : unitName // ignore: cast_nullable_to_non_nullable +as String?,city: freezed == city ? _self.city : city // ignore: cast_nullable_to_non_nullable +as String?,province: freezed == province ? _self.province : province // ignore: cast_nullable_to_non_nullable +as String?,role: freezed == role ? _self.role : role // ignore: cast_nullable_to_non_nullable +as String?,active: freezed == active ? _self.active : active // ignore: cast_nullable_to_non_nullable +as bool?,typeActivity: freezed == typeActivity ? _self.typeActivity : typeActivity // ignore: cast_nullable_to_non_nullable +as String?,killHouse: freezed == killHouse ? _self.killHouse : killHouse // ignore: cast_nullable_to_non_nullable +as dynamic,steward: freezed == steward ? _self.steward : steward // ignore: cast_nullable_to_non_nullable +as int?, + )); +} +/// Create a copy of OutProvinceCarcassesBuyer +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$BuyerCopyWith<$Res>? get buyer { + if (_self.buyer == null) { + return null; + } + + return $BuyerCopyWith<$Res>(_self.buyer!, (value) { + return _then(_self.copyWith(buyer: value)); + }); +}/// Create a copy of OutProvinceCarcassesBuyer +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$RequestsInfoCopyWith<$Res>? get requestsInfo { + if (_self.requestsInfo == null) { + return null; + } + + return $RequestsInfoCopyWith<$Res>(_self.requestsInfo!, (value) { + return _then(_self.copyWith(requestsInfo: value)); + }); +} +} + + +/// @nodoc +@JsonSerializable() + +class _OutProvinceCarcassesBuyer implements OutProvinceCarcassesBuyer { + const _OutProvinceCarcassesBuyer({this.buyer, this.requestsInfo, this.key, this.trash, this.fullname, this.firstName, this.lastName, this.mobile, this.unitName, this.city, this.province, this.role, this.active, this.typeActivity, this.killHouse, this.steward}); + factory _OutProvinceCarcassesBuyer.fromJson(Map json) => _$OutProvinceCarcassesBuyerFromJson(json); + +@override final Buyer? buyer; +@override final RequestsInfo? requestsInfo; +@override final String? key; +@override final bool? trash; +@override final String? fullname; +@override final String? firstName; +@override final String? lastName; +@override final String? mobile; +@override final String? unitName; +@override final String? city; +@override final String? province; +@override final String? role; +@override final bool? active; +@override final String? typeActivity; +@override final dynamic killHouse; +@override final int? steward; + +/// Create a copy of OutProvinceCarcassesBuyer +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$OutProvinceCarcassesBuyerCopyWith<_OutProvinceCarcassesBuyer> get copyWith => __$OutProvinceCarcassesBuyerCopyWithImpl<_OutProvinceCarcassesBuyer>(this, _$identity); + +@override +Map toJson() { + return _$OutProvinceCarcassesBuyerToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _OutProvinceCarcassesBuyer&&(identical(other.buyer, buyer) || other.buyer == buyer)&&(identical(other.requestsInfo, requestsInfo) || other.requestsInfo == requestsInfo)&&(identical(other.key, key) || other.key == key)&&(identical(other.trash, trash) || other.trash == trash)&&(identical(other.fullname, fullname) || other.fullname == fullname)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.mobile, mobile) || other.mobile == mobile)&&(identical(other.unitName, unitName) || other.unitName == unitName)&&(identical(other.city, city) || other.city == city)&&(identical(other.province, province) || other.province == province)&&(identical(other.role, role) || other.role == role)&&(identical(other.active, active) || other.active == active)&&(identical(other.typeActivity, typeActivity) || other.typeActivity == typeActivity)&&const DeepCollectionEquality().equals(other.killHouse, killHouse)&&(identical(other.steward, steward) || other.steward == steward)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,buyer,requestsInfo,key,trash,fullname,firstName,lastName,mobile,unitName,city,province,role,active,typeActivity,const DeepCollectionEquality().hash(killHouse),steward); + +@override +String toString() { + return 'OutProvinceCarcassesBuyer(buyer: $buyer, requestsInfo: $requestsInfo, key: $key, trash: $trash, fullname: $fullname, firstName: $firstName, lastName: $lastName, mobile: $mobile, unitName: $unitName, city: $city, province: $province, role: $role, active: $active, typeActivity: $typeActivity, killHouse: $killHouse, steward: $steward)'; +} + + +} + +/// @nodoc +abstract mixin class _$OutProvinceCarcassesBuyerCopyWith<$Res> implements $OutProvinceCarcassesBuyerCopyWith<$Res> { + factory _$OutProvinceCarcassesBuyerCopyWith(_OutProvinceCarcassesBuyer value, $Res Function(_OutProvinceCarcassesBuyer) _then) = __$OutProvinceCarcassesBuyerCopyWithImpl; +@override @useResult +$Res call({ + Buyer? buyer, RequestsInfo? requestsInfo, String? key, bool? trash, String? fullname, String? firstName, String? lastName, String? mobile, String? unitName, String? city, String? province, String? role, bool? active, String? typeActivity, dynamic killHouse, int? steward +}); + + +@override $BuyerCopyWith<$Res>? get buyer;@override $RequestsInfoCopyWith<$Res>? get requestsInfo; + +} +/// @nodoc +class __$OutProvinceCarcassesBuyerCopyWithImpl<$Res> + implements _$OutProvinceCarcassesBuyerCopyWith<$Res> { + __$OutProvinceCarcassesBuyerCopyWithImpl(this._self, this._then); + + final _OutProvinceCarcassesBuyer _self; + final $Res Function(_OutProvinceCarcassesBuyer) _then; + +/// Create a copy of OutProvinceCarcassesBuyer +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? buyer = freezed,Object? requestsInfo = freezed,Object? key = freezed,Object? trash = freezed,Object? fullname = freezed,Object? firstName = freezed,Object? lastName = freezed,Object? mobile = freezed,Object? unitName = freezed,Object? city = freezed,Object? province = freezed,Object? role = freezed,Object? active = freezed,Object? typeActivity = freezed,Object? killHouse = freezed,Object? steward = freezed,}) { + return _then(_OutProvinceCarcassesBuyer( +buyer: freezed == buyer ? _self.buyer : buyer // ignore: cast_nullable_to_non_nullable +as Buyer?,requestsInfo: freezed == requestsInfo ? _self.requestsInfo : requestsInfo // ignore: cast_nullable_to_non_nullable +as RequestsInfo?,key: freezed == key ? _self.key : key // ignore: cast_nullable_to_non_nullable +as String?,trash: freezed == trash ? _self.trash : trash // ignore: cast_nullable_to_non_nullable +as bool?,fullname: freezed == fullname ? _self.fullname : fullname // ignore: cast_nullable_to_non_nullable +as String?,firstName: freezed == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable +as String?,lastName: freezed == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable +as String?,mobile: freezed == mobile ? _self.mobile : mobile // ignore: cast_nullable_to_non_nullable +as String?,unitName: freezed == unitName ? _self.unitName : unitName // ignore: cast_nullable_to_non_nullable +as String?,city: freezed == city ? _self.city : city // ignore: cast_nullable_to_non_nullable +as String?,province: freezed == province ? _self.province : province // ignore: cast_nullable_to_non_nullable +as String?,role: freezed == role ? _self.role : role // ignore: cast_nullable_to_non_nullable +as String?,active: freezed == active ? _self.active : active // ignore: cast_nullable_to_non_nullable +as bool?,typeActivity: freezed == typeActivity ? _self.typeActivity : typeActivity // ignore: cast_nullable_to_non_nullable +as String?,killHouse: freezed == killHouse ? _self.killHouse : killHouse // ignore: cast_nullable_to_non_nullable +as dynamic,steward: freezed == steward ? _self.steward : steward // ignore: cast_nullable_to_non_nullable +as int?, + )); +} + +/// Create a copy of OutProvinceCarcassesBuyer +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$BuyerCopyWith<$Res>? get buyer { + if (_self.buyer == null) { + return null; + } + + return $BuyerCopyWith<$Res>(_self.buyer!, (value) { + return _then(_self.copyWith(buyer: value)); + }); +}/// Create a copy of OutProvinceCarcassesBuyer +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$RequestsInfoCopyWith<$Res>? get requestsInfo { + if (_self.requestsInfo == null) { + return null; + } + + return $RequestsInfoCopyWith<$Res>(_self.requestsInfo!, (value) { + return _then(_self.copyWith(requestsInfo: value)); + }); +} +} + + +/// @nodoc +mixin _$Buyer { + + String? get key; bool? get trash; String? get fullname; String? get firstName; String? get lastName; String? get mobile; String? get unitName; String? get city; String? get province; bool? get active; int? get user; +/// Create a copy of Buyer +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$BuyerCopyWith get copyWith => _$BuyerCopyWithImpl(this as Buyer, _$identity); + + /// Serializes this Buyer to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is Buyer&&(identical(other.key, key) || other.key == key)&&(identical(other.trash, trash) || other.trash == trash)&&(identical(other.fullname, fullname) || other.fullname == fullname)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.mobile, mobile) || other.mobile == mobile)&&(identical(other.unitName, unitName) || other.unitName == unitName)&&(identical(other.city, city) || other.city == city)&&(identical(other.province, province) || other.province == province)&&(identical(other.active, active) || other.active == active)&&(identical(other.user, user) || other.user == user)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,key,trash,fullname,firstName,lastName,mobile,unitName,city,province,active,user); + +@override +String toString() { + return 'Buyer(key: $key, trash: $trash, fullname: $fullname, firstName: $firstName, lastName: $lastName, mobile: $mobile, unitName: $unitName, city: $city, province: $province, active: $active, user: $user)'; +} + + +} + +/// @nodoc +abstract mixin class $BuyerCopyWith<$Res> { + factory $BuyerCopyWith(Buyer value, $Res Function(Buyer) _then) = _$BuyerCopyWithImpl; +@useResult +$Res call({ + String? key, bool? trash, String? fullname, String? firstName, String? lastName, String? mobile, String? unitName, String? city, String? province, bool? active, int? user +}); + + + + +} +/// @nodoc +class _$BuyerCopyWithImpl<$Res> + implements $BuyerCopyWith<$Res> { + _$BuyerCopyWithImpl(this._self, this._then); + + final Buyer _self; + final $Res Function(Buyer) _then; + +/// Create a copy of Buyer +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? key = freezed,Object? trash = freezed,Object? fullname = freezed,Object? firstName = freezed,Object? lastName = freezed,Object? mobile = freezed,Object? unitName = freezed,Object? city = freezed,Object? province = freezed,Object? active = freezed,Object? user = freezed,}) { + return _then(_self.copyWith( +key: freezed == key ? _self.key : key // ignore: cast_nullable_to_non_nullable +as String?,trash: freezed == trash ? _self.trash : trash // ignore: cast_nullable_to_non_nullable +as bool?,fullname: freezed == fullname ? _self.fullname : fullname // ignore: cast_nullable_to_non_nullable +as String?,firstName: freezed == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable +as String?,lastName: freezed == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable +as String?,mobile: freezed == mobile ? _self.mobile : mobile // ignore: cast_nullable_to_non_nullable +as String?,unitName: freezed == unitName ? _self.unitName : unitName // ignore: cast_nullable_to_non_nullable +as String?,city: freezed == city ? _self.city : city // ignore: cast_nullable_to_non_nullable +as String?,province: freezed == province ? _self.province : province // ignore: cast_nullable_to_non_nullable +as String?,active: freezed == active ? _self.active : active // ignore: cast_nullable_to_non_nullable +as bool?,user: freezed == user ? _self.user : user // ignore: cast_nullable_to_non_nullable +as int?, + )); +} + +} + + +/// @nodoc +@JsonSerializable() + +class _Buyer implements Buyer { + const _Buyer({this.key, this.trash, this.fullname, this.firstName, this.lastName, this.mobile, this.unitName, this.city, this.province, this.active, this.user}); + factory _Buyer.fromJson(Map json) => _$BuyerFromJson(json); + +@override final String? key; +@override final bool? trash; +@override final String? fullname; +@override final String? firstName; +@override final String? lastName; +@override final String? mobile; +@override final String? unitName; +@override final String? city; +@override final String? province; +@override final bool? active; +@override final int? user; + +/// Create a copy of Buyer +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$BuyerCopyWith<_Buyer> get copyWith => __$BuyerCopyWithImpl<_Buyer>(this, _$identity); + +@override +Map toJson() { + return _$BuyerToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _Buyer&&(identical(other.key, key) || other.key == key)&&(identical(other.trash, trash) || other.trash == trash)&&(identical(other.fullname, fullname) || other.fullname == fullname)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.mobile, mobile) || other.mobile == mobile)&&(identical(other.unitName, unitName) || other.unitName == unitName)&&(identical(other.city, city) || other.city == city)&&(identical(other.province, province) || other.province == province)&&(identical(other.active, active) || other.active == active)&&(identical(other.user, user) || other.user == user)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,key,trash,fullname,firstName,lastName,mobile,unitName,city,province,active,user); + +@override +String toString() { + return 'Buyer(key: $key, trash: $trash, fullname: $fullname, firstName: $firstName, lastName: $lastName, mobile: $mobile, unitName: $unitName, city: $city, province: $province, active: $active, user: $user)'; +} + + +} + +/// @nodoc +abstract mixin class _$BuyerCopyWith<$Res> implements $BuyerCopyWith<$Res> { + factory _$BuyerCopyWith(_Buyer value, $Res Function(_Buyer) _then) = __$BuyerCopyWithImpl; +@override @useResult +$Res call({ + String? key, bool? trash, String? fullname, String? firstName, String? lastName, String? mobile, String? unitName, String? city, String? province, bool? active, int? user +}); + + + + +} +/// @nodoc +class __$BuyerCopyWithImpl<$Res> + implements _$BuyerCopyWith<$Res> { + __$BuyerCopyWithImpl(this._self, this._then); + + final _Buyer _self; + final $Res Function(_Buyer) _then; + +/// Create a copy of Buyer +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? key = freezed,Object? trash = freezed,Object? fullname = freezed,Object? firstName = freezed,Object? lastName = freezed,Object? mobile = freezed,Object? unitName = freezed,Object? city = freezed,Object? province = freezed,Object? active = freezed,Object? user = freezed,}) { + return _then(_Buyer( +key: freezed == key ? _self.key : key // ignore: cast_nullable_to_non_nullable +as String?,trash: freezed == trash ? _self.trash : trash // ignore: cast_nullable_to_non_nullable +as bool?,fullname: freezed == fullname ? _self.fullname : fullname // ignore: cast_nullable_to_non_nullable +as String?,firstName: freezed == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable +as String?,lastName: freezed == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable +as String?,mobile: freezed == mobile ? _self.mobile : mobile // ignore: cast_nullable_to_non_nullable +as String?,unitName: freezed == unitName ? _self.unitName : unitName // ignore: cast_nullable_to_non_nullable +as String?,city: freezed == city ? _self.city : city // ignore: cast_nullable_to_non_nullable +as String?,province: freezed == province ? _self.province : province // ignore: cast_nullable_to_non_nullable +as String?,active: freezed == active ? _self.active : active // ignore: cast_nullable_to_non_nullable +as bool?,user: freezed == user ? _self.user : user // ignore: cast_nullable_to_non_nullable +as int?, + )); +} + + +} + + +/// @nodoc +mixin _$RequestsInfo { + + int? get numberOfRequests; int? get totalQuantity; double? get totalWeight; +/// Create a copy of RequestsInfo +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$RequestsInfoCopyWith get copyWith => _$RequestsInfoCopyWithImpl(this as RequestsInfo, _$identity); + + /// Serializes this RequestsInfo to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is RequestsInfo&&(identical(other.numberOfRequests, numberOfRequests) || other.numberOfRequests == numberOfRequests)&&(identical(other.totalQuantity, totalQuantity) || other.totalQuantity == totalQuantity)&&(identical(other.totalWeight, totalWeight) || other.totalWeight == totalWeight)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,numberOfRequests,totalQuantity,totalWeight); + +@override +String toString() { + return 'RequestsInfo(numberOfRequests: $numberOfRequests, totalQuantity: $totalQuantity, totalWeight: $totalWeight)'; +} + + +} + +/// @nodoc +abstract mixin class $RequestsInfoCopyWith<$Res> { + factory $RequestsInfoCopyWith(RequestsInfo value, $Res Function(RequestsInfo) _then) = _$RequestsInfoCopyWithImpl; +@useResult +$Res call({ + int? numberOfRequests, int? totalQuantity, double? totalWeight +}); + + + + +} +/// @nodoc +class _$RequestsInfoCopyWithImpl<$Res> + implements $RequestsInfoCopyWith<$Res> { + _$RequestsInfoCopyWithImpl(this._self, this._then); + + final RequestsInfo _self; + final $Res Function(RequestsInfo) _then; + +/// Create a copy of RequestsInfo +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? numberOfRequests = freezed,Object? totalQuantity = freezed,Object? totalWeight = freezed,}) { + return _then(_self.copyWith( +numberOfRequests: freezed == numberOfRequests ? _self.numberOfRequests : numberOfRequests // ignore: cast_nullable_to_non_nullable +as int?,totalQuantity: freezed == totalQuantity ? _self.totalQuantity : totalQuantity // ignore: cast_nullable_to_non_nullable +as int?,totalWeight: freezed == totalWeight ? _self.totalWeight : totalWeight // ignore: cast_nullable_to_non_nullable +as double?, + )); +} + +} + + +/// @nodoc +@JsonSerializable() + +class _RequestsInfo implements RequestsInfo { + const _RequestsInfo({this.numberOfRequests, this.totalQuantity, this.totalWeight}); + factory _RequestsInfo.fromJson(Map json) => _$RequestsInfoFromJson(json); + +@override final int? numberOfRequests; +@override final int? totalQuantity; +@override final double? totalWeight; + +/// Create a copy of RequestsInfo +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$RequestsInfoCopyWith<_RequestsInfo> get copyWith => __$RequestsInfoCopyWithImpl<_RequestsInfo>(this, _$identity); + +@override +Map toJson() { + return _$RequestsInfoToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _RequestsInfo&&(identical(other.numberOfRequests, numberOfRequests) || other.numberOfRequests == numberOfRequests)&&(identical(other.totalQuantity, totalQuantity) || other.totalQuantity == totalQuantity)&&(identical(other.totalWeight, totalWeight) || other.totalWeight == totalWeight)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,numberOfRequests,totalQuantity,totalWeight); + +@override +String toString() { + return 'RequestsInfo(numberOfRequests: $numberOfRequests, totalQuantity: $totalQuantity, totalWeight: $totalWeight)'; +} + + +} + +/// @nodoc +abstract mixin class _$RequestsInfoCopyWith<$Res> implements $RequestsInfoCopyWith<$Res> { + factory _$RequestsInfoCopyWith(_RequestsInfo value, $Res Function(_RequestsInfo) _then) = __$RequestsInfoCopyWithImpl; +@override @useResult +$Res call({ + int? numberOfRequests, int? totalQuantity, double? totalWeight +}); + + + + +} +/// @nodoc +class __$RequestsInfoCopyWithImpl<$Res> + implements _$RequestsInfoCopyWith<$Res> { + __$RequestsInfoCopyWithImpl(this._self, this._then); + + final _RequestsInfo _self; + final $Res Function(_RequestsInfo) _then; + +/// Create a copy of RequestsInfo +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? numberOfRequests = freezed,Object? totalQuantity = freezed,Object? totalWeight = freezed,}) { + return _then(_RequestsInfo( +numberOfRequests: freezed == numberOfRequests ? _self.numberOfRequests : numberOfRequests // ignore: cast_nullable_to_non_nullable +as int?,totalQuantity: freezed == totalQuantity ? _self.totalQuantity : totalQuantity // ignore: cast_nullable_to_non_nullable +as int?,totalWeight: freezed == totalWeight ? _self.totalWeight : totalWeight // ignore: cast_nullable_to_non_nullable +as double?, + )); +} + + +} + +// dart format on diff --git a/packages/chicken/lib/data/models/response/out_province_carcasses_buyer/out_province_carcasses_buyer.g.dart b/packages/chicken/lib/data/models/response/out_province_carcasses_buyer/out_province_carcasses_buyer.g.dart new file mode 100644 index 0000000..c042f9f --- /dev/null +++ b/packages/chicken/lib/data/models/response/out_province_carcasses_buyer/out_province_carcasses_buyer.g.dart @@ -0,0 +1,95 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'out_province_carcasses_buyer.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_OutProvinceCarcassesBuyer _$OutProvinceCarcassesBuyerFromJson( + Map json, +) => _OutProvinceCarcassesBuyer( + buyer: json['buyer'] == null + ? null + : Buyer.fromJson(json['buyer'] as Map), + requestsInfo: json['requests_info'] == null + ? null + : RequestsInfo.fromJson(json['requests_info'] as Map), + key: json['key'] as String?, + trash: json['trash'] as bool?, + fullname: json['fullname'] as String?, + firstName: json['first_name'] as String?, + lastName: json['last_name'] as String?, + mobile: json['mobile'] as String?, + unitName: json['unit_name'] as String?, + city: json['city'] as String?, + province: json['province'] as String?, + role: json['role'] as String?, + active: json['active'] as bool?, + typeActivity: json['type_activity'] as String?, + killHouse: json['kill_house'], + steward: (json['steward'] as num?)?.toInt(), +); + +Map _$OutProvinceCarcassesBuyerToJson( + _OutProvinceCarcassesBuyer instance, +) => { + 'buyer': instance.buyer, + 'requests_info': instance.requestsInfo, + 'key': instance.key, + 'trash': instance.trash, + 'fullname': instance.fullname, + 'first_name': instance.firstName, + 'last_name': instance.lastName, + 'mobile': instance.mobile, + 'unit_name': instance.unitName, + 'city': instance.city, + 'province': instance.province, + 'role': instance.role, + 'active': instance.active, + 'type_activity': instance.typeActivity, + 'kill_house': instance.killHouse, + 'steward': instance.steward, +}; + +_Buyer _$BuyerFromJson(Map json) => _Buyer( + key: json['key'] as String?, + trash: json['trash'] as bool?, + fullname: json['fullname'] as String?, + firstName: json['first_name'] as String?, + lastName: json['last_name'] as String?, + mobile: json['mobile'] as String?, + unitName: json['unit_name'] as String?, + city: json['city'] as String?, + province: json['province'] as String?, + active: json['active'] as bool?, + user: (json['user'] as num?)?.toInt(), +); + +Map _$BuyerToJson(_Buyer instance) => { + 'key': instance.key, + 'trash': instance.trash, + 'fullname': instance.fullname, + 'first_name': instance.firstName, + 'last_name': instance.lastName, + 'mobile': instance.mobile, + 'unit_name': instance.unitName, + 'city': instance.city, + 'province': instance.province, + 'active': instance.active, + 'user': instance.user, +}; + +_RequestsInfo _$RequestsInfoFromJson(Map json) => + _RequestsInfo( + numberOfRequests: (json['number_of_requests'] as num?)?.toInt(), + totalQuantity: (json['total_quantity'] as num?)?.toInt(), + totalWeight: (json['total_weight'] as num?)?.toDouble(), + ); + +Map _$RequestsInfoToJson(_RequestsInfo instance) => + { + 'number_of_requests': instance.numberOfRequests, + 'total_quantity': instance.totalQuantity, + 'total_weight': instance.totalWeight, + }; diff --git a/packages/chicken/lib/data/repositories/chicken_repository.dart b/packages/chicken/lib/data/repositories/chicken_repository.dart index 250fbd8..a6c4e2b 100644 --- a/packages/chicken/lib/data/repositories/chicken_repository.dart +++ b/packages/chicken/lib/data/repositories/chicken_repository.dart @@ -8,6 +8,7 @@ import 'package:rasadyar_chicken/data/models/response/imported_loads_model/impor 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/kill_house_distribution_info/kill_house_distribution_info.dart'; +import 'package:rasadyar_chicken/data/models/response/out_province_carcasses_buyer/out_province_carcasses_buyer.dart'; import 'package:rasadyar_chicken/data/models/response/pagination_model/pagination_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'; @@ -62,10 +63,20 @@ abstract class ChickenRepository { Map? queryParameters, }); + Future createStewardPurchasesOutSideOfTheProvince({required String token, required CreateStewardFreeBar body}); Future deleteStewardPurchasesOutSideOfTheProvince({required String token, required String stewardFreeBarKey}); + Future?> getOutProvinceCarcassesBuyer({ + required String token, + Map? queryParameters, + }); + + + Future createOutProvinceCarcassesBuyer({required String token, required OutProvinceCarcassesBuyer body}); + + Future?> getProvince(); Future?> getCity({required String provinceName}); diff --git a/packages/chicken/lib/data/repositories/chicken_repository_imp.dart b/packages/chicken/lib/data/repositories/chicken_repository_imp.dart index 556c6ba..2379751 100644 --- a/packages/chicken/lib/data/repositories/chicken_repository_imp.dart +++ b/packages/chicken/lib/data/repositories/chicken_repository_imp.dart @@ -9,6 +9,7 @@ import 'package:rasadyar_chicken/data/models/response/imported_loads_model/impor 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/kill_house_distribution_info/kill_house_distribution_info.dart'; +import 'package:rasadyar_chicken/data/models/response/out_province_carcasses_buyer/out_province_carcasses_buyer.dart'; import 'package:rasadyar_chicken/data/models/response/pagination_model/pagination_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'; @@ -243,4 +244,30 @@ class ChickenRepositoryImpl implements ChickenRepository { queryParameters: {'key': stewardFreeBarKey}, ); } + + @override + Future?> getOutProvinceCarcassesBuyer({ + required String token, + Map? queryParameters, + }) async { + var res = await _httpClient.get( + '/out-province-carcasses-buyer/?', + queryParameters: queryParameters, + headers: {'Authorization': 'Bearer $token'}, + fromJson: (json) => PaginationModel.fromJson( + json, + (json) => OutProvinceCarcassesBuyer.fromJson(json as Map), + ), + ); + return res.data; + } + + @override + Future createOutProvinceCarcassesBuyer({required String token, required OutProvinceCarcassesBuyer body}) async { + await _httpClient.post( + '/out-province-carcasses-buyer/', + data: body.toJson()..removeWhere((key, value) => value == null), + headers: {'Authorization': 'Bearer $token'}, + ); + } } diff --git a/packages/chicken/lib/presentation/pages/buys_out_of_province/logic.dart b/packages/chicken/lib/presentation/pages/buys_out_of_province/logic.dart index 0466222..2787f71 100644 --- a/packages/chicken/lib/presentation/pages/buys_out_of_province/logic.dart +++ b/packages/chicken/lib/presentation/pages/buys_out_of_province/logic.dart @@ -1,15 +1,200 @@ -import 'package:get/get.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:rasadyar_auth/data/utils/safe_call.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/out_of_province/logic.dart'; +import 'package:rasadyar_chicken/presentation/pages/root/logic.dart'; +import 'package:rasadyar_core/core.dart'; class BuysOutOfProvinceLogic extends GetxController { + RxBool isExpanded = false.obs; + RxBool isSubmitButtonEnabled = false.obs; + RxList isExpandedList = [].obs; + RxBool searchIsSelected = false.obs; + + //TODO add this to Di + ImagePicker imagePicker = ImagePicker(); + + Rx>> purchaseOutOfProvinceList = Resource>.loading().obs; + Rxn selectedProduct = Rxn(); + + RxList cites = [].obs; + Rxn selectedProvince = Rxn(); + Rxn selectedCity = Rxn(); + Rxn selectedImage = Rxn(); + RxnString _base64Image = RxnString(); + RxnString editImageUrl = RxnString(); + + RootLogic get rootLogic => Get.find(); + + OutOfProvinceLogic get outOfTheProvinceLogic => Get.find(); + + GlobalKey formKey = GlobalKey(); + TextEditingController sellerNameController = TextEditingController(); + TextEditingController sellerPhoneController = TextEditingController(); + TextEditingController carcassWeightController = TextEditingController(); + + Rx fromDateFilter = Jalali.now().obs; + Rx toDateFilter = Jalali.now().obs; + RxnString searchedValue = RxnString(); + @override void onReady() { - // TODO: implement onReady super.onReady(); + getStewardPurchaseOutOfProvince(); + selectedProvince.listen((p0) => getCites()); + tLog(selectedProduct.value); + outOfTheProvinceLogic.rolesProductsModel.listen((lists) { + selectedProduct.value = lists.first; + },); + tLog(selectedProduct.value); + setupListeners(); + debounce(searchedValue, (callback) => getStewardPurchaseOutOfProvince(), time: Duration(milliseconds: 2000)); + ever(searchIsSelected, (data) { + if (data == false) { + searchedValue.value = null; + } + }); } @override void onClose() { - // TODO: implement onClose + sellerNameController.dispose(); + sellerPhoneController.dispose(); + carcassWeightController.dispose(); + isExpandedList.clear(); + super.onClose(); } + + Future getStewardPurchaseOutOfProvince() async { + await safeCall( + call: () => rootLogic.chickenRepository.getStewardPurchasesOutSideOfTheProvince( + token: rootLogic.tokenService.accessToken.value!, + queryParameters: buildQueryParams( + pageSize: 10, + page: 1, + search: 'filter', + role: 'Steward', + value: searchedValue.value, + fromDate: fromDateFilter.value.toDateTime(), + toDate: toDateFilter.value.toDateTime(), + ), + ), + onSuccess: (res) { + if ((res?.count ?? 0) == 0) { + purchaseOutOfProvinceList.value = Resource>.empty(); + } else { + purchaseOutOfProvinceList.value = Resource>.success(res!.results!); + } + }, + ); + } + + Future 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 createStewardPurchaseOutOfProvince() async { + bool res = false; + 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; + }, + ); + return res; + } + + void resetSubmitForm() { + sellerNameController.clear(); + sellerPhoneController.clear(); + carcassWeightController.clear(); + selectedProvince.value = null; + selectedCity.value = null; + selectedProduct.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 = outOfTheProvinceLogic.rolesProductsModel.firstWhere( + (element) => element.key == item.product!.key, + ); + isSubmitButtonEnabled.value = true; + } + + Future deleteStewardPurchaseOutOfProvince(String key) async { + await safeCall( + call: () => rootLogic.chickenRepository.deleteStewardPurchasesOutSideOfTheProvince( + token: rootLogic.tokenService.accessToken.value!, + stewardFreeBarKey: key, + ), + ); + } } diff --git a/packages/chicken/lib/presentation/pages/buys_out_of_province/view.dart b/packages/chicken/lib/presentation/pages/buys_out_of_province/view.dart index d076df7..99b878e 100644 --- a/packages/chicken/lib/presentation/pages/buys_out_of_province/view.dart +++ b/packages/chicken/lib/presentation/pages/buys_out_of_province/view.dart @@ -1,13 +1,799 @@ + +import 'dart:io'; + +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:get/get.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_core/core.dart'; import 'logic.dart'; - class BuysOutOfProvincePage extends GetView { const BuysOutOfProvincePage({super.key}); + @override Widget build(BuildContext context) { - return Container(); + return Scaffold( + appBar: RAppBar( + titleTextStyle: AppFonts.yekan16Bold.copyWith(color: Colors.white), + centerTitle: true, + hasBack: true, + onBackPressed: () { + Get.back(id: 1); + + }, + leadingWidth: 155, + leading: Row( + mainAxisSize: MainAxisSize.min, + spacing: 6, + children: [ + Assets.vec.chickenSvg.svg( + width: 24, + height: 24, + colorFilter: const ColorFilter.mode(Colors.white, BlendMode.srcIn), + ), + Text('رصدطیور', style: AppFonts.yekan16Bold.copyWith(color: Colors.white)), + ], + ), + additionalActions: [ + GestureDetector( + onTap: () { + controller.searchIsSelected.value = !controller.searchIsSelected.value; + }, + child: Assets.vec.searchSvg.svg( + width: 24, + height: 24, + colorFilter: const ColorFilter.mode(Colors.white, BlendMode.srcIn), + ), + ), + SizedBox(width: 8), + GestureDetector( + onTap: () { + Get.bottomSheet(filterBottomSheet()); + }, + child: Assets.vec.filterOutlineSvg.svg( + width: 20, + height: 20, + colorFilter: const ColorFilter.mode(Colors.white, BlendMode.srcIn), + ), + ), + SizedBox(width: 8), + ], + ), + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + routePageWidget(), + _buildSearchWidget(), + Expanded(child: saleListWidget()), + ], + ), + floatingActionButton: RFab.add( + onPressed: () { + Get.bottomSheet(addPurchasedInformationBottomSheet(), isScrollControlled: true); + }, + ), + floatingActionButtonLocation: FloatingActionButtonLocation.startFloat, + ); + } + + Widget saleListWidget() { + return ObxValue((data) { + switch (data.value.status) { + case Status.initial: + case Status.loading: + return Center(child: CupertinoActivityIndicator()); + case Status.success: + return ListView.separated( + shrinkWrap: true, + physics: BouncingScrollPhysics(), + padding: EdgeInsets.fromLTRB(8, 8, 18, 80), + itemBuilder: (context, index) { + return ObxValue( + (expandList) => saleListItem(expandList: expandList, index: index, item: data.value.data![index]), + controller.isExpandedList, + ); + }, + separatorBuilder: (context, index) => SizedBox(height: 8), + itemCount: data.value.data?.length ?? 0, + ); + case Status.error: + return Center( + child: Text( + data.value.message ?? 'خطا در دریافت اطلاعات', + style: AppFonts.yekan16.copyWith(color: AppColor.error), + ), + ); + case Status.empty: + return emptyWidget(); + } + }, controller.purchaseOutOfProvinceList); + } + + Widget emptyWidget() { + return Center( + child: Text('داده ای دریافت نشد!', style: AppFonts.yekan16.copyWith(color: AppColor.darkGreyDarkHover)), + ); + } + + GestureDetector saleListItem({required RxList expandList, required int index, required StewardFreeBar item}) { + return GestureDetector( + onTap: () { + if (expandList.contains(index)) { + controller.isExpandedList.remove(index); + } else { + controller.isExpandedList.add(index); + } + }, + child: AnimatedSize( + duration: Duration(milliseconds: 400), + alignment: Alignment.center, + child: Stack( + clipBehavior: Clip.none, + alignment: Alignment.centerRight, + children: [ + AnimatedSize( + duration: Duration(milliseconds: 300), + child: Container( + width: Get.width - 30, + alignment: Alignment.center, + decoration: BoxDecoration( + color: Colors.transparent, + borderRadius: BorderRadius.circular(8), + border: Border.all(width: 0.5, color: AppColor.darkGreyNormal), + ), + child: AnimatedCrossFade( + alignment: Alignment.center, + firstChild: Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + SizedBox(width: 12), + Expanded( + flex: 2, + child: Text( + item.date?.formattedJalaliDate ?? 'N/A', + textAlign: TextAlign.center, + style: AppFonts.yekan14.copyWith(color: AppColor.bgDark), + ), + ), + Expanded( + flex: 3, + child: Text( + item.killHouseName ?? 'N/A', + textAlign: TextAlign.center, + style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal), + ), + ), + SizedBox(width: 8,), + Expanded( + flex: 2, + child: Column( + children: [ + Text( + '${item.weightOfCarcasses?.separatedByComma}kg', + textAlign: TextAlign.left, + style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal), + ), + + SizedBox(height: 2,), + Visibility( + visible: item.product?.name?.contains('مرغ گرم') ?? false, + child: Assets.vec.hotChickenSvg.svg(width: 24,height: 24, colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn))), + + ], + ), + ), + Expanded( + flex: 2, + child: Text( + '${item.province}\n${item.city}', + textAlign: TextAlign.center, + style: AppFonts.yekan12.copyWith(color: AppColor.bgDark), + ), + ), + Icon(CupertinoIcons.chevron_down, size: 12), + SizedBox(width: 8), + ], + ), + ), + secondChild: Container( + padding: EdgeInsets.fromLTRB(8, 12, 14, 12), + + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(8)), + child: Column( + spacing: 8, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + onTap: () { + controller.setEditData(item); + Get.bottomSheet( + addPurchasedInformationBottomSheet(true), + isScrollControlled: true, + ).whenComplete(() { + controller.resetSubmitForm(); + }); + }, + child: Assets.vec.editSvg.svg( + width: 20, + height: 20, + colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn), + ), + ), + + Text( + '${item.province}-${item.city}', + textAlign: TextAlign.center, + style: AppFonts.yekan16.copyWith(color: AppColor.greenDark), + ), + + GestureDetector( + onTap: () { + buildDeleteDialog( + onConfirm: () => controller.deleteStewardPurchaseOutOfProvince(item.key!), + ); + }, + child: Assets.vec.trashSvg.svg( + width: 20, + height: 20, + colorFilter: ColorFilter.mode(AppColor.error, BlendMode.srcIn), + ), + ), + ], + ), + Container( + height: 32, + padding: EdgeInsets.symmetric(horizontal: 4), + decoration: ShapeDecoration( + color: AppColor.blueLight, + shape: RoundedRectangleBorder( + side: BorderSide(width: 1, color: AppColor.blueLightHover), + borderRadius: BorderRadius.circular(8), + ), + ), + child: buildRow('تاریخ', item.date?.formattedJalaliDateYHMS ?? 'N/A'), + ), + + buildRow('مشخصات فروشنده', '${item.killHouseName} - ${item.killHouseMobile ?? 'N/A'}'), + buildRow('محصول', item.product?.name ??'N/A'), + buildRow('وزن خریداری شده', '${item.weightOfCarcasses?.separatedByComma} کیلوگرم'), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + onTap: () { + Get.bottomSheet( + BaseBottomSheet( + height: 400, + child: Column( + spacing: 16, + children: [ + Text( + 'بارنامه', + style: AppFonts.yekan16Bold.copyWith(color: AppColor.darkGreyDarkHover), + ), + + 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; + print('Loading progress: $loadingProgress'); + return CupertinoActivityIndicator(); + }, + ), + ], + ), + ), + ); + }, + child: Text('مشاهده بارنامه', style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal)), + ), + Icon(CupertinoIcons.chevron_up, size: 12), + ], + ), + ], + ), + ), + crossFadeState: expandList.contains(index) ? CrossFadeState.showSecond : CrossFadeState.showFirst, + duration: Duration(milliseconds: 300), + ), + ), + ), + Positioned( + right: -12, + child: Container( + width: index < 999 ? 24 : null, + height: index < 999 ? 24 : null, + padding: EdgeInsets.all(2), + decoration: BoxDecoration( + color: AppColor.greenLightHover, + borderRadius: BorderRadius.circular(4), + border: Border.all(width: 0.50, color: AppColor.greenDarkActive), + ), + alignment: Alignment.center, + child: Text((index + 1).toString(), style: AppFonts.yekan12.copyWith(color: Colors.black)), + ), + ), + ], + ), + ), + ); + } + + Row routePageWidget() { + return Row( + children: [ + SizedBox(width: 8), + RichText( + text: TextSpan( + style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal), + children: [ + WidgetSpan( + child: Row( + children: [ + Assets.vec.cubeSearchSvg.svg( + width: 24, + height: 24, + colorFilter: const ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn), + ), + SizedBox(width: 6), + ], + ), + ), + + TextSpan(text: 'خارج استان'), + TextSpan(text: '/'), + TextSpan(text: 'خرید'), + ], + ), + ), + ], + ); + } + + 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), + ), + ), + ], + ), + ); + } + + Widget addPurchasedInformationBottomSheet([bool isOnEdit = false]) { + return BaseBottomSheet( + child: SingleChildScrollView( + child: Form( + key: controller.formKey, + child: Column( + spacing: 16, + children: [ + Text( + isOnEdit ? 'ویرایش اطلاعات خرید' : 'ثبت اطلاعات خرید', + style: AppFonts.yekan16Bold.copyWith(color: AppColor.darkGreyDarkHover), + ), + _productTypeWidget(), + RTextField( + controller: controller.sellerNameController, + label: 'نام فروشنده', + borderColor: AppColor.darkGreyLight, + ), + RTextField( + controller: controller.sellerPhoneController, + label: 'تلفن فروشنده', + keyboardType: TextInputType.phone, + borderColor: AppColor.darkGreyLight, + maxLength: 11, + validator: (value) { + if (value == null || value.isEmpty) { + return 'لطفاً شماره موبایل را وارد کنید'; + } + // حذف کاماها برای اعتبارسنجی + String cleaned = value.replaceAll(',', ''); + if (cleaned.length != 11) { + return 'شماره موبایل باید ۱۱ رقم باشد'; + } + if (!cleaned.startsWith('09')) { + return 'شماره موبایل باید با 09 شروع شود'; + } + return null; + }, + ), + _provinceWidget(), + _cityWidget(), + RTextField( + controller: controller.carcassWeightController, + label: 'وزن لاشه', + keyboardType: TextInputType.number, + borderColor: AppColor.darkGreyLight, + inputFormatters: [FilteringTextInputFormatter.digitsOnly, SeparatorInputFormatter()], + ), + _imageCarcasesWidget(isOnEdit), + submitButtonWidget(isOnEdit), + SizedBox(), + ], + ), + ), + ), + ); + } + + Widget submitButtonWidget(bool isOnEdit) { + return ObxValue((data) { + return RElevated( + text: isOnEdit ? 'ویرایش' : 'ثبت خرید', + onPressed: data.value + ? () async { + var res = await controller.createStewardPurchaseOutOfProvince(); + if (res) { + Get.back(); + } + } + : null, + height: 40, + ); + }, controller.isSubmitButtonEnabled); + } + + Widget _productTypeWidget() { + tLog(controller.outOfTheProvinceLogic.rolesProductsModel); + iLog(controller.selectedProduct.value); + return Obx(() { + return OverlayDropdownWidget( + items: controller.outOfTheProvinceLogic.rolesProductsModel, + onChanged: (value) { + controller.selectedProduct.value = value; + }, + selectedItem: controller.selectedProduct.value, + initialValue: controller.selectedProduct.value, + itemBuilder: (item) => Text(item.name ?? 'بدون نام'), + labelBuilder: (item) => Text(item?.name ?? 'انتخاب محصول'), + ); + }); + } + + Widget _provinceWidget() { + return Obx(() { + return OverlayDropdownWidget( + 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( + 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); + } + + SizedBox _imageCarcasesWidget(bool isOnEdit) { + return SizedBox( + width: Get.width, + height: 250, + child: Card( + color: Colors.white, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + Expanded( + child: ObxValue((data) { + return Container( + width: Get.width, + decoration: BoxDecoration(color: AppColor.lightGreyNormal, borderRadius: BorderRadius.circular(8)), + child: Center( + child: isOnEdit + ? Image.network(controller.editImageUrl.value ?? '') + : data.value == null + ? Assets.images.placeHolder.image(height: 150, width: 200) + : Image.file(File(data.value!.path), fit: BoxFit.cover), + ), + ); + }, controller.selectedImage), + ), + SizedBox(height: 15), + Container( + width: Get.width, + height: 40, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text('تصویر بار', style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal)), + Spacer(), + GestureDetector( + onTap: () async { + controller.selectedImage.value = await controller.imagePicker.pickImage( + source: ImageSource.camera, + imageQuality: 60, + maxWidth: 1080, + maxHeight: 720, + ); + }, + child: Container( + decoration: ShapeDecoration( + color: AppColor.blueNormal, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + ), + padding: EdgeInsetsGeometry.symmetric(horizontal: 6, vertical: 4), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text('دوربین', style: AppFonts.yekan14.copyWith(color: Colors.white)), + SizedBox(width: 8), + Icon(CupertinoIcons.camera, color: Colors.white), + ], + ), + ), + ), + SizedBox(width: 16), + GestureDetector( + onTap: () async { + controller.selectedImage.value = await controller.imagePicker.pickImage( + source: ImageSource.gallery, + imageQuality: 60, + maxWidth: 1080, + maxHeight: 720, + ); + }, + child: Container( + decoration: ShapeDecoration( + color: AppColor.blueNormal, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + ), + padding: EdgeInsetsGeometry.symmetric(horizontal: 6, vertical: 4), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text('گالری', style: AppFonts.yekan14.copyWith(color: Colors.white)), + SizedBox(width: 8), + Icon(CupertinoIcons.photo, color: Colors.white), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ); + } + + Future buildDeleteDialog({required Future Function() onConfirm}) async { + await Get.defaultDialog( + title: 'حذف خرید', + middleText: 'آیا از حذف این خرید مطمئن هستید؟', + confirm: ElevatedButton( + style: ElevatedButton.styleFrom(backgroundColor: AppColor.error, foregroundColor: Colors.white), + onPressed: () async { + await onConfirm(); + Get.back(); + }, + child: Text('بله'), + ), + cancel: ElevatedButton( + onPressed: () { + Get.back(); + }, + child: Text('خیر'), + ), + ).whenComplete(() => controller.getStewardPurchaseOutOfProvince()); + } + + Widget filterBottomSheet() { + return BaseBottomSheet( + height: 250, + child: Column( + spacing: 16, + children: [ + Text('فیلترها', style: AppFonts.yekan16Bold.copyWith(color: AppColor.darkGreyDarkHover)), + 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, + ), + ), + ], + ), + SizedBox(height: 2), + RElevated( + text: 'اعمال فیلتر', + onPressed: () { + controller.getStewardPurchaseOutOfProvince(); + Get.back(); + }, + height: 40, + ), + SizedBox(height: 16), + ], + ), + ); + } + + GestureDetector timeFilterWidget({ + isFrom = true, + required Rx 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(), + textAlign: TextAlign.center, + style: AppFonts.yekan16.copyWith(color: AppColor.lightGreyNormalActive), + ); + }, date), + ), + ], + ), + ), + ); + } + + Container modalDatePicker(ValueChanged onDateSelected) { + Jalali? tempPickedDate; + return Container( + height: 250, + color: Colors.white, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + 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; + }, + ), + ), + ), + ], + ), + ); + } + + ObxValue _buildSearchWidget() { + return ObxValue((data) { + return AnimatedContainer( + duration: Duration(milliseconds: 300), + padding: EdgeInsets.only(top: 5), + curve: Curves.easeInOut, + height: data.value ? 50 : 0, + child: Visibility( + visible: data.value, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: RTextField( + suffixIcon: Padding( + padding: const EdgeInsets.all(12.0), + child: Assets.vec.searchSvg.svg( + width: 10, + height: 10, + colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn), + ), + ), + hintText: 'جستجو', + onChanged: (value) { + controller.searchedValue.value = value; + }, + controller: TextEditingController(), + ), + ), + ), + ); + }, controller.searchIsSelected); } } diff --git a/packages/chicken/lib/presentation/pages/out_of_province/view.dart b/packages/chicken/lib/presentation/pages/out_of_province/view.dart index 4315ca2..88e9fd3 100644 --- a/packages/chicken/lib/presentation/pages/out_of_province/view.dart +++ b/packages/chicken/lib/presentation/pages/out_of_province/view.dart @@ -41,20 +41,23 @@ class OutOfProvincePage extends GetView { children: [ Expanded( child: _typeOuterInfoCard( - title: 'خرید خارج استان', + title: 'خرید', iconPath: Assets.vec.cubeBottomRotationSvg.path, foregroundColor: AppColor.blueNormal, onTap: () { - Get.toNamed(ChickenRoutes.salesOutOfProvince,id:1); + Get.toNamed(ChickenRoutes.buysOutOfProvince, id: 1); }, ), ), Expanded( child: _typeOuterInfoCard( - title: 'فروش خارج استان', + title: 'فروش', iconPath: Assets.vec.cubeTopRotationSvg.path, foregroundColor: AppColor.greenDark, - onTap: () {}, + onTap: () { + iLog('فروش'); + Get.toNamed(ChickenRoutes.salesOutOfProvince, id: 1); + }, ), ), ], diff --git a/packages/chicken/lib/presentation/pages/sales_out_of_province/logic.dart b/packages/chicken/lib/presentation/pages/sales_out_of_province/logic.dart index c22cda4..a3f15a0 100644 --- a/packages/chicken/lib/presentation/pages/sales_out_of_province/logic.dart +++ b/packages/chicken/lib/presentation/pages/sales_out_of_province/logic.dart @@ -1,14 +1,16 @@ import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; import 'package:rasadyar_auth/data/utils/safe_call.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/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_bar/steward_free_bar.dart'; import 'package:rasadyar_chicken/presentation/pages/out_of_province/logic.dart'; import 'package:rasadyar_chicken/presentation/pages/root/logic.dart'; import 'package:rasadyar_core/core.dart'; class SalesOutOfProvinceLogic extends GetxController { + RxInt currentIndex = 0.obs; + RxBool isExpanded = false.obs; RxBool isSubmitButtonEnabled = false.obs; RxList isExpandedList = [].obs; @@ -17,24 +19,23 @@ class SalesOutOfProvinceLogic extends GetxController { //TODO add this to Di ImagePicker imagePicker = ImagePicker(); - Rx>> purchaseOutOfProvinceList = Resource>.loading().obs; + Rx>> buyerList = Resource>.loading().obs; + Rxn selectedProduct = Rxn(); RxList cites = [].obs; Rxn selectedProvince = Rxn(); Rxn selectedCity = Rxn(); - Rxn selectedImage = Rxn(); - RxnString _base64Image = RxnString(); - RxnString editImageUrl = RxnString(); RootLogic get rootLogic => Get.find(); OutOfProvinceLogic get outOfTheProvinceLogic => Get.find(); GlobalKey formKey = GlobalKey(); - TextEditingController sellerNameController = TextEditingController(); - TextEditingController sellerPhoneController = TextEditingController(); - TextEditingController carcassWeightController = TextEditingController(); + TextEditingController buyerNameController = TextEditingController(); + TextEditingController buyerLastNameController = TextEditingController(); + TextEditingController buyerPhoneController = TextEditingController(); + TextEditingController buyerUnitNameController = TextEditingController(); Rx fromDateFilter = Jalali.now().obs; Rx toDateFilter = Jalali.now().obs; @@ -43,15 +44,14 @@ class SalesOutOfProvinceLogic extends GetxController { @override void onReady() { super.onReady(); - getStewardPurchaseOutOfProvince(); + getOutProvinceCarcassesBuyer(); selectedProvince.listen((p0) => getCites()); tLog(selectedProduct.value); - outOfTheProvinceLogic.rolesProductsModel.listen((lists) { - selectedProduct.value = lists.first; - },); - tLog(selectedProduct.value); + outOfTheProvinceLogic.rolesProductsModel.listen((lists) { + selectedProduct.value = lists.first; + }); setupListeners(); - debounce(searchedValue, (callback) => getStewardPurchaseOutOfProvince(), time: Duration(milliseconds: 2000)); + debounce(searchedValue, (callback) => getOutProvinceCarcassesBuyer(), time: Duration(milliseconds: 2000)); ever(searchIsSelected, (data) { if (data == false) { searchedValue.value = null; @@ -61,33 +61,35 @@ class SalesOutOfProvinceLogic extends GetxController { @override void onClose() { - sellerNameController.dispose(); - sellerPhoneController.dispose(); - carcassWeightController.dispose(); + buyerNameController.dispose(); + buyerLastNameController.dispose(); + buyerPhoneController.dispose(); + buyerUnitNameController.dispose(); + selectedCity.value = null; + selectedProvince.value = null; isExpandedList.clear(); super.onClose(); } - Future getStewardPurchaseOutOfProvince() async { + Future getOutProvinceCarcassesBuyer() async { await safeCall( - call: () => rootLogic.chickenRepository.getStewardPurchasesOutSideOfTheProvince( + call: () => rootLogic.chickenRepository.getOutProvinceCarcassesBuyer( token: rootLogic.tokenService.accessToken.value!, queryParameters: buildQueryParams( pageSize: 10, page: 1, + state: 'buyer-list', search: 'filter', role: 'Steward', - value: searchedValue.value, - fromDate: fromDateFilter.value.toDateTime(), - toDate: toDateFilter.value.toDateTime(), + value: searchedValue.value ?? '', ), ), onSuccess: (res) { if ((res?.count ?? 0) == 0) { - purchaseOutOfProvinceList.value = Resource>.empty(); + buyerList.value = Resource>.empty(); } else { - purchaseOutOfProvinceList.value = Resource>.success(res!.results!); + buyerList.value = Resource>.success(res!.results!); } }, ); @@ -105,56 +107,50 @@ class SalesOutOfProvinceLogic extends GetxController { } void setupListeners() { - sellerNameController.addListener(checkFormValid); - sellerPhoneController.addListener(checkFormValid); - carcassWeightController.addListener(checkFormValid); + buyerNameController.addListener(checkFormValid); + buyerLastNameController.addListener(checkFormValid); + buyerPhoneController.addListener(checkFormValid); + buyerUnitNameController.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 && + buyerNameController.text.isNotEmpty && + buyerLastNameController.text.isNotEmpty && + buyerPhoneController.text.isNotEmpty && + buyerUnitNameController.text.isNotEmpty && selectedProvince.value != null && selectedCity.value != null && - selectedProduct.value != null && - selectedImage.value != null; + selectedProduct.value != null; } - Future createStewardPurchaseOutOfProvince() async { + Future createBuyer() async { bool res = false; if (!(formKey.currentState?.validate() ?? false)) { return res; } await safeCall( call: () async { - CreateStewardFreeBar createStewardFreeBar = CreateStewardFreeBar( - productKey: selectedProduct.value!.key, - killHouseName: sellerNameController.text, - killHouseMobile: sellerPhoneController.text, + OutProvinceCarcassesBuyer buyer = OutProvinceCarcassesBuyer( province: selectedProvince.value!.name, city: selectedCity.value!.name, - weightOfCarcasses: int.parse(carcassWeightController.text.clearComma), - barImage: _base64Image.value, - date: DateTime.now().formattedYHMS, + firstName: buyerNameController.text, + lastName: buyerLastNameController.text, + unitName: buyerUnitNameController.text, + mobile: buyerPhoneController.text, + role: 'Steward', ); - final res = await rootLogic.chickenRepository.createStewardPurchasesOutSideOfTheProvince( + final res = await rootLogic.chickenRepository.createOutProvinceCarcassesBuyer( token: rootLogic.tokenService.accessToken.value!, - body: createStewardFreeBar, + body: buyer, ); }, onSuccess: (result) { - getStewardPurchaseOutOfProvince(); + getOutProvinceCarcassesBuyer(); resetSubmitForm(); res = true; }, @@ -163,29 +159,24 @@ class SalesOutOfProvinceLogic extends GetxController { } void resetSubmitForm() { - sellerNameController.clear(); - sellerPhoneController.clear(); - carcassWeightController.clear(); + buyerNameController.clear(); + buyerLastNameController.clear(); + buyerPhoneController.clear(); + buyerUnitNameController.clear(); selectedProvince.value = null; selectedCity.value = null; selectedProduct.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() ?? ''; + void setEditData(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); - selectedProduct.value = outOfTheProvinceLogic.rolesProductsModel.firstWhere( - (element) => element.key == item.product!.key, - ); isSubmitButtonEnabled.value = true; } diff --git a/packages/chicken/lib/presentation/pages/sales_out_of_province/view.dart b/packages/chicken/lib/presentation/pages/sales_out_of_province/view.dart index d165e09..79a07a6 100644 --- a/packages/chicken/lib/presentation/pages/sales_out_of_province/view.dart +++ b/packages/chicken/lib/presentation/pages/sales_out_of_province/view.dart @@ -1,11 +1,11 @@ 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/pages/sales_out_of_province/widgets/buyers_page.dart'; import 'package:rasadyar_core/core.dart'; import 'logic.dart'; @@ -15,70 +15,106 @@ class SalesOutOfProvincePage extends GetView { @override Widget build(BuildContext context) { - return Scaffold( - appBar: RAppBar( - titleTextStyle: AppFonts.yekan16Bold.copyWith(color: Colors.white), - centerTitle: true, - hasBack: true, - onBackPressed: () { - Get.back(id: 1); + return ObxValue((index) { + return DefaultTabController( + length: 2, + initialIndex: index.value, + child: Scaffold( + appBar: RAppBar( + titleTextStyle: AppFonts.yekan16Bold.copyWith(color: Colors.white), + centerTitle: true, + hasBack: true, + onBackPressed: () { + Get.back(id: 1); + }, + leadingWidth: 155, + leading: Row( + mainAxisSize: MainAxisSize.min, + spacing: 6, + children: [ + Assets.vec.chickenSvg.svg( + width: 24, + height: 24, + colorFilter: const ColorFilter.mode(Colors.white, BlendMode.srcIn), + ), + Text('رصدطیور', style: AppFonts.yekan16Bold.copyWith(color: Colors.white)), + ], + ), + additionalActions: [ + GestureDetector( + onTap: () { + controller.searchIsSelected.value = !controller.searchIsSelected.value; + }, + child: Assets.vec.searchSvg.svg( + width: 24, + height: 24, + colorFilter: const ColorFilter.mode(Colors.white, BlendMode.srcIn), + ), + ), + SizedBox(width: 8), + GestureDetector( + onTap: () { + // Get.bottomSheet(filterBottomSheet()); + }, + child: Assets.vec.filterOutlineSvg.svg( + width: 20, + height: 20, + colorFilter: const ColorFilter.mode(Colors.white, BlendMode.srcIn), + ), + ), + SizedBox(width: 8), + ], + ), + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + routePageWidget(), + Container( + child: TabBar( + tabs: [ + Tab(text: 'فروش' ), + Tab(text: 'خریداران'), + ], + onTap: (value) { + controller.currentIndex.value= value; + }, + labelColor: AppColor.blueNormal, + unselectedLabelColor: AppColor.mediumGreyDarkHover, + indicatorColor: AppColor.blueNormal, + indicatorSize: TabBarIndicatorSize.tab, + indicator: BoxDecoration( + color: AppColor.blueLight, + border: Border( + bottom: BorderSide( + color:AppColor.blueNormal, + ), + ), + ), + ), + ), + Expanded( + child: TabBarView( + children: [ + salePage(), + BuyersPage() + ], + ), + ), + ], + ), - }, - leadingWidth: 155, - leading: Row( - mainAxisSize: MainAxisSize.min, - spacing: 6, - children: [ - Assets.vec.chickenSvg.svg( - width: 24, - height: 24, - colorFilter: const ColorFilter.mode(Colors.white, BlendMode.srcIn), - ), - Text('رصدطیور', style: AppFonts.yekan16Bold.copyWith(color: Colors.white)), - ], ), - additionalActions: [ - GestureDetector( - onTap: () { - controller.searchIsSelected.value = !controller.searchIsSelected.value; - }, - child: Assets.vec.searchSvg.svg( - width: 24, - height: 24, - colorFilter: const ColorFilter.mode(Colors.white, BlendMode.srcIn), - ), - ), - SizedBox(width: 8), - GestureDetector( - onTap: () { - Get.bottomSheet(filterBottomSheet()); - }, - child: Assets.vec.filterOutlineSvg.svg( - width: 20, - height: 20, - colorFilter: const ColorFilter.mode(Colors.white, BlendMode.srcIn), - ), - ), - SizedBox(width: 8), - ], - ), - body: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - routePageWidget(), - _buildSearchWidget(), - Expanded(child: saleListWidget()), - ], - ), - floatingActionButton: RFab.add( - onPressed: () { - Get.bottomSheet(addPurchasedInformationBottomSheet(), isScrollControlled: true); - }, - ), - floatingActionButtonLocation: FloatingActionButtonLocation.startFloat, - ); + ); + }, controller.currentIndex); } + + Widget salePage(){ + return SizedBox(); + } + +/* + Widget saleListWidget() { return ObxValue((data) { switch (data.value.status) { @@ -92,7 +128,7 @@ class SalesOutOfProvincePage extends GetView { padding: EdgeInsets.fromLTRB(8, 8, 18, 80), itemBuilder: (context, index) { return ObxValue( - (expandList) => saleListItem(expandList: expandList, index: index, item: data.value.data![index]), + (expandList) => saleListItem(expandList: expandList, index: index, item: data.value.data![index]), controller.isExpandedList, ); }, @@ -111,14 +147,11 @@ class SalesOutOfProvincePage extends GetView { } }, controller.purchaseOutOfProvinceList); } +*/ - Widget emptyWidget() { - return Center( - child: Text('داده ای دریافت نشد!', style: AppFonts.yekan16.copyWith(color: AppColor.darkGreyDarkHover)), - ); - } - GestureDetector saleListItem({required RxList expandList, required int index, required StewardFreeBar item}) { + +/* GestureDetector saleListItem({required RxList expandList, required int index, required StewardFreeBar item}) { return GestureDetector( onTap: () { if (expandList.contains(index)) { @@ -182,7 +215,9 @@ class SalesOutOfProvincePage extends GetView { SizedBox(height: 2,), Visibility( visible: item.product?.name?.contains('مرغ گرم') ?? false, - child: Assets.vec.hotChickenSvg.svg(width: 24,height: 24, colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn))), + child: Assets.vec.hotChickenSvg.svg(width: 24, + height: 24, + colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn))), ], ), @@ -235,9 +270,9 @@ class SalesOutOfProvincePage extends GetView { GestureDetector( onTap: () { - buildDeleteDialog( - onConfirm: () => controller.deleteStewardPurchaseOutOfProvince(item.key!), - ); + // buildDeleteDialog( + // onConfirm: () => controller.deleteStewardPurchaseOutOfProvince(item.key!), + // ); }, child: Assets.vec.trashSvg.svg( width: 20, @@ -261,7 +296,8 @@ class SalesOutOfProvincePage extends GetView { ), buildRow('مشخصات فروشنده', '${item.killHouseName} - ${item.killHouseMobile ?? 'N/A'}'), - buildRow('وزن خریداری شده', '${item.weightOfCarcasses?.toInt()} کیلوگرم'), + buildRow('محصول', item.product?.name ?? 'N/A'), + buildRow('وزن خریداری شده', '${item.weightOfCarcasses?.separatedByComma} کیلوگرم'), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -297,7 +333,8 @@ class SalesOutOfProvincePage extends GetView { ), ); }, - child: Text('مشاهده بارنامه', style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal)), + child: Text( + 'مشاهده بارنامه', style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal)), ), Icon(CupertinoIcons.chevron_up, size: 12), ], @@ -329,7 +366,7 @@ class SalesOutOfProvincePage extends GetView { ), ), ); - } + }*/ Row routePageWidget() { return Row( @@ -354,7 +391,7 @@ class SalesOutOfProvincePage extends GetView { TextSpan(text: 'خارج استان'), TextSpan(text: '/'), - TextSpan(text: 'خرید'), + TextSpan(text: 'فروش'), ], ), ), @@ -389,37 +426,8 @@ class SalesOutOfProvincePage extends GetView { ), ); } - Widget buildRow2(String title, String value) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 4.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - title, - textAlign: TextAlign.right, - style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDarkHover), - ), - SizedBox(width: 8,), - Expanded( - child: Container - ( - color: Colors.green, - child: Text( - value, - textAlign: TextAlign.left, - style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDarkHover), - ), - ), - ), - ], - ), - ); - } - - - Widget addPurchasedInformationBottomSheet([bool isOnEdit = false]) { +/* Widget addPurchasedInformationBottomSheet([bool isOnEdit = false]) { return BaseBottomSheet( child: SingleChildScrollView( child: Form( @@ -475,24 +483,24 @@ class SalesOutOfProvincePage extends GetView { ), ), ); - } + }*/ - Widget submitButtonWidget(bool isOnEdit) { +/* Widget submitButtonWidget(bool isOnEdit) { return ObxValue((data) { return RElevated( text: isOnEdit ? 'ویرایش' : 'ثبت خرید', onPressed: data.value ? () async { - var res = await controller.createStewardPurchaseOutOfProvince(); - if (res) { - Get.back(); - } - } + var res = await controller.createStewardPurchaseOutOfProvince(); + if (res) { + Get.back(); + } + } : null, height: 40, ); }, controller.isSubmitButtonEnabled); - } + }*/ Widget _productTypeWidget() { tLog(controller.outOfTheProvinceLogic.rolesProductsModel); @@ -541,7 +549,7 @@ class SalesOutOfProvincePage extends GetView { }, controller.cites); } - SizedBox _imageCarcasesWidget(bool isOnEdit) { +/* SizedBox _imageCarcasesWidget(bool isOnEdit) { return SizedBox( width: Get.width, height: 250, @@ -634,9 +642,9 @@ class SalesOutOfProvincePage extends GetView { ), ), ); - } + }*/ - Future buildDeleteDialog({required Future Function() onConfirm}) async { +/* Future buildDeleteDialog({required Future Function() onConfirm}) async { await Get.defaultDialog( title: 'حذف خرید', middleText: 'آیا از حذف این خرید مطمئن هستید؟', @@ -695,7 +703,7 @@ class SalesOutOfProvincePage extends GetView { ], ), ); - } + }*/ GestureDetector timeFilterWidget({ isFrom = true, diff --git a/packages/chicken/lib/presentation/pages/sales_out_of_province/widgets/buyers_page.dart b/packages/chicken/lib/presentation/pages/sales_out_of_province/widgets/buyers_page.dart new file mode 100644 index 0000000..c61749a --- /dev/null +++ b/packages/chicken/lib/presentation/pages/sales_out_of_province/widgets/buyers_page.dart @@ -0,0 +1,346 @@ +import 'package:flutter/cupertino.dart'; +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/sales_out_of_province/logic.dart'; +import 'package:rasadyar_core/core.dart'; + +import 'empty_widget.dart'; + +class BuyersPage extends GetView { + const BuyersPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: buyerListWidget(), + floatingActionButton: RFab.add( + onPressed: () { + Get.bottomSheet(addOrEditBuyerBottomSheet(), isScrollControlled: true); + }, + ), + floatingActionButtonLocation: FloatingActionButtonLocation.startFloat, + ); + } + + Widget buyerListWidget() { + return ObxValue((data) { + switch (data.value.status) { + case Status.initial: + case Status.loading: + return Center(child: CupertinoActivityIndicator()); + case Status.success: + return ListView.separated( + shrinkWrap: true, + physics: BouncingScrollPhysics(), + padding: EdgeInsets.fromLTRB(8, 8, 18, 80), + itemBuilder: (context, index) { + return ObxValue( + (expandList) => buyerListItem(expandList: expandList, index: index, item: data.value.data![index]), + controller.isExpandedList, + ); + }, + separatorBuilder: (context, index) => SizedBox(height: 8), + itemCount: data.value.data?.length ?? 0, + ); + case Status.error: + return Center( + child: Text( + data.value.message ?? 'خطا در دریافت اطلاعات', + style: AppFonts.yekan16.copyWith(color: AppColor.error), + ), + ); + case Status.empty: + return emptyWidget(); + } + }, controller.buyerList); + } + + GestureDetector buyerListItem({ + required RxList expandList, + required int index, + required OutProvinceCarcassesBuyer item, + }) { + return GestureDetector( + onTap: () { + if (expandList.contains(index)) { + controller.isExpandedList.remove(index); + } else { + controller.isExpandedList.add(index); + } + }, + child: AnimatedSize( + duration: Duration(milliseconds: 400), + alignment: Alignment.center, + child: Stack( + clipBehavior: Clip.none, + alignment: Alignment.centerRight, + children: [ + AnimatedSize( + duration: Duration(milliseconds: 300), + child: Container( + width: Get.width - 30, + alignment: Alignment.center, + decoration: BoxDecoration( + color: Colors.transparent, + borderRadius: BorderRadius.circular(8), + border: Border.all(width: 0.5, color: AppColor.darkGreyNormal), + ), + child: AnimatedCrossFade( + alignment: Alignment.center, + firstChild: Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + SizedBox(width: 12), + + Expanded( + flex: 2, + child: Column( + 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: 8), + Expanded( + flex: 2, + child: Text( + '${item.unitName}', + textAlign: TextAlign.center, + style: AppFonts.yekan12.copyWith(color: AppColor.bgDark), + ), + ), + Expanded( + flex: 1, + child: Text( + '${item.buyer?.province}\n${item.buyer?.city}', + textAlign: TextAlign.center, + style: AppFonts.yekan12.copyWith(color: AppColor.darkGreyDark), + ), + ), + + Icon(CupertinoIcons.chevron_down, size: 12), + SizedBox(width: 8), + ], + ), + ), + secondChild: Container( + padding: EdgeInsets.fromLTRB(8, 12, 14, 12), + + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(8)), + child: Column( + spacing: 8, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + onTap: () { + controller.setEditData(item); + Get.bottomSheet(addOrEditBuyerBottomSheet(true), isScrollControlled: true).whenComplete( + () { + controller.resetSubmitForm(); + }, + ); + }, + child: Assets.vec.editSvg.svg( + width: 20, + height: 20, + colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn), + ), + ), + + Text( + '${item.province}-${item.city}', + textAlign: TextAlign.center, + style: AppFonts.yekan16.copyWith(color: AppColor.greenDark), + ), + + SizedBox(), + ], + ), + + buildRow('مشخصات خریدار', item.fullname ?? 'N/A'), + buildRow('نام واحد', item.unitName ?? 'N/A'), + buildRow('تعداد درخواست ها', '${item.requestsInfo?.numberOfRequests.separatedByComma}'), + buildRow('وزن', '${item.requestsInfo?.totalWeight.separatedByComma}'), + ], + ), + ), + crossFadeState: expandList.contains(index) ? CrossFadeState.showSecond : CrossFadeState.showFirst, + duration: Duration(milliseconds: 300), + ), + ), + ), + Positioned( + right: -12, + child: Container( + width: index < 999 ? 24 : null, + height: index < 999 ? 24 : null, + padding: EdgeInsets.all(2), + decoration: BoxDecoration( + color: AppColor.greenLightHover, + borderRadius: BorderRadius.circular(4), + border: Border.all(width: 0.50, color: AppColor.greenDarkActive), + ), + alignment: Alignment.center, + child: Text((index + 1).toString(), style: AppFonts.yekan12.copyWith(color: Colors.black)), + ), + ), + ], + ), + ), + ); + } + + 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), + ), + ), + ], + ), + ); + } + + Widget addOrEditBuyerBottomSheet([bool isOnEdit = false]) { + return BaseBottomSheet( + height: 600, + child: SingleChildScrollView( + child: Form( + key: controller.formKey, + child: Column( + spacing: 16, + children: [ + Text( + isOnEdit ? 'ویرایش خریدار' : 'افزودن خریدار', + style: AppFonts.yekan16Bold.copyWith(color: AppColor.darkGreyDarkHover), + ), + + RTextField( + controller: controller.buyerPhoneController, + label: 'تلفن خریدار', + keyboardType: TextInputType.phone, + borderColor: AppColor.darkGreyLight, + maxLength: 11, + validator: (value) { + if (value == null || value.isEmpty) { + return 'لطفاً شماره موبایل را وارد کنید'; + } + // حذف کاماها برای اعتبارسنجی + String cleaned = value.replaceAll(',', ''); + if (cleaned.length != 11) { + return 'شماره موبایل باید ۱۱ رقم باشد'; + } + if (!cleaned.startsWith('09')) { + return 'شماره موبایل باید با 09 شروع شود'; + } + return null; + }, + ), + RTextField( + controller: controller.buyerNameController, + label: 'نام خریدار', + borderColor: AppColor.darkGreyLight, + ), + RTextField( + controller: controller.buyerLastNameController, + label: 'نام خانوادگی خریدار', + borderColor: AppColor.darkGreyLight, + ), + + RTextField( + controller: controller.buyerUnitNameController, + label: 'نام واحد', + borderColor: AppColor.darkGreyLight, + ), + _provinceWidget(), + _cityWidget(), + submitButtonWidget(isOnEdit), + SizedBox(), + ], + ), + ), + ), + ); + } + + Widget submitButtonWidget(bool isOnEdit) { + return ObxValue((data) { + return RElevated( + text: isOnEdit ? 'ویرایش' : 'ثبت', + onPressed: data.value + ? () async { + var res = await controller.createBuyer(); + if (res) { + Get.back(); + } + } + : null, + height: 40, + ); + }, controller.isSubmitButtonEnabled); + } + + Widget _provinceWidget() { + return Obx(() { + return OverlayDropdownWidget( + 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( + 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); + } +} diff --git a/packages/chicken/lib/presentation/pages/sales_out_of_province/widgets/empty_widget.dart b/packages/chicken/lib/presentation/pages/sales_out_of_province/widgets/empty_widget.dart new file mode 100644 index 0000000..4c7c6a9 --- /dev/null +++ b/packages/chicken/lib/presentation/pages/sales_out_of_province/widgets/empty_widget.dart @@ -0,0 +1,8 @@ +import 'package:flutter/material.dart'; +import 'package:rasadyar_core/core.dart'; + +Widget emptyWidget() { + return Center( + child: Text('داده ای دریافت نشد!', style: AppFonts.yekan16.copyWith(color: AppColor.darkGreyDarkHover)), + ); +} diff --git a/packages/core/lib/utils/map_utils.dart b/packages/core/lib/utils/map_utils.dart index 943ab2a..39e63c4 100644 --- a/packages/core/lib/utils/map_utils.dart +++ b/packages/core/lib/utils/map_utils.dart @@ -9,13 +9,12 @@ Map buildQueryParams({ DateTime? fromDate, DateTime? toDate, String? role, + String? state, }) { final params = {}; - if (queryParams != null) { - params.addAll(queryParams); - } + if (fromDate != null) { params['date1'] = fromDate.formattedDashedGregorian; @@ -29,7 +28,7 @@ Map buildQueryParams({ params['search'] = search; } - if (value != null && value.isNotEmpty) { + if (value != null) { params['value'] = value; } @@ -41,9 +40,17 @@ Map buildQueryParams({ params['page_size'] = pageSize; } - if(role != null && role.isNotEmpty) { + if (role != null && role.isNotEmpty) { params['role'] = role; } + if (state != null && state.isNotEmpty) { + params['state'] = state; + } + + if (queryParams != null) { + params.addAll(queryParams); + } + return params; }