feat : home page

This commit is contained in:
2025-09-06 17:22:19 +03:30
parent 34609d22a1
commit d2e74e6627
18 changed files with 2084 additions and 2 deletions

View File

@@ -0,0 +1,8 @@
import 'package:rasadyar_chicken/data/models/response/poultry_science/home_poultry_science/home_poultry_science_model.dart';
abstract class PoultryScienceRemoteDatasource {
Future<HomePoultryScienceModel?> getHomePoultryScience({
required String token,
required String type,
});
}

View File

@@ -0,0 +1,25 @@
import 'package:rasadyar_chicken/data/models/response/poultry_science/home_poultry_science/home_poultry_science_model.dart';
import 'package:rasadyar_core/core.dart';
import 'poultry_science_remote.dart';
class PoultryScienceRemoteDatasourceImp implements PoultryScienceRemoteDatasource {
final DioRemote _httpClient;
PoultryScienceRemoteDatasourceImp(this._httpClient);
@override
Future<HomePoultryScienceModel?> getHomePoultryScience({
required String token,
required String type,
}) async {
var res = await _httpClient.get(
'poultry_and_hatching_for_poultry_science/',
queryParameters: {'type': type},
headers: {'Authorization': 'Bearer $token'},
fromJson: (json) => HomePoultryScienceModel.fromJson(json),
);
return res.data;
}
}

View File

@@ -6,10 +6,14 @@ import 'package:rasadyar_chicken/data/data_source/remote/auth/auth_remote.dart';
import 'package:rasadyar_chicken/data/data_source/remote/auth/auth_remote_imp.dart';
import 'package:rasadyar_chicken/data/data_source/remote/chicken/chicken_remote.dart';
import 'package:rasadyar_chicken/data/data_source/remote/chicken/chicken_remote_imp.dart';
import 'package:rasadyar_chicken/data/data_source/remote/poultry_science/poultry_science_remote.dart';
import 'package:rasadyar_chicken/data/data_source/remote/poultry_science/poultry_science_remote_imp.dart';
import 'package:rasadyar_chicken/data/repositories/auth/auth_repository.dart';
import 'package:rasadyar_chicken/data/repositories/auth/auth_repository_imp.dart';
import 'package:rasadyar_chicken/data/repositories/chicken/chicken_repository.dart';
import 'package:rasadyar_chicken/data/repositories/chicken/chicken_repository_imp.dart';
import 'package:rasadyar_chicken/data/repositories/poultry_science/poultry_science_repository.dart';
import 'package:rasadyar_chicken/data/repositories/poultry_science/poultry_science_repository_imp.dart';
import 'package:rasadyar_core/core.dart';
GetIt diChicken = GetIt.asNewInstance();
@@ -67,6 +71,14 @@ Future<void> setupChickenDI() async {
local: diChicken.get<ChickenLocalDataSource>(),
),
);
diChicken.registerLazySingleton<PoultryScienceRemoteDatasource>(
() => PoultryScienceRemoteDatasourceImp(diChicken.get<DioRemote>()),
);
diChicken.registerLazySingleton<PoultryScienceRepository>(
() => PoultryScienceRepositoryImp(diChicken.get<PoultryScienceRemoteDatasource>()),
);
}
Future<void> newSetupAuthDI(String newUrl) async {
@@ -117,6 +129,13 @@ Future<void> newSetupAuthDI(String newUrl) async {
local: diChicken.get<ChickenLocalDataSource>(),
),
);
await reRegister<PoultryScienceRemoteDatasource>(
() => PoultryScienceRemoteDatasourceImp(dioRemote),
);
await reRegister<PoultryScienceRepository>(
() => PoultryScienceRepositoryImp(diChicken.get<PoultryScienceRemoteDatasource>()),
);
}
Future<void> reRegister<T extends Object>(T Function() factory) async {

View File

@@ -8,7 +8,7 @@ part of 'widely_used_local_model.dart';
class WidelyUsedLocalModelAdapter extends TypeAdapter<WidelyUsedLocalModel> {
@override
final typeId = 2;
final typeId = 4;
@override
WidelyUsedLocalModel read(BinaryReader reader) {
@@ -45,7 +45,7 @@ class WidelyUsedLocalModelAdapter extends TypeAdapter<WidelyUsedLocalModel> {
class WidelyUsedLocalItemAdapter extends TypeAdapter<WidelyUsedLocalItem> {
@override
final typeId = 3;
final typeId = 5;
@override
WidelyUsedLocalItem read(BinaryReader reader) {

View File

@@ -0,0 +1,21 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'home_poultry_science_model.freezed.dart';
part 'home_poultry_science_model.g.dart';
@freezed
abstract class HomePoultryScienceModel with _$HomePoultryScienceModel {
const factory HomePoultryScienceModel({
int? farmCount,
int? hatchingCount,
int? hatchingQuantity,
int? hatchingLeftOver,
int? hatchingLosses,
int? hatchingKilledQuantity,
int? hatchingMaxAge,
int? hatchingMinAge,
}) = _HomePoultryScienceModel;
factory HomePoultryScienceModel.fromJson(Map<String, dynamic> json) =>
_$HomePoultryScienceModelFromJson(json);
}

View File

@@ -0,0 +1,298 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// 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 'home_poultry_science_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$HomePoultryScienceModel {
int? get farmCount; int? get hatchingCount; int? get hatchingQuantity; int? get hatchingLeftOver; int? get hatchingLosses; int? get hatchingKilledQuantity; int? get hatchingMaxAge; int? get hatchingMinAge;
/// Create a copy of HomePoultryScienceModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$HomePoultryScienceModelCopyWith<HomePoultryScienceModel> get copyWith => _$HomePoultryScienceModelCopyWithImpl<HomePoultryScienceModel>(this as HomePoultryScienceModel, _$identity);
/// Serializes this HomePoultryScienceModel to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is HomePoultryScienceModel&&(identical(other.farmCount, farmCount) || other.farmCount == farmCount)&&(identical(other.hatchingCount, hatchingCount) || other.hatchingCount == hatchingCount)&&(identical(other.hatchingQuantity, hatchingQuantity) || other.hatchingQuantity == hatchingQuantity)&&(identical(other.hatchingLeftOver, hatchingLeftOver) || other.hatchingLeftOver == hatchingLeftOver)&&(identical(other.hatchingLosses, hatchingLosses) || other.hatchingLosses == hatchingLosses)&&(identical(other.hatchingKilledQuantity, hatchingKilledQuantity) || other.hatchingKilledQuantity == hatchingKilledQuantity)&&(identical(other.hatchingMaxAge, hatchingMaxAge) || other.hatchingMaxAge == hatchingMaxAge)&&(identical(other.hatchingMinAge, hatchingMinAge) || other.hatchingMinAge == hatchingMinAge));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,farmCount,hatchingCount,hatchingQuantity,hatchingLeftOver,hatchingLosses,hatchingKilledQuantity,hatchingMaxAge,hatchingMinAge);
@override
String toString() {
return 'HomePoultryScienceModel(farmCount: $farmCount, hatchingCount: $hatchingCount, hatchingQuantity: $hatchingQuantity, hatchingLeftOver: $hatchingLeftOver, hatchingLosses: $hatchingLosses, hatchingKilledQuantity: $hatchingKilledQuantity, hatchingMaxAge: $hatchingMaxAge, hatchingMinAge: $hatchingMinAge)';
}
}
/// @nodoc
abstract mixin class $HomePoultryScienceModelCopyWith<$Res> {
factory $HomePoultryScienceModelCopyWith(HomePoultryScienceModel value, $Res Function(HomePoultryScienceModel) _then) = _$HomePoultryScienceModelCopyWithImpl;
@useResult
$Res call({
int? farmCount, int? hatchingCount, int? hatchingQuantity, int? hatchingLeftOver, int? hatchingLosses, int? hatchingKilledQuantity, int? hatchingMaxAge, int? hatchingMinAge
});
}
/// @nodoc
class _$HomePoultryScienceModelCopyWithImpl<$Res>
implements $HomePoultryScienceModelCopyWith<$Res> {
_$HomePoultryScienceModelCopyWithImpl(this._self, this._then);
final HomePoultryScienceModel _self;
final $Res Function(HomePoultryScienceModel) _then;
/// Create a copy of HomePoultryScienceModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? farmCount = freezed,Object? hatchingCount = freezed,Object? hatchingQuantity = freezed,Object? hatchingLeftOver = freezed,Object? hatchingLosses = freezed,Object? hatchingKilledQuantity = freezed,Object? hatchingMaxAge = freezed,Object? hatchingMinAge = freezed,}) {
return _then(_self.copyWith(
farmCount: freezed == farmCount ? _self.farmCount : farmCount // ignore: cast_nullable_to_non_nullable
as int?,hatchingCount: freezed == hatchingCount ? _self.hatchingCount : hatchingCount // ignore: cast_nullable_to_non_nullable
as int?,hatchingQuantity: freezed == hatchingQuantity ? _self.hatchingQuantity : hatchingQuantity // ignore: cast_nullable_to_non_nullable
as int?,hatchingLeftOver: freezed == hatchingLeftOver ? _self.hatchingLeftOver : hatchingLeftOver // ignore: cast_nullable_to_non_nullable
as int?,hatchingLosses: freezed == hatchingLosses ? _self.hatchingLosses : hatchingLosses // ignore: cast_nullable_to_non_nullable
as int?,hatchingKilledQuantity: freezed == hatchingKilledQuantity ? _self.hatchingKilledQuantity : hatchingKilledQuantity // ignore: cast_nullable_to_non_nullable
as int?,hatchingMaxAge: freezed == hatchingMaxAge ? _self.hatchingMaxAge : hatchingMaxAge // ignore: cast_nullable_to_non_nullable
as int?,hatchingMinAge: freezed == hatchingMinAge ? _self.hatchingMinAge : hatchingMinAge // ignore: cast_nullable_to_non_nullable
as int?,
));
}
}
/// Adds pattern-matching-related methods to [HomePoultryScienceModel].
extension HomePoultryScienceModelPatterns on HomePoultryScienceModel {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _HomePoultryScienceModel value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _HomePoultryScienceModel() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _HomePoultryScienceModel value) $default,){
final _that = this;
switch (_that) {
case _HomePoultryScienceModel():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _HomePoultryScienceModel value)? $default,){
final _that = this;
switch (_that) {
case _HomePoultryScienceModel() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( int? farmCount, int? hatchingCount, int? hatchingQuantity, int? hatchingLeftOver, int? hatchingLosses, int? hatchingKilledQuantity, int? hatchingMaxAge, int? hatchingMinAge)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _HomePoultryScienceModel() when $default != null:
return $default(_that.farmCount,_that.hatchingCount,_that.hatchingQuantity,_that.hatchingLeftOver,_that.hatchingLosses,_that.hatchingKilledQuantity,_that.hatchingMaxAge,_that.hatchingMinAge);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( int? farmCount, int? hatchingCount, int? hatchingQuantity, int? hatchingLeftOver, int? hatchingLosses, int? hatchingKilledQuantity, int? hatchingMaxAge, int? hatchingMinAge) $default,) {final _that = this;
switch (_that) {
case _HomePoultryScienceModel():
return $default(_that.farmCount,_that.hatchingCount,_that.hatchingQuantity,_that.hatchingLeftOver,_that.hatchingLosses,_that.hatchingKilledQuantity,_that.hatchingMaxAge,_that.hatchingMinAge);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( int? farmCount, int? hatchingCount, int? hatchingQuantity, int? hatchingLeftOver, int? hatchingLosses, int? hatchingKilledQuantity, int? hatchingMaxAge, int? hatchingMinAge)? $default,) {final _that = this;
switch (_that) {
case _HomePoultryScienceModel() when $default != null:
return $default(_that.farmCount,_that.hatchingCount,_that.hatchingQuantity,_that.hatchingLeftOver,_that.hatchingLosses,_that.hatchingKilledQuantity,_that.hatchingMaxAge,_that.hatchingMinAge);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _HomePoultryScienceModel implements HomePoultryScienceModel {
const _HomePoultryScienceModel({this.farmCount, this.hatchingCount, this.hatchingQuantity, this.hatchingLeftOver, this.hatchingLosses, this.hatchingKilledQuantity, this.hatchingMaxAge, this.hatchingMinAge});
factory _HomePoultryScienceModel.fromJson(Map<String, dynamic> json) => _$HomePoultryScienceModelFromJson(json);
@override final int? farmCount;
@override final int? hatchingCount;
@override final int? hatchingQuantity;
@override final int? hatchingLeftOver;
@override final int? hatchingLosses;
@override final int? hatchingKilledQuantity;
@override final int? hatchingMaxAge;
@override final int? hatchingMinAge;
/// Create a copy of HomePoultryScienceModel
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$HomePoultryScienceModelCopyWith<_HomePoultryScienceModel> get copyWith => __$HomePoultryScienceModelCopyWithImpl<_HomePoultryScienceModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$HomePoultryScienceModelToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _HomePoultryScienceModel&&(identical(other.farmCount, farmCount) || other.farmCount == farmCount)&&(identical(other.hatchingCount, hatchingCount) || other.hatchingCount == hatchingCount)&&(identical(other.hatchingQuantity, hatchingQuantity) || other.hatchingQuantity == hatchingQuantity)&&(identical(other.hatchingLeftOver, hatchingLeftOver) || other.hatchingLeftOver == hatchingLeftOver)&&(identical(other.hatchingLosses, hatchingLosses) || other.hatchingLosses == hatchingLosses)&&(identical(other.hatchingKilledQuantity, hatchingKilledQuantity) || other.hatchingKilledQuantity == hatchingKilledQuantity)&&(identical(other.hatchingMaxAge, hatchingMaxAge) || other.hatchingMaxAge == hatchingMaxAge)&&(identical(other.hatchingMinAge, hatchingMinAge) || other.hatchingMinAge == hatchingMinAge));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,farmCount,hatchingCount,hatchingQuantity,hatchingLeftOver,hatchingLosses,hatchingKilledQuantity,hatchingMaxAge,hatchingMinAge);
@override
String toString() {
return 'HomePoultryScienceModel(farmCount: $farmCount, hatchingCount: $hatchingCount, hatchingQuantity: $hatchingQuantity, hatchingLeftOver: $hatchingLeftOver, hatchingLosses: $hatchingLosses, hatchingKilledQuantity: $hatchingKilledQuantity, hatchingMaxAge: $hatchingMaxAge, hatchingMinAge: $hatchingMinAge)';
}
}
/// @nodoc
abstract mixin class _$HomePoultryScienceModelCopyWith<$Res> implements $HomePoultryScienceModelCopyWith<$Res> {
factory _$HomePoultryScienceModelCopyWith(_HomePoultryScienceModel value, $Res Function(_HomePoultryScienceModel) _then) = __$HomePoultryScienceModelCopyWithImpl;
@override @useResult
$Res call({
int? farmCount, int? hatchingCount, int? hatchingQuantity, int? hatchingLeftOver, int? hatchingLosses, int? hatchingKilledQuantity, int? hatchingMaxAge, int? hatchingMinAge
});
}
/// @nodoc
class __$HomePoultryScienceModelCopyWithImpl<$Res>
implements _$HomePoultryScienceModelCopyWith<$Res> {
__$HomePoultryScienceModelCopyWithImpl(this._self, this._then);
final _HomePoultryScienceModel _self;
final $Res Function(_HomePoultryScienceModel) _then;
/// Create a copy of HomePoultryScienceModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? farmCount = freezed,Object? hatchingCount = freezed,Object? hatchingQuantity = freezed,Object? hatchingLeftOver = freezed,Object? hatchingLosses = freezed,Object? hatchingKilledQuantity = freezed,Object? hatchingMaxAge = freezed,Object? hatchingMinAge = freezed,}) {
return _then(_HomePoultryScienceModel(
farmCount: freezed == farmCount ? _self.farmCount : farmCount // ignore: cast_nullable_to_non_nullable
as int?,hatchingCount: freezed == hatchingCount ? _self.hatchingCount : hatchingCount // ignore: cast_nullable_to_non_nullable
as int?,hatchingQuantity: freezed == hatchingQuantity ? _self.hatchingQuantity : hatchingQuantity // ignore: cast_nullable_to_non_nullable
as int?,hatchingLeftOver: freezed == hatchingLeftOver ? _self.hatchingLeftOver : hatchingLeftOver // ignore: cast_nullable_to_non_nullable
as int?,hatchingLosses: freezed == hatchingLosses ? _self.hatchingLosses : hatchingLosses // ignore: cast_nullable_to_non_nullable
as int?,hatchingKilledQuantity: freezed == hatchingKilledQuantity ? _self.hatchingKilledQuantity : hatchingKilledQuantity // ignore: cast_nullable_to_non_nullable
as int?,hatchingMaxAge: freezed == hatchingMaxAge ? _self.hatchingMaxAge : hatchingMaxAge // ignore: cast_nullable_to_non_nullable
as int?,hatchingMinAge: freezed == hatchingMinAge ? _self.hatchingMinAge : hatchingMinAge // ignore: cast_nullable_to_non_nullable
as int?,
));
}
}
// dart format on

View File

@@ -0,0 +1,33 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'home_poultry_science_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_HomePoultryScienceModel _$HomePoultryScienceModelFromJson(
Map<String, dynamic> json,
) => _HomePoultryScienceModel(
farmCount: (json['farm_count'] as num?)?.toInt(),
hatchingCount: (json['hatching_count'] as num?)?.toInt(),
hatchingQuantity: (json['hatching_quantity'] as num?)?.toInt(),
hatchingLeftOver: (json['hatching_left_over'] as num?)?.toInt(),
hatchingLosses: (json['hatching_losses'] as num?)?.toInt(),
hatchingKilledQuantity: (json['hatching_killed_quantity'] as num?)?.toInt(),
hatchingMaxAge: (json['hatching_max_age'] as num?)?.toInt(),
hatchingMinAge: (json['hatching_min_age'] as num?)?.toInt(),
);
Map<String, dynamic> _$HomePoultryScienceModelToJson(
_HomePoultryScienceModel instance,
) => <String, dynamic>{
'farm_count': instance.farmCount,
'hatching_count': instance.hatchingCount,
'hatching_quantity': instance.hatchingQuantity,
'hatching_left_over': instance.hatchingLeftOver,
'hatching_losses': instance.hatchingLosses,
'hatching_killed_quantity': instance.hatchingKilledQuantity,
'hatching_max_age': instance.hatchingMaxAge,
'hatching_min_age': instance.hatchingMinAge,
};

View File

@@ -27,6 +27,8 @@ import '../../models/request/create_steward_free_bar/create_steward_free_bar.dar
abstract class ChickenRepository {
//region Remote
//region Steward
Future<List<InventoryModel>?> getInventory({required String token, CancelToken? cancelToken});
Future<KillHouseDistributionInfo?> getKillHouseDistributionInfo({required String token});
@@ -156,6 +158,11 @@ abstract class ChickenRepository {
Future<SegmentationModel?> deleteSegmentation({required String token, required String key});
//endregion
//endregion
//region local

View File

@@ -0,0 +1,8 @@
import 'package:rasadyar_chicken/data/models/response/poultry_science/home_poultry_science/home_poultry_science_model.dart';
abstract class PoultryScienceRepository {
Future<HomePoultryScienceModel?> getHomePoultryScience({
required String token,
required String type,
});
}

View File

@@ -0,0 +1,17 @@
import 'package:rasadyar_chicken/data/data_source/remote/poultry_science/poultry_science_remote.dart';
import 'package:rasadyar_chicken/data/models/response/poultry_science/home_poultry_science/home_poultry_science_model.dart';
import 'poultry_science_repository.dart';
class PoultryScienceRepositoryImp implements PoultryScienceRepository {
final PoultryScienceRemoteDatasource datasource;
PoultryScienceRepositoryImp(this.datasource);
@override
Future<HomePoultryScienceModel?> getHomePoultryScience({
required String token,
required String type,
}) async => await datasource.getHomePoultryScience(token: token, type: type);
}

View File

@@ -0,0 +1,30 @@
import 'package:rasadyar_chicken/data/models/response/poultry_science/home_poultry_science/home_poultry_science_model.dart';
import 'package:rasadyar_chicken/presentation/pages/poultry_science/root/logic.dart';
import 'package:rasadyar_core/core.dart';
class PoultryScienceHomeLogic extends GetxController {
PoultryScienceRootLogic rootLogic = Get.find<PoultryScienceRootLogic>();
Rxn<HomePoultryScienceModel> homeInformation = Rxn();
RxBool isExpanded = false.obs;
@override
void onReady() {
super.onReady();
getHomePoultryHatching();
}
Future<void> getHomePoultryHatching() async {
await safeCall<HomePoultryScienceModel?>(
call: () async => await rootLogic.poultryRepository.getHomePoultryScience(
token: rootLogic.tokenService.accessToken.value!,
type: 'home',
),
onSuccess: (result) {
if (result != null) {
homeInformation.value = result;
}
},
onError: (error, stackTrace) {},
);
}
}

View File

@@ -0,0 +1,608 @@
import 'package:flutter/cupertino.dart' hide LinearGradient;
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/data/models/response/inventory/inventory_model.dart';
import 'package:rasadyar_chicken/data/models/response/kill_house_distribution_info/kill_house_distribution_info.dart';
import 'package:rasadyar_chicken/presentation/widget/app_bar.dart';
import 'package:rasadyar_chicken/presentation/widget/steward/widely_used/view.dart';
import 'package:rasadyar_core/core.dart';
import 'logic.dart';
class PoultryScienceHomePage extends GetView<PoultryScienceHomeLogic> {
const PoultryScienceHomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColor.bgLight,
appBar: chickenAppBar(hasBack: false, hasFilter: false, hasSearch: false),
body: SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Column(
spacing: 8,
children: [
InkWell(
onTap: () {
controller.isExpanded.value = !controller.isExpanded.value;
},
child: Card(
margin: EdgeInsetsGeometry.all(6),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(width: 0.50, color: const Color(0xFFA9A9A9)),
),
child: ObxValue((data) {
return AnimatedSize(
duration: Duration(milliseconds: 300),
child: data.value
? Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
spacing: 8,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
width: 40,
height: 40,
decoration: ShapeDecoration(
image: DecorationImage(
image: AssetImage(Assets.images.chicken.path),
fit: BoxFit.cover,
),
shape: RoundedRectangleBorder(
side: BorderSide(
width: 0.25,
color: const Color(0xFFB0B0B0),
),
borderRadius: BorderRadius.circular(4),
),
),
),
Text(
'مرغ گرم',
textAlign: TextAlign.right,
style: AppFonts.yekan16.copyWith(
color: AppColor.darkGreyDarkActive,
),
),
Spacer(),
AnimatedRotation(
turns: 180,
duration: Duration(milliseconds: 3000),
child: Icon(CupertinoIcons.chevron_up, size: 18),
),
],
),
SizedBox(height: 8),
_todayShipmentWidget(),
Row(
children: [
Text(
'اطلاعات جوجه ریزی',
textAlign: TextAlign.right,
style: AppFonts.yekan16,
),
],
),
_inventoryWidget(),
killsShipment(),
ageWidget(),
],
),
)
: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
spacing: 8,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
width: 40,
height: 40,
decoration: ShapeDecoration(
image: DecorationImage(
image: AssetImage(Assets.images.chicken.path),
fit: BoxFit.cover,
),
shape: RoundedRectangleBorder(
side: BorderSide(
width: 0.25,
color: const Color(0xFFB0B0B0),
),
borderRadius: BorderRadius.circular(4),
),
),
),
Text(
'مرغ گرم',
textAlign: TextAlign.right,
style: AppFonts.yekan16.copyWith(
color: AppColor.darkGreyDarkActive,
),
),
Spacer(),
Icon(CupertinoIcons.chevron_down, size: 18),
],
),
_todayShipmentWidget(),
_inventoryWidget(),
],
),
),
);
}, controller.isExpanded),
),
),
WidelyUsedWidget(),
SizedBox(height: 20),
],
),
),
);
}
Widget ageWidget() {
return Padding(
padding: const EdgeInsets.fromLTRB(0, 8, 0, 13),
child: ObxValue((data) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 8,
children: [
_informationIconCard(
title: 'کمترین سن جوجه ریزیط',
isLoading: data.value == null,
description: data.value?.hatchingMinAge.separatedByComma ?? '0',
iconPath: Assets.vec.truckSvg.path,
iconColor: const Color.fromRGBO(85, 97, 93, 1),
bgDescriptionColor: const Color(0xFFE6FAF5),
bgLabelColor: const Color(0xFFB0EFDF),
),
_informationIconCard(
title: 'بیشترین سن جوجه ریزی',
description: data.value?.hatchingMaxAge?.separatedByComma ?? '0',
iconPath: Assets.vec.convertCubeSvg.path,
iconColor: const Color(0xFF6F6164),
bgDescriptionColor: const Color(0xFFEDDCE0),
bgLabelColor: const Color(0xFFE0BCC5),
),
],
);
}, controller.homeInformation),
);
}
Widget killsShipment() {
return Padding(
padding: const EdgeInsets.fromLTRB(0, 8, 0, 13),
child: ObxValue((data) {
return Row(
spacing: 8,
children: [
Expanded(
child: _informationLabelCard(
title: 'تلفات',
isLoading: data.value == null,
description: data.value?.hatchingLosses?.separatedByComma ?? '0',
iconPath: Assets.vec.a3dCubeSquareSvg.path,
iconColor: const Color(0xFF6C5D60),
bgDescriptionColor: const Color(0xFFEDDCE0),
bgLabelColor: const Color(0xFFDDC0C7),
),
),
Expanded(
child: _informationLabelCard(
title: 'حجم کشتار شده',
isLoading: data.value == null,
description: data.value?.hatchingKilledQuantity.separatedByComma ?? '0',
iconPath: Assets.vec.cubeSearchSvg.path,
iconColor: Color(0xFF2D5FFF),
bgLabelColor: const Color(0xFFAFCBFF),
bgDescriptionColor: const Color(0xFFCEDFFF),
),
),
],
);
}, controller.homeInformation),
);
}
Widget _inventoryWidget() {
return ObxValue((data) {
return Padding(
padding: const EdgeInsets.fromLTRB(0, 10, 0, 13),
child: Row(
spacing: 8,
children: [
Expanded(
child: _informationLabelCard(
title: 'حجم جوجه ریزی',
isLoading: data.value == null,
unit: 'قطعه',
description: data.value?.hatchingQuantity.separatedByComma ?? '0',
iconPath: Assets.vec.cubeSearchSvg.path,
iconColor: const Color(0xFF426060),
bgDescriptionColor: const Color(0xFFC7DFE0),
bgLabelColor: const Color(0xFFA5D1D2),
),
),
Expanded(
child: _informationLabelCard(
title: 'مانده در سالن',
unit: 'قطعه',
isLoading: data.value == null,
description: data.value?.hatchingLeftOver.separatedByComma ?? '0',
iconPath: Assets.vec.cubeRotateSvg.path,
iconColor: Color(0xFF5C4D64),
bgLabelColor: Color(0xFFC8B8D1),
bgDescriptionColor: Color(0xFFDAD4DD),
),
),
],
),
);
}, controller.homeInformation);
}
Widget _todayShipmentWidget() {
return Padding(
padding: const EdgeInsets.fromLTRB(0, 10, 0, 13),
child: Row(
spacing: 8,
children: [
Expanded(
child: ObxValue(
(data) => _informationLabelCard(
title: 'کل فارم ها',
titleColor: AppColor.blueNormal,
isLoading: data.value == null,
description: data.value?.farmCount?.separatedByComma ?? '0',
iconPath: Assets.vec.cubeSearchSvg.path,
iconColor: AppColor.blueNormal,
bgDescriptionColor: Colors.white,
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [AppColor.blueLight, Colors.white],
),
),
controller.homeInformation,
),
),
Expanded(
child: ObxValue((data) {
return _informationLabelCard(
title: 'تعداد جوجه ریزی ها',
isLoading: data.value == null,
description: data.value?.hatchingCount.separatedByComma ?? '0',
iconPath: Assets.vec.cubeWattingSvg.path,
bgDescriptionColor: Colors.white,
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [const Color(0xFFFFE7BB), Colors.white],
),
);
}, controller.homeInformation),
),
],
),
);
}
Container _informationLabelCard({
required String title,
required String description,
required String iconPath,
required Color bgDescriptionColor,
String unit = 'کیلوگرم',
bool isLoading = false,
Color? iconColor,
Color? titleColor,
Color? bgLabelColor,
LinearGradient? gradient,
}) {
return Container(
height: 82,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)),
clipBehavior: Clip.hardEdge,
child: Row(
children: [
// Left side with icon and title
Expanded(
child: Container(
height: 82,
decoration: BoxDecoration(
color: gradient == null ? bgLabelColor : null,
borderRadius: BorderRadius.only(
topRight: Radius.circular(8),
bottomRight: Radius.circular(8),
),
gradient: gradient,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 4,
children: [
SvgGenImage.vec(iconPath).svg(
width: 24,
height: 24,
colorFilter: iconColor != null
? ColorFilter.mode(iconColor, BlendMode.srcIn)
: null,
),
Text(
title,
textAlign: TextAlign.right,
style: AppFonts.yekan14.copyWith(
color: titleColor ?? AppColor.mediumGreyDarkActive,
),
),
],
),
),
),
// Right side with description and unit
Expanded(
child: Container(
decoration: BoxDecoration(
color: bgDescriptionColor,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(8),
bottomLeft: Radius.circular(8),
),
),
child: isLoading
? Center(child: CupertinoActivityIndicator())
: Column(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 4,
children: [
Text(
description,
textAlign: TextAlign.right,
style: AppFonts.yekan16.copyWith(color: AppColor.mediumGreyDarkActive),
),
Text(
unit,
textAlign: TextAlign.center,
style: AppFonts.yekan12.copyWith(color: AppColor.mediumGreyDarkActive),
),
],
),
),
),
],
),
);
}
Container _informationIconCard({
required String title,
required String description,
String? unit,
bool isLoading = false,
required String iconPath,
required Color iconColor,
required Color bgDescriptionColor,
required Color bgLabelColor,
}) {
return Container(
height: 80.h,
width: 145.w,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)),
clipBehavior: Clip.hardEdge,
child: Stack(
alignment: Alignment.topCenter,
children: [
Positioned(
bottom: 0,
right: 0,
left: 0,
child: Container(
height: 91,
decoration: BoxDecoration(
color: bgDescriptionColor,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 0.25, color: const Color(0xFFB4B4B4)),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 4,
children: [
Text(
title,
textAlign: TextAlign.right,
style: AppFonts.yekan14.copyWith(color: AppColor.mediumGreyDarkActive),
),
isLoading
? Center(child: CupertinoActivityIndicator())
: Text(
description,
textAlign: TextAlign.right,
style: AppFonts.yekan16.copyWith(color: AppColor.mediumGreyDarkActive),
),
Visibility(
visible: unit != null,
child: Text(
unit ?? '',
textAlign: TextAlign.center,
style: AppFonts.yekan12.copyWith(color: AppColor.mediumGreyDarkActive),
),
),
],
),
),
),
Positioned(
top: 0,
child: Container(
width: 32,
height: 32,
decoration: ShapeDecoration(
color: bgLabelColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
side: BorderSide(width: 0.25, color: const Color(0xFFD5D5D5)),
),
),
child: Center(
child: SvgGenImage.vec(iconPath).svg(
width: 24,
height: 24,
colorFilter: ColorFilter.mode(iconColor, BlendMode.srcIn),
),
),
),
),
],
),
);
}
Widget inventoryItem({
required bool isExpanded,
required int index,
required InventoryModel model,
}) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 8,
children: [
buildRow('نام محصول', model.name ?? ''),
Visibility(
visible: isExpanded,
child: Column(
spacing: 8,
children: [
buildRow('وزن خریدهای دولتی داخل استان (کیلوگرم)', '0326598653'),
buildRow(
'وزن خریدهای آزاد داخل استان (کیلوگرم)',
model.receiveFreeCarcassesWeight.toString(),
),
buildRow(
'وزن خریدهای خارج استان (کیلوگرم)',
model.freeBuyingCarcassesWeight.toString(),
),
buildRow(
'کل ورودی به انبار (کیلوگرم)',
model.totalFreeBarsCarcassesWeight.toString(),
),
buildRow('کل فروش (کیلوگرم)', model.realAllocatedWeight.toString()),
buildRow('مانده انبار (کیلوگرم)', model.totalRemainWeight.toString()),
],
),
),
],
);
}
Widget buildRow(String title, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
flex: 2,
child: Text(
title,
textAlign: TextAlign.right,
style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDarkHover),
),
),
Flexible(
flex: 1,
child: Text(
value,
textAlign: TextAlign.center,
style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDarkHover),
),
),
],
),
);
}
Widget broadcastInformationWidget(KillHouseDistributionInfo? model) {
return Container(
height: 140,
margin: const EdgeInsets.all(8),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: AppColor.blueNormal, width: 1),
),
child: model != null
? Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 10,
children: [
Text(
'اطلاعات ارسالی',
textAlign: TextAlign.right,
style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal),
),
const SizedBox(height: 12),
buildRow(
'فروش و توزیع داخل استان (کیلوگرم)',
model.stewardAllocationsWeight!.toInt().toString(),
),
buildRow(
'فروش و توزیع خارج استان (کیلوگرم)',
model.freeSalesWeight!.toInt().toString(),
),
],
)
: const Center(child: CircularProgressIndicator()),
);
}
Widget cardWidget({
required String title,
required String iconPath,
required VoidCallback onTap,
}) {
return Container(
width: Get.width / 4,
height: 130,
child: GestureDetector(
onTap: onTap,
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(width: 1, color: AppColor.blueNormal),
),
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SvgGenImage(iconPath).svg(width: 50, height: 50),
SizedBox(height: 4),
Text(
title,
textAlign: TextAlign.center,
style: AppFonts.yekan12.copyWith(color: AppColor.blueNormal),
),
],
),
),
),
),
);
}
}

View File

@@ -0,0 +1,148 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/data/models/request/change_password/change_password_request_model.dart';
import 'package:rasadyar_chicken/data/models/response/iran_province_city/iran_province_city_model.dart';
import 'package:rasadyar_chicken/data/models/response/user_profile/user_profile.dart';
import 'package:rasadyar_chicken/presentation/pages/poultry_science/root/logic.dart';
import 'package:rasadyar_core/core.dart';
class PoultryScienceProfileLogic extends GetxController {
PoultryScienceRootLogic rootLogic = Get.find<PoultryScienceRootLogic>();
RxInt selectedInformationType = 0.obs;
Rxn<Jalali> birthDate = Rxn<Jalali>();
Rx<Resource<UserProfile>> userProfile = Rx<Resource<UserProfile>>(Resource.loading());
TextEditingController nameController = TextEditingController();
TextEditingController lastNameController = TextEditingController();
TextEditingController nationalCodeController = TextEditingController();
TextEditingController nationalIdController = TextEditingController();
TextEditingController birthdayController = TextEditingController();
TextEditingController oldPasswordController = TextEditingController();
TextEditingController newPasswordController = TextEditingController();
TextEditingController retryNewPasswordController = TextEditingController();
RxList<IranProvinceCityModel> cites = <IranProvinceCityModel>[].obs;
Rxn<IranProvinceCityModel> selectedProvince = Rxn();
Rxn<IranProvinceCityModel> selectedCity = Rxn();
GlobalKey<FormState> formKey = GlobalKey();
ImagePicker imagePicker = ImagePicker();
Rxn<XFile> selectedImage = Rxn<XFile>();
RxnString _base64Image = RxnString();
RxBool isOnLoading = false.obs;
@override
void onInit() {
super.onInit();
ever(selectedImage, (data) async {
if (data?.path != null) {
_base64Image.value = await convertImageToBase64(data!.path);
}
});
}
@override
void onReady() {
super.onReady();
getUserProfile();
selectedProvince.listen((p0) => getCites());
userProfile.listen((data) {
nameController.text = data.data?.firstName ?? '';
lastNameController.text = data.data?.lastName ?? '';
nationalCodeController.text = data.data?.nationalCode ?? '';
nationalIdController.text = data.data?.nationalId ?? '';
birthdayController.text = data.data?.birthday?.toJalali.formatCompactDate() ?? '';
birthDate.value = data.data?.birthday?.toJalali;
selectedProvince.value = IranProvinceCityModel(
name: data.data?.province ?? '',
id: data.data?.provinceNumber ?? 0,
);
selectedCity.value = IranProvinceCityModel(
name: data.data?.city ?? '',
id: data.data?.cityNumber ?? 0,
);
});
}
@override
void onClose() {
super.onClose();
}
Future<void> getUserProfile() async {
userProfile.value = Resource.loading();
await safeCall<UserProfile?>(
call: () async => await rootLogic.chickenRepository.getUserProfile(
token: rootLogic.tokenService.accessToken.value!,
),
onSuccess: (result) {
if (result != null) {
userProfile.value = Resource.success(result);
}
},
onError: (error, stackTrace) {},
);
}
Future<void> getCites() async {
await safeCall(
call: () =>
rootLogic.chickenRepository.getCity(provinceName: selectedProvince.value?.name ?? ''),
onSuccess: (result) {
if (result != null && result.isNotEmpty) {
cites.value = result;
}
},
);
}
Future<void> updateUserProfile() async {
UserProfile userProfile = UserProfile(
firstName: nameController.text,
lastName: lastNameController.text,
nationalCode: nationalCodeController.text,
nationalId: nationalIdController.text,
birthday: birthDate.value?.toDateTime().formattedDashedGregorian.toString(),
image: _base64Image.value,
personType: 'self',
type: 'self_profile',
);
isOnLoading.value = true;
await safeCall(
call: () async => await rootLogic.chickenRepository.updateUserProfile(
token: rootLogic.tokenService.accessToken.value!,
userProfile: userProfile,
),
onSuccess: (result) {
isOnLoading.value = false;
},
onError: (error, stackTrace) {
isOnLoading.value = false;
},
);
}
Future<void> updatePassword() async {
if (formKey.currentState?.validate() ?? false) {
ChangePasswordRequestModel model = ChangePasswordRequestModel(
username: userProfile.value.data?.mobile,
password: newPasswordController.text,
);
await safeCall(
call: () async => await rootLogic.chickenRepository.updatePassword(
token: rootLogic.tokenService.accessToken.value!,
model: model,
),
);
}
}
void clearPasswordForm() {
oldPasswordController.clear();
newPasswordController.clear();
retryNewPasswordController.clear();
}
}

View File

@@ -0,0 +1,637 @@
import 'dart:io';
import 'package:flutter/cupertino.dart' hide Image;
import 'package:flutter/material.dart';
import 'package:rasadyar_chicken/chicken.dart';
import 'package:rasadyar_chicken/data/models/response/iran_province_city/iran_province_city_model.dart';
import 'package:rasadyar_chicken/data/models/response/user_profile/user_profile.dart';
import 'package:rasadyar_core/core.dart';
import 'logic.dart';
class PoultryScienceProfilePage extends GetView<PoultryScienceProfileLogic> {
const PoultryScienceProfilePage({super.key});
@override
Widget build(BuildContext context) {
return Column(
spacing: 30,
children: [
Expanded(
flex: 1,
child: Container(
color: AppColor.blueNormal,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(),
ObxValue((data) {
final status = data.value.status;
if (status == ResourceStatus.loading) {
return Container(
width: 128.w,
height: 128.h,
child: Center(child: CupertinoActivityIndicator(color: AppColor.greenNormal)),
);
}
if (status == ResourceStatus.error) {
return Container(
width: 128.w,
height: 128.h,
child: Center(child: Text('خطا در دریافت اطلاعات')),
);
}
// Default UI
return Container(
width: 128.w,
height: 128.h,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppColor.blueLightActive,
),
child: Center(
child: CircleAvatar(
radius: 64.w,
backgroundImage: NetworkImage(data.value.data!.image!),
),
),
);
}, controller.userProfile),
],
),
),
),
Expanded(
flex: 3,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 16,
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 10),
child: userProfileInformation(),
),
),
Center(
child: Wrap(
alignment: WrapAlignment.center,
spacing: 20,
runSpacing: 10,
children: [
cardActionWidget(
title: 'تغییر رمز عبور',
selected: true,
onPressed: () {
Get.bottomSheet(changePasswordBottomSheet(), isScrollControlled: true);
},
icon: Assets.vec.lockSvg.path,
),
cardActionWidget(
title: 'خروج',
selected: true,
color: ColorFilter.mode(Colors.redAccent, BlendMode.srcIn),
cardColor: Color(0xFFEFEFEF),
textColor: AppColor.redDarkerText,
onPressed: () {
Get.bottomSheet(exitBottomSheet(), isScrollControlled: true);
},
icon: Assets.vec.logoutSvg.path,
),
],
),
),
SizedBox(height: 100),
],
),
),
],
);
}
Container invoiceIssuanceInformation() => Container();
Widget bankInformationWidget() => Column(
spacing: 16,
children: [
itemList(title: 'نام بانک', content: 'سامان'),
itemList(title: 'نام صاحب حساب', content: 'رضا رضایی'),
itemList(title: 'شماره کارت ', content: '54154545415'),
itemList(title: 'شماره حساب', content: '62565263263652'),
itemList(title: 'شماره شبا', content: '62565263263652'),
],
);
Widget userProfileInformation() {
return ObxValue((data) {
if (data.value.status == ResourceStatus.loading) {
return LoadingWidget();
} else if (data.value.status == ResourceStatus.error) {
return ErrorWidget('خطا در دریافت اطلاعات کاربر');
} else if (data.value.status == ResourceStatus.success) {
UserProfile item = data.value.data!;
return Column(
spacing: 6,
children: [
buildRowOnTapped(
onTap: () {
Get.bottomSheet(
userInformationBottomSheet(),
isScrollControlled: true,
ignoreSafeArea: false,
);
},
titleWidget: Column(
spacing: 3,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'اطلاعات هویتی',
style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal),
),
Container(width: 37.w, height: 1.h, color: AppColor.greenNormal),
],
),
valueWidget: Assets.vec.editSvg.svg(
width: 24.w,
height: 24.h,
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
),
),
itemList(
title: 'نام و نام خانوادگی',
content: item.fullname ?? 'نامشخص',
icon: Assets.vec.userSvg.path,
hasColoredBox: true,
),
itemList(
title: 'موبایل',
content: item.mobile ?? 'نامشخص',
icon: Assets.vec.callSvg.path,
),
itemList(
title: 'کدملی',
content: item.nationalId ?? 'نامشخص',
icon: Assets.vec.tagUserSvg.path,
),
itemList(
title: 'شماره شناسنامه',
content: item.nationalCode ?? 'نامشخص',
icon: Assets.vec.userSquareSvg.path,
),
itemList(
title: 'تاریخ تولد',
content: item.birthday?.toJalali.formatCompactDate() ?? 'نامشخص',
icon: Assets.vec.calendarSvg.path,
),
itemList(
title: 'استان',
content: item.province ?? 'نامشخص',
icon: Assets.vec.pictureFrameSvg.path,
),
itemList(title: 'شهر', content: item.city ?? 'نامشخص', icon: Assets.vec.mapSvg.path),
],
);
} else {
return SizedBox.shrink();
}
}, controller.userProfile);
}
Widget itemList({
required String title,
required String content,
String? icon,
bool hasColoredBox = false,
}) => Container(
padding: EdgeInsets.symmetric(horizontal: 12.h, vertical: 6.h),
decoration: BoxDecoration(
color: hasColoredBox ? AppColor.greenLight : Colors.transparent,
borderRadius: BorderRadius.circular(8),
border: hasColoredBox
? Border.all(width: 0.25, color: AppColor.bgDark)
: Border.all(width: 0, color: Colors.transparent),
),
child: Row(
spacing: 4,
children: [
if (icon != null)
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: SvgGenImage.vec(icon).svg(
width: 20.w,
height: 20.h,
colorFilter: ColorFilter.mode(AppColor.mediumGreyNormalActive, BlendMode.srcIn),
),
),
Text(title, style: AppFonts.yekan12.copyWith(color: AppColor.mediumGreyNormalActive)),
Spacer(),
Text(content, style: AppFonts.yekan13.copyWith(color: AppColor.mediumGreyNormalHover)),
],
),
);
Widget cardActionWidget({
required String title,
required VoidCallback onPressed,
required String icon,
bool selected = false,
ColorFilter? color,
Color? cardColor,
Color? textColor,
}) {
return GestureDetector(
onTap: onPressed,
child: Column(
spacing: 4,
children: [
Container(
width: 52,
height: 52,
padding: EdgeInsets.all(8),
decoration: ShapeDecoration(
color: cardColor ?? AppColor.blueLight,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
child: SvgGenImage.vec(icon).svg(
width: 40,
height: 40,
colorFilter:
color ??
ColorFilter.mode(
selected ? AppColor.blueNormal : AppColor.whiteLight,
BlendMode.srcIn,
),
),
),
SizedBox(height: 2),
Text(
title,
style: AppFonts.yekan10.copyWith(
color: textColor ?? (selected ? AppColor.blueNormal : AppColor.blueLightActive),
),
textAlign: TextAlign.center,
),
],
),
);
}
Widget userInformationBottomSheet() {
return BaseBottomSheet(
height: 750.h,
child: SingleChildScrollView(
child: Column(
spacing: 8,
children: [
Text(
'ویرایش اطلاعات هویتی',
style: AppFonts.yekan16Bold.copyWith(color: AppColor.darkGreyDarkHover),
),
Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: AppColor.darkGreyLight, width: 1),
),
child: Column(
spacing: 12,
children: [
RTextField(
controller: controller.nameController,
label: 'نام',
borderColor: AppColor.darkGreyLight,
filledColor: AppColor.bgLight,
filled: true,
),
RTextField(
controller: controller.lastNameController,
label: 'نام خانوادگی',
borderColor: AppColor.darkGreyLight,
filledColor: AppColor.bgLight,
filled: true,
),
RTextField(
controller: controller.nationalCodeController,
label: 'شماره شناسنامه',
borderColor: AppColor.darkGreyLight,
filledColor: AppColor.bgLight,
filled: true,
),
RTextField(
controller: controller.nationalIdController,
label: 'کد ملی',
borderColor: AppColor.darkGreyLight,
filledColor: AppColor.bgLight,
filled: true,
),
ObxValue((data) {
return RTextField(
controller: controller.birthdayController,
label: 'تاریخ تولد',
initText: data.value?.formatCompactDate() ?? '',
borderColor: AppColor.darkGreyLight,
filledColor: AppColor.bgLight,
filled: true,
onTap: () {},
);
}, controller.birthDate),
SizedBox(),
],
),
),
SizedBox(),
Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: AppColor.darkGreyLight, width: 1),
),
child: Column(
spacing: 8,
children: [
Text(
'عکس پروفایل',
style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal),
),
ObxValue((data) {
return Container(
width: Get.width,
height: 270,
decoration: BoxDecoration(
color: AppColor.lightGreyNormal,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 1, color: AppColor.blackLight),
),
child: Center(
child: data.value == null
? Padding(
padding: const EdgeInsets.fromLTRB(30, 10, 10, 30),
child: Image.network(
controller.userProfile.value.data?.image ?? '',
),
)
: Image.file(File(data.value!.path), fit: BoxFit.cover),
),
);
}, controller.selectedImage),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
RElevated(
text: 'گالری',
width: 150.w,
height: 40.h,
textStyle: AppFonts.yekan20.copyWith(color: Colors.white),
onPressed: () async {
controller.selectedImage.value = await controller.imagePicker.pickImage(
source: ImageSource.gallery,
imageQuality: 60,
maxWidth: 1080,
maxHeight: 720,
);
},
),
SizedBox(width: 16),
ROutlinedElevated(
text: 'دوربین',
width: 150.w,
height: 40.h,
textStyle: AppFonts.yekan20.copyWith(color: AppColor.blueNormal),
onPressed: () async {
controller.selectedImage.value = await controller.imagePicker.pickImage(
source: ImageSource.camera,
imageQuality: 60,
maxWidth: 1080,
maxHeight: 720,
);
},
),
],
),
],
),
),
Row(
spacing: 16,
mainAxisAlignment: MainAxisAlignment.center,
children: [
ObxValue((data) {
return RElevated(
height: 40.h,
text: 'ویرایش',
isLoading: data.value,
onPressed: () async {
await controller.updateUserProfile();
controller.getUserProfile();
Get.back();
},
);
}, controller.isOnLoading),
ROutlinedElevated(
height: 40.h,
text: 'انصراف',
borderColor: AppColor.blueNormal,
onPressed: () {
Get.back();
},
),
],
),
],
),
),
);
}
Widget _provinceWidget() {
return Obx(() {
return OverlayDropdownWidget<IranProvinceCityModel>(
items: controller.rootLogic.provinces,
onChanged: (value) {
controller.selectedProvince.value = value;
},
selectedItem: controller.selectedProvince.value,
itemBuilder: (item) => Text(item.name ?? 'بدون نام'),
labelBuilder: (item) => Text(item?.name ?? 'انتخاب استان'),
);
});
}
Widget _cityWidget() {
return ObxValue((data) {
return OverlayDropdownWidget<IranProvinceCityModel>(
items: data,
onChanged: (value) {
controller.selectedCity.value = value;
},
selectedItem: controller.selectedCity.value,
itemBuilder: (item) => Text(item.name ?? 'بدون نام'),
labelBuilder: (item) => Text(item?.name ?? 'انتخاب شهر'),
);
}, controller.cites);
}
Widget changePasswordBottomSheet() {
return BaseBottomSheet(
height: 400.h,
child: SingleChildScrollView(
child: Form(
key: controller.formKey,
child: Column(
spacing: 8,
children: [
Text(
'تغییر رمز عبور',
style: AppFonts.yekan16Bold.copyWith(color: AppColor.darkGreyDarkHover),
),
SizedBox(),
RTextField(
controller: controller.oldPasswordController,
hintText: 'رمز عبور قبلی',
borderColor: AppColor.darkGreyLight,
filledColor: AppColor.bgLight,
filled: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'رمز عبور را وارد کنید';
} else if (controller.userProfile.value.data?.password != value) {
return 'رمز عبور صحیح نیست';
}
return null;
},
),
RTextField(
controller: controller.newPasswordController,
hintText: 'رمز عبور جدید',
borderColor: AppColor.darkGreyLight,
filledColor: AppColor.bgLight,
filled: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'رمز عبور را وارد کنید';
} else if (value.length < 6) {
return 'رمز عبور باید بیش از 6 کارکتر باشد.';
}
return null;
},
),
RTextField(
controller: controller.retryNewPasswordController,
hintText: 'تکرار رمز عبور جدید',
borderColor: AppColor.darkGreyLight,
filledColor: AppColor.bgLight,
filled: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'رمز عبور را وارد کنید';
} else if (value.length < 6) {
return 'رمز عبور باید بیش از 6 کارکتر باشد.';
} else if (controller.newPasswordController.text != value) {
return 'رمز عبور جدید یکسان نیست';
}
return null;
},
),
SizedBox(),
Row(
spacing: 16,
mainAxisAlignment: MainAxisAlignment.center,
children: [
RElevated(
height: 40.h,
text: 'ویرایش',
onPressed: () async {
if (controller.formKey.currentState?.validate() != true) {
return;
}
await controller.updatePassword();
controller.getUserProfile();
controller.clearPasswordForm();
Get.back();
},
),
ROutlinedElevated(
height: 40.h,
text: 'انصراف',
borderColor: AppColor.blueNormal,
onPressed: () {
Get.back();
},
),
],
),
],
),
),
),
);
}
Widget exitBottomSheet() {
return BaseBottomSheet(
height: 220.h,
child: SingleChildScrollView(
child: Form(
key: controller.formKey,
child: Column(
spacing: 8,
children: [
Text('خروج', style: AppFonts.yekan16Bold.copyWith(color: AppColor.error)),
SizedBox(),
Text(
'آیا مطمئن هستید که می‌خواهید از حساب کاربری خود خارج شوید؟',
textAlign: TextAlign.center,
style: AppFonts.yekan16Bold.copyWith(color: AppColor.textColor),
),
SizedBox(),
Row(
spacing: 16,
mainAxisAlignment: MainAxisAlignment.center,
children: [
RElevated(
height: 40.h,
text: 'خروج',
backgroundColor: AppColor.error,
onPressed: () async {
await controller.rootLogic.tokenService.deleteAllTokens().then((value) {
Get.back();
Get.offAllNamed(ChickenRoutes.auth, arguments: Module.chicken);
});
},
),
ROutlinedElevated(
height: 40.h,
text: 'انصراف',
borderColor: AppColor.blueNormal,
onPressed: () {
Get.back();
},
),
],
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,71 @@
import 'package:flutter/widgets.dart';
import 'package:rasadyar_chicken/data/data_source/local/chicken_local.dart';
import 'package:rasadyar_chicken/data/di/chicken_di.dart';
import 'package:rasadyar_chicken/data/models/local/widely_used_local_model.dart';
import 'package:rasadyar_chicken/data/models/response/inventory/inventory_model.dart';
import 'package:rasadyar_chicken/data/models/response/iran_province_city/iran_province_city_model.dart';
import 'package:rasadyar_chicken/data/models/response/roles_products/roles_products.dart';
import 'package:rasadyar_chicken/data/repositories/poultry_science/poultry_science_repository.dart';
import 'package:rasadyar_chicken/presentation/pages/poultry_science/home/view.dart';
import 'package:rasadyar_chicken/presentation/pages/steward/buy/view.dart';
import 'package:rasadyar_chicken/presentation/pages/steward/home/view.dart';
import 'package:rasadyar_chicken/presentation/pages/steward/profile/view.dart';
import 'package:rasadyar_chicken/presentation/pages/steward/sale/view.dart';
import 'package:rasadyar_chicken/presentation/pages/steward/segmentation/view.dart';
import 'package:rasadyar_chicken/presentation/routes/routes.dart';
import 'package:rasadyar_chicken/presentation/utils/utils.dart';
import 'package:rasadyar_core/core.dart';
enum ErrorLocationType { serviceDisabled, permissionDenied, none }
class PoultryScienceRootLogic extends GetxController {
RxInt currentPage = 2.obs;
List<Widget> pages = [BuyPage(), SalePage(), PoultryScienceHomePage(), SegmentationPage(), ProfilePage()];
late DioRemote dioRemote;
var tokenService = Get.find<TokenStorageService>();
late PoultryScienceRepository poultryRepository;
RxList<ErrorLocationType> errorLocationType = RxList();
RxMap<int, dynamic> homeExpandedList = RxMap();
Rxn<InventoryModel> inventoryModel = Rxn<InventoryModel>();
RxList<IranProvinceCityModel> provinces = <IranProvinceCityModel>[].obs;
// Cancel tokens for API calls
CancelToken? _inventoryCancelToken;
CancelToken? _provincesCancelToken;
@override
void onInit() {
super.onInit();
poultryRepository = diChicken.get<PoultryScienceRepository>();
}
@override
void onReady() {
super.onReady();
}
@override
void onClose() {
super.onClose();
}
void toggleExpanded(int index) {
if (homeExpandedList.keys.contains(index)) {
homeExpandedList.remove(index);
} else {
homeExpandedList[index] = false;
}
}
void rootErrorHandler(DioException error) {
handleGeneric(error, () {
tokenService.deleteAllTokens();
});
}
void changePage(int index) {
currentPage.value = index;
}
}

View File

@@ -0,0 +1,128 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:rasadyar_chicken/chicken.dart';
import 'package:rasadyar_chicken/data/models/response/kill_house_distribution_info/kill_house_distribution_info.dart';
import 'package:rasadyar_core/core.dart';
import 'logic.dart';
class PoultryScienceRootPage extends GetView<PoultryScienceRootLogic> {
PoultryScienceRootPage({super.key});
DateTime? _lastBackPressed;
@override
Widget build(BuildContext context) {
return ObxValue((data) {
return PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, result) async {
final nestedKey = Get.nestedKey(controller.currentPage.value);
final currentNavigator = nestedKey?.currentState;
if (currentNavigator?.canPop() ?? false) {
currentNavigator?.pop();
} else {
final now = DateTime.now();
if (_lastBackPressed == null ||
now.difference(_lastBackPressed!) > Duration(seconds: 2)) {
_lastBackPressed = now;
Get.snackbar(
'خروج از برنامه',
'برای خروج دوباره بازگشت را بزنید',
snackPosition: SnackPosition.TOP,
duration: Duration(seconds: 2),
backgroundColor: AppColor.warning,
);
} else {
await SystemNavigator.pop();
}
}
},
child: Scaffold(
backgroundColor: AppColor.bgLight,
body: IndexedStack(
children: [
Navigator(
key: Get.nestedKey(0),
onGenerateRoute: (settings) {
final page = ChickenPages.pages.firstWhere(
(e) => e.name == settings.name,
orElse: () =>
ChickenPages.pages.firstWhere((e) => e.name == ChickenRoutes.buySteward),
);
return buildRouteFromGetPage(page);
},
),
Navigator(
key: Get.nestedKey(1),
onGenerateRoute: (settings) {
final page = ChickenPages.pages.firstWhere(
(e) => e.name == settings.name,
orElse: () =>
ChickenPages.pages.firstWhere((e) => e.name == ChickenRoutes.saleSteward),
);
return buildRouteFromGetPage(page);
},
),
Navigator(
key: Get.nestedKey(2),
onGenerateRoute: (settings) => GetPageRoute(page: () => controller.pages[2]),
),
Navigator(
key: Get.nestedKey(3),
onGenerateRoute: (settings) => GetPageRoute(page: () => controller.pages[3]),
),
Navigator(
key: Get.nestedKey(4),
onGenerateRoute: (settings) => GetPageRoute(page: () => controller.pages[4]),
),
],
index: data.value,
),
bottomNavigationBar: RBottomNavigation(
mainAxisAlignment: MainAxisAlignment.spaceAround,
items: [
RBottomNavigationItem(
label: 'اقدام',
icon: Assets.vec.settingSvg.path,
isSelected: controller.currentPage.value == 0,
onTap: () {
//Get.nestedKey(1)?.currentState?.popUntil((route) => route.isFirst);
controller.changePage(0);
},
),
RBottomNavigationItem(
label: 'خانه',
icon: Assets.vec.homeSvg.path,
isSelected: controller.currentPage.value == 1,
onTap: () {
/* Get.nestedKey(1)?.currentState?.popUntil((route) => route.isFirst);
Get.nestedKey(0)?.currentState?.popUntil((route) => route.isFirst);*/
controller.changePage(1);
},
),
RBottomNavigationItem(
label: 'پروفایل',
icon: Assets.vec.profileCircleSvg.path,
isSelected: controller.currentPage.value == 2,
onTap: () {
/* Get.nestedKey(1)?.currentState?.popUntil((route) => route.isFirst);
Get.nestedKey(0)?.currentState?.popUntil((route) => route.isFirst);
*/
controller.changePage(2);
},
),
],
),
),
);
}, controller.currentPage);
}
}

View File

@@ -1,5 +1,8 @@
import 'package:rasadyar_chicken/presentation/pages/auth/logic.dart';
import 'package:rasadyar_chicken/presentation/pages/auth/view.dart';
import 'package:rasadyar_chicken/presentation/pages/poultry_science/home/logic.dart';
import 'package:rasadyar_chicken/presentation/pages/poultry_science/root/logic.dart';
import 'package:rasadyar_chicken/presentation/pages/poultry_science/root/view.dart';
import 'package:rasadyar_chicken/presentation/pages/role/logic.dart';
import 'package:rasadyar_chicken/presentation/pages/role/view.dart';
import 'package:rasadyar_chicken/presentation/pages/steward/steward.dart';
@@ -136,5 +139,24 @@ sealed class ChickenPages {
),
//endregion
//region Poultry science Pages
GetPage(
name: ChickenRoutes.initPoultryScience,
page: () => PoultryScienceRootPage(),
middlewares: [AuthMiddleware()],
binding: BindingsBuilder(() {
Get.lazyPut(() => BaseLogic());
Get.lazyPut(() => PoultryScienceRootLogic());
Get.lazyPut(() => PoultryScienceHomeLogic());
Get.lazyPut(() => BuyLogic());
Get.lazyPut(() => SaleLogic());
Get.lazyPut(() => ProfileLogic());
Get.lazyPut(() => SegmentationLogic());
Get.lazyPut(() => SearchLogic());
}),
),
//endregion
];
}