feat: category analytic
This commit is contained in:
parent
b3c72cbbc0
commit
577adb7964
@ -0,0 +1,51 @@
|
|||||||
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:injectable/injectable.dart';
|
||||||
|
|
||||||
|
import '../../../domain/analytic/analytic.dart';
|
||||||
|
import '../../../domain/analytic/repositories/i_analytic_repository.dart';
|
||||||
|
|
||||||
|
part 'category_analytic_loader_event.dart';
|
||||||
|
part 'category_analytic_loader_state.dart';
|
||||||
|
part 'category_analytic_loader_bloc.freezed.dart';
|
||||||
|
|
||||||
|
@injectable
|
||||||
|
class CategoryAnalyticLoaderBloc
|
||||||
|
extends Bloc<CategoryAnalyticLoaderEvent, CategoryAnalyticLoaderState> {
|
||||||
|
final IAnalyticRepository _repository;
|
||||||
|
|
||||||
|
CategoryAnalyticLoaderBloc(this._repository)
|
||||||
|
: super(CategoryAnalyticLoaderState.initial()) {
|
||||||
|
on<CategoryAnalyticLoaderEvent>(_onCategoryAnalyticLoaderEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onCategoryAnalyticLoaderEvent(
|
||||||
|
CategoryAnalyticLoaderEvent event,
|
||||||
|
Emitter<CategoryAnalyticLoaderState> emit,
|
||||||
|
) {
|
||||||
|
return event.map(
|
||||||
|
fetched: (e) async {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
isFetching: true,
|
||||||
|
failureOptionCategoryAnalytic: none(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final result = await _repository.getCategory(
|
||||||
|
dateFrom: DateTime.now().subtract(const Duration(days: 30)),
|
||||||
|
dateTo: DateTime.now(),
|
||||||
|
);
|
||||||
|
|
||||||
|
var data = result.fold(
|
||||||
|
(f) => state.copyWith(failureOptionCategoryAnalytic: optionOf(f)),
|
||||||
|
(categoryAnalytic) =>
|
||||||
|
state.copyWith(categoryAnalytic: categoryAnalytic),
|
||||||
|
);
|
||||||
|
|
||||||
|
emit(data.copyWith(isFetching: false));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,401 @@
|
|||||||
|
// 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 'category_analytic_loader_bloc.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models',
|
||||||
|
);
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$CategoryAnalyticLoaderEvent {
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({
|
||||||
|
required TResult Function() fetched,
|
||||||
|
}) => throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function()? fetched,
|
||||||
|
}) => throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function()? fetched,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) => throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(_Fetched value) fetched,
|
||||||
|
}) => throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(_Fetched value)? fetched,
|
||||||
|
}) => throw _privateConstructorUsedError;
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(_Fetched value)? fetched,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) => throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $CategoryAnalyticLoaderEventCopyWith<$Res> {
|
||||||
|
factory $CategoryAnalyticLoaderEventCopyWith(
|
||||||
|
CategoryAnalyticLoaderEvent value,
|
||||||
|
$Res Function(CategoryAnalyticLoaderEvent) then,
|
||||||
|
) =
|
||||||
|
_$CategoryAnalyticLoaderEventCopyWithImpl<
|
||||||
|
$Res,
|
||||||
|
CategoryAnalyticLoaderEvent
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$CategoryAnalyticLoaderEventCopyWithImpl<
|
||||||
|
$Res,
|
||||||
|
$Val extends CategoryAnalyticLoaderEvent
|
||||||
|
>
|
||||||
|
implements $CategoryAnalyticLoaderEventCopyWith<$Res> {
|
||||||
|
_$CategoryAnalyticLoaderEventCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
/// Create a copy of CategoryAnalyticLoaderEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$FetchedImplCopyWith<$Res> {
|
||||||
|
factory _$$FetchedImplCopyWith(
|
||||||
|
_$FetchedImpl value,
|
||||||
|
$Res Function(_$FetchedImpl) then,
|
||||||
|
) = __$$FetchedImplCopyWithImpl<$Res>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$FetchedImplCopyWithImpl<$Res>
|
||||||
|
extends _$CategoryAnalyticLoaderEventCopyWithImpl<$Res, _$FetchedImpl>
|
||||||
|
implements _$$FetchedImplCopyWith<$Res> {
|
||||||
|
__$$FetchedImplCopyWithImpl(
|
||||||
|
_$FetchedImpl _value,
|
||||||
|
$Res Function(_$FetchedImpl) _then,
|
||||||
|
) : super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of CategoryAnalyticLoaderEvent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$FetchedImpl implements _Fetched {
|
||||||
|
const _$FetchedImpl();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'CategoryAnalyticLoaderEvent.fetched()';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType && other is _$FetchedImpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => runtimeType.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult when<TResult extends Object?>({required TResult Function() fetched}) {
|
||||||
|
return fetched();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? whenOrNull<TResult extends Object?>({TResult? Function()? fetched}) {
|
||||||
|
return fetched?.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeWhen<TResult extends Object?>({
|
||||||
|
TResult Function()? fetched,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (fetched != null) {
|
||||||
|
return fetched();
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult map<TResult extends Object?>({
|
||||||
|
required TResult Function(_Fetched value) fetched,
|
||||||
|
}) {
|
||||||
|
return fetched(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult? mapOrNull<TResult extends Object?>({
|
||||||
|
TResult? Function(_Fetched value)? fetched,
|
||||||
|
}) {
|
||||||
|
return fetched?.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@optionalTypeArgs
|
||||||
|
TResult maybeMap<TResult extends Object?>({
|
||||||
|
TResult Function(_Fetched value)? fetched,
|
||||||
|
required TResult orElse(),
|
||||||
|
}) {
|
||||||
|
if (fetched != null) {
|
||||||
|
return fetched(this);
|
||||||
|
}
|
||||||
|
return orElse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _Fetched implements CategoryAnalyticLoaderEvent {
|
||||||
|
const factory _Fetched() = _$FetchedImpl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$CategoryAnalyticLoaderState {
|
||||||
|
CategoryAnalytic get categoryAnalytic => throw _privateConstructorUsedError;
|
||||||
|
Option<AnalyticFailure> get failureOptionCategoryAnalytic =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
bool get isFetching => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Create a copy of CategoryAnalyticLoaderState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
$CategoryAnalyticLoaderStateCopyWith<CategoryAnalyticLoaderState>
|
||||||
|
get copyWith => throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $CategoryAnalyticLoaderStateCopyWith<$Res> {
|
||||||
|
factory $CategoryAnalyticLoaderStateCopyWith(
|
||||||
|
CategoryAnalyticLoaderState value,
|
||||||
|
$Res Function(CategoryAnalyticLoaderState) then,
|
||||||
|
) =
|
||||||
|
_$CategoryAnalyticLoaderStateCopyWithImpl<
|
||||||
|
$Res,
|
||||||
|
CategoryAnalyticLoaderState
|
||||||
|
>;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
CategoryAnalytic categoryAnalytic,
|
||||||
|
Option<AnalyticFailure> failureOptionCategoryAnalytic,
|
||||||
|
bool isFetching,
|
||||||
|
});
|
||||||
|
|
||||||
|
$CategoryAnalyticCopyWith<$Res> get categoryAnalytic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$CategoryAnalyticLoaderStateCopyWithImpl<
|
||||||
|
$Res,
|
||||||
|
$Val extends CategoryAnalyticLoaderState
|
||||||
|
>
|
||||||
|
implements $CategoryAnalyticLoaderStateCopyWith<$Res> {
|
||||||
|
_$CategoryAnalyticLoaderStateCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
/// Create a copy of CategoryAnalyticLoaderState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? categoryAnalytic = null,
|
||||||
|
Object? failureOptionCategoryAnalytic = null,
|
||||||
|
Object? isFetching = null,
|
||||||
|
}) {
|
||||||
|
return _then(
|
||||||
|
_value.copyWith(
|
||||||
|
categoryAnalytic: null == categoryAnalytic
|
||||||
|
? _value.categoryAnalytic
|
||||||
|
: categoryAnalytic // ignore: cast_nullable_to_non_nullable
|
||||||
|
as CategoryAnalytic,
|
||||||
|
failureOptionCategoryAnalytic: null == failureOptionCategoryAnalytic
|
||||||
|
? _value.failureOptionCategoryAnalytic
|
||||||
|
: failureOptionCategoryAnalytic // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Option<AnalyticFailure>,
|
||||||
|
isFetching: null == isFetching
|
||||||
|
? _value.isFetching
|
||||||
|
: isFetching // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
)
|
||||||
|
as $Val,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a copy of CategoryAnalyticLoaderState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$CategoryAnalyticCopyWith<$Res> get categoryAnalytic {
|
||||||
|
return $CategoryAnalyticCopyWith<$Res>(_value.categoryAnalytic, (value) {
|
||||||
|
return _then(_value.copyWith(categoryAnalytic: value) as $Val);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$CategoryAnalyticLoaderStateImplCopyWith<$Res>
|
||||||
|
implements $CategoryAnalyticLoaderStateCopyWith<$Res> {
|
||||||
|
factory _$$CategoryAnalyticLoaderStateImplCopyWith(
|
||||||
|
_$CategoryAnalyticLoaderStateImpl value,
|
||||||
|
$Res Function(_$CategoryAnalyticLoaderStateImpl) then,
|
||||||
|
) = __$$CategoryAnalyticLoaderStateImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
CategoryAnalytic categoryAnalytic,
|
||||||
|
Option<AnalyticFailure> failureOptionCategoryAnalytic,
|
||||||
|
bool isFetching,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
$CategoryAnalyticCopyWith<$Res> get categoryAnalytic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$CategoryAnalyticLoaderStateImplCopyWithImpl<$Res>
|
||||||
|
extends
|
||||||
|
_$CategoryAnalyticLoaderStateCopyWithImpl<
|
||||||
|
$Res,
|
||||||
|
_$CategoryAnalyticLoaderStateImpl
|
||||||
|
>
|
||||||
|
implements _$$CategoryAnalyticLoaderStateImplCopyWith<$Res> {
|
||||||
|
__$$CategoryAnalyticLoaderStateImplCopyWithImpl(
|
||||||
|
_$CategoryAnalyticLoaderStateImpl _value,
|
||||||
|
$Res Function(_$CategoryAnalyticLoaderStateImpl) _then,
|
||||||
|
) : super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of CategoryAnalyticLoaderState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? categoryAnalytic = null,
|
||||||
|
Object? failureOptionCategoryAnalytic = null,
|
||||||
|
Object? isFetching = null,
|
||||||
|
}) {
|
||||||
|
return _then(
|
||||||
|
_$CategoryAnalyticLoaderStateImpl(
|
||||||
|
categoryAnalytic: null == categoryAnalytic
|
||||||
|
? _value.categoryAnalytic
|
||||||
|
: categoryAnalytic // ignore: cast_nullable_to_non_nullable
|
||||||
|
as CategoryAnalytic,
|
||||||
|
failureOptionCategoryAnalytic: null == failureOptionCategoryAnalytic
|
||||||
|
? _value.failureOptionCategoryAnalytic
|
||||||
|
: failureOptionCategoryAnalytic // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Option<AnalyticFailure>,
|
||||||
|
isFetching: null == isFetching
|
||||||
|
? _value.isFetching
|
||||||
|
: isFetching // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$CategoryAnalyticLoaderStateImpl
|
||||||
|
implements _CategoryAnalyticLoaderState {
|
||||||
|
const _$CategoryAnalyticLoaderStateImpl({
|
||||||
|
required this.categoryAnalytic,
|
||||||
|
required this.failureOptionCategoryAnalytic,
|
||||||
|
this.isFetching = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
final CategoryAnalytic categoryAnalytic;
|
||||||
|
@override
|
||||||
|
final Option<AnalyticFailure> failureOptionCategoryAnalytic;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final bool isFetching;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'CategoryAnalyticLoaderState(categoryAnalytic: $categoryAnalytic, failureOptionCategoryAnalytic: $failureOptionCategoryAnalytic, isFetching: $isFetching)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$CategoryAnalyticLoaderStateImpl &&
|
||||||
|
(identical(other.categoryAnalytic, categoryAnalytic) ||
|
||||||
|
other.categoryAnalytic == categoryAnalytic) &&
|
||||||
|
(identical(
|
||||||
|
other.failureOptionCategoryAnalytic,
|
||||||
|
failureOptionCategoryAnalytic,
|
||||||
|
) ||
|
||||||
|
other.failureOptionCategoryAnalytic ==
|
||||||
|
failureOptionCategoryAnalytic) &&
|
||||||
|
(identical(other.isFetching, isFetching) ||
|
||||||
|
other.isFetching == isFetching));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
runtimeType,
|
||||||
|
categoryAnalytic,
|
||||||
|
failureOptionCategoryAnalytic,
|
||||||
|
isFetching,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Create a copy of CategoryAnalyticLoaderState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$CategoryAnalyticLoaderStateImplCopyWith<_$CategoryAnalyticLoaderStateImpl>
|
||||||
|
get copyWith =>
|
||||||
|
__$$CategoryAnalyticLoaderStateImplCopyWithImpl<
|
||||||
|
_$CategoryAnalyticLoaderStateImpl
|
||||||
|
>(this, _$identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _CategoryAnalyticLoaderState
|
||||||
|
implements CategoryAnalyticLoaderState {
|
||||||
|
const factory _CategoryAnalyticLoaderState({
|
||||||
|
required final CategoryAnalytic categoryAnalytic,
|
||||||
|
required final Option<AnalyticFailure> failureOptionCategoryAnalytic,
|
||||||
|
final bool isFetching,
|
||||||
|
}) = _$CategoryAnalyticLoaderStateImpl;
|
||||||
|
|
||||||
|
@override
|
||||||
|
CategoryAnalytic get categoryAnalytic;
|
||||||
|
@override
|
||||||
|
Option<AnalyticFailure> get failureOptionCategoryAnalytic;
|
||||||
|
@override
|
||||||
|
bool get isFetching;
|
||||||
|
|
||||||
|
/// Create a copy of CategoryAnalyticLoaderState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$CategoryAnalyticLoaderStateImplCopyWith<_$CategoryAnalyticLoaderStateImpl>
|
||||||
|
get copyWith => throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
part of 'category_analytic_loader_bloc.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class CategoryAnalyticLoaderEvent with _$CategoryAnalyticLoaderEvent {
|
||||||
|
const factory CategoryAnalyticLoaderEvent.fetched() = _Fetched;
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
part of 'category_analytic_loader_bloc.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class CategoryAnalyticLoaderState with _$CategoryAnalyticLoaderState {
|
||||||
|
const factory CategoryAnalyticLoaderState({
|
||||||
|
required CategoryAnalytic categoryAnalytic,
|
||||||
|
required Option<AnalyticFailure> failureOptionCategoryAnalytic,
|
||||||
|
@Default(false) bool isFetching,
|
||||||
|
}) = _CategoryAnalyticLoaderState;
|
||||||
|
|
||||||
|
factory CategoryAnalyticLoaderState.initial() => CategoryAnalyticLoaderState(
|
||||||
|
categoryAnalytic: CategoryAnalytic.empty(),
|
||||||
|
failureOptionCategoryAnalytic: none(),
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -78,7 +78,7 @@ class AnalyticRemoteDataProvider {
|
|||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final response = await _apiClient.get(
|
final response = await _apiClient.get(
|
||||||
ApiPath.category,
|
ApiPath.categoryAnalytic,
|
||||||
params: {
|
params: {
|
||||||
'date_from': dateFrom.toServerDate,
|
'date_from': dateFrom.toServerDate,
|
||||||
'date_to': dateTo.toServerDate,
|
'date_to': dateTo.toServerDate,
|
||||||
|
|||||||
@ -9,6 +9,8 @@
|
|||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
|
|
||||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||||
|
import 'package:apskel_owner_flutter/application/analytic/category_analytic_loader/category_analytic_loader_bloc.dart'
|
||||||
|
as _i1038;
|
||||||
import 'package:apskel_owner_flutter/application/analytic/profit_loss_loader/profit_loss_loader_bloc.dart'
|
import 'package:apskel_owner_flutter/application/analytic/profit_loss_loader/profit_loss_loader_bloc.dart'
|
||||||
as _i11;
|
as _i11;
|
||||||
import 'package:apskel_owner_flutter/application/analytic/sales_loader/sales_loader_bloc.dart'
|
import 'package:apskel_owner_flutter/application/analytic/sales_loader/sales_loader_bloc.dart'
|
||||||
@ -145,6 +147,9 @@ extension GetItInjectableX on _i174.GetIt {
|
|||||||
gh.factory<_i11.ProfitLossLoaderBloc>(
|
gh.factory<_i11.ProfitLossLoaderBloc>(
|
||||||
() => _i11.ProfitLossLoaderBloc(gh<_i477.IAnalyticRepository>()),
|
() => _i11.ProfitLossLoaderBloc(gh<_i477.IAnalyticRepository>()),
|
||||||
);
|
);
|
||||||
|
gh.factory<_i1038.CategoryAnalyticLoaderBloc>(
|
||||||
|
() => _i1038.CategoryAnalyticLoaderBloc(gh<_i477.IAnalyticRepository>()),
|
||||||
|
);
|
||||||
gh.factory<_i775.LoginFormBloc>(
|
gh.factory<_i775.LoginFormBloc>(
|
||||||
() => _i775.LoginFormBloc(gh<_i49.IAuthRepository>()),
|
() => _i775.LoginFormBloc(gh<_i49.IAuthRepository>()),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:line_icons/line_icons.dart';
|
import 'package:line_icons/line_icons.dart';
|
||||||
|
|
||||||
|
import '../../../application/analytic/category_analytic_loader/category_analytic_loader_bloc.dart';
|
||||||
import '../../../application/analytic/profit_loss_loader/profit_loss_loader_bloc.dart';
|
import '../../../application/analytic/profit_loss_loader/profit_loss_loader_bloc.dart';
|
||||||
import '../../../common/extension/extension.dart';
|
import '../../../common/extension/extension.dart';
|
||||||
import '../../../common/theme/theme.dart';
|
import '../../../common/theme/theme.dart';
|
||||||
@ -23,9 +24,18 @@ class FinancePage extends StatefulWidget implements AutoRouteWrapper {
|
|||||||
State<FinancePage> createState() => _FinancePageState();
|
State<FinancePage> createState() => _FinancePageState();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget wrappedRoute(BuildContext context) => BlocProvider(
|
Widget wrappedRoute(BuildContext context) => MultiBlocProvider(
|
||||||
create: (_) =>
|
providers: [
|
||||||
getIt<ProfitLossLoaderBloc>()..add(ProfitLossLoaderEvent.fetched()),
|
BlocProvider(
|
||||||
|
create: (_) =>
|
||||||
|
getIt<ProfitLossLoaderBloc>()..add(ProfitLossLoaderEvent.fetched()),
|
||||||
|
),
|
||||||
|
BlocProvider(
|
||||||
|
create: (context) =>
|
||||||
|
getIt<CategoryAnalyticLoaderBloc>()
|
||||||
|
..add(CategoryAnalyticLoaderEvent.fetched()),
|
||||||
|
),
|
||||||
|
],
|
||||||
child: this,
|
child: this,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -149,11 +159,20 @@ class _FinancePageState extends State<FinancePage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
SliverToBoxAdapter(
|
BlocBuilder<
|
||||||
child: SlideTransition(
|
CategoryAnalyticLoaderBloc,
|
||||||
position: _slideAnimation,
|
CategoryAnalyticLoaderState
|
||||||
child: FinanceCategory(),
|
>(
|
||||||
),
|
builder: (context, stateCategory) {
|
||||||
|
return SliverToBoxAdapter(
|
||||||
|
child: SlideTransition(
|
||||||
|
position: _slideAnimation,
|
||||||
|
child: FinanceCategory(
|
||||||
|
categories: stateCategory.categoryAnalytic.data,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
// Product Analysis Section
|
// Product Analysis Section
|
||||||
|
|||||||
@ -1,33 +1,21 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:line_icons/line_icons.dart';
|
import 'package:line_icons/line_icons.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
import '../../../../common/extension/extension.dart';
|
||||||
import '../../../../common/theme/theme.dart';
|
import '../../../../common/theme/theme.dart';
|
||||||
|
import '../../../../domain/analytic/analytic.dart';
|
||||||
|
import '../../../components/widgets/empty_widget.dart';
|
||||||
|
|
||||||
class FinanceCategory extends StatelessWidget {
|
class FinanceCategory extends StatelessWidget {
|
||||||
const FinanceCategory({super.key});
|
final List<CategoryAnalyticItem> categories;
|
||||||
|
|
||||||
|
const FinanceCategory({super.key, required this.categories});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final categories = [
|
final totalRevenue = _calculateTotalRevenue();
|
||||||
{
|
final sortedCategories = _sortCategoriesByRevenue();
|
||||||
'name': 'Makanan & Minuman',
|
|
||||||
'amount': 'Rp 18.5M',
|
|
||||||
'percentage': 72,
|
|
||||||
'color': AppColor.primary,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'name': 'Produk Retail',
|
|
||||||
'amount': 'Rp 4.2M',
|
|
||||||
'percentage': 16,
|
|
||||||
'color': AppColor.secondary,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'name': 'Jasa & Lainnya',
|
|
||||||
'amount': 'Rp 3.1M',
|
|
||||||
'percentage': 12,
|
|
||||||
'color': AppColor.info,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.all(16),
|
margin: const EdgeInsets.all(16),
|
||||||
@ -70,25 +58,25 @@ class FinanceCategory extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
...categories
|
// Show empty state if no categories
|
||||||
.map(
|
if (categories.isEmpty)
|
||||||
(category) => _buildCategoryItem(
|
_buildEmptyState()
|
||||||
category['name'] as String,
|
else
|
||||||
category['amount'] as String,
|
...sortedCategories.asMap().entries.map(
|
||||||
category['percentage'] as int,
|
(entry) => _buildCategoryItem(
|
||||||
category['color'] as Color,
|
entry.value,
|
||||||
),
|
_calculatePercentage(entry.value.totalRevenue, totalRevenue),
|
||||||
)
|
_getCategoryColor(entry.key),
|
||||||
.toList(),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCategoryItem(
|
Widget _buildCategoryItem(
|
||||||
String name,
|
CategoryAnalyticItem category,
|
||||||
String amount,
|
double percentage,
|
||||||
int percentage,
|
|
||||||
Color color,
|
Color color,
|
||||||
) {
|
) {
|
||||||
return Container(
|
return Container(
|
||||||
@ -98,30 +86,59 @@ class FinanceCategory extends StatelessWidget {
|
|||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: color,
|
||||||
|
borderRadius: BorderRadius.circular(6),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
category.categoryName,
|
||||||
|
style: AppStyle.md.copyWith(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 2),
|
||||||
|
Text(
|
||||||
|
'${category.productCount} produk • ${category.orderCount} pesanan',
|
||||||
|
style: AppStyle.xs.copyWith(
|
||||||
|
color: AppColor.textSecondary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Text(
|
||||||
width: 12,
|
category.totalRevenue.currencyFormatRp,
|
||||||
height: 12,
|
style: AppStyle.md.copyWith(
|
||||||
decoration: BoxDecoration(
|
fontWeight: FontWeight.bold,
|
||||||
color: color,
|
color: color,
|
||||||
borderRadius: BorderRadius.circular(6),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
|
||||||
Text(
|
Text(
|
||||||
name,
|
'${NumberFormat('#,###', 'id_ID').format(category.totalQuantity)} unit',
|
||||||
style: AppStyle.md.copyWith(fontWeight: FontWeight.w600),
|
style: AppStyle.xs.copyWith(color: AppColor.textSecondary),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Text(
|
|
||||||
amount,
|
|
||||||
style: AppStyle.md.copyWith(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: color,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
@ -135,7 +152,7 @@ class FinanceCategory extends StatelessWidget {
|
|||||||
Align(
|
Align(
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: Text(
|
child: Text(
|
||||||
'$percentage%',
|
'${percentage.toStringAsFixed(1)}%',
|
||||||
style: AppStyle.xs.copyWith(color: AppColor.textSecondary),
|
style: AppStyle.xs.copyWith(color: AppColor.textSecondary),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -143,4 +160,48 @@ class FinanceCategory extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildEmptyState() {
|
||||||
|
return EmptyWidget(
|
||||||
|
title: 'Belum ada data kategori',
|
||||||
|
message: 'Data kategori penjualan akan muncul di sini',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper methods
|
||||||
|
int _calculateTotalRevenue() {
|
||||||
|
return categories.fold(0, (sum, category) => sum + category.totalRevenue);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<CategoryAnalyticItem> _sortCategoriesByRevenue() {
|
||||||
|
final sorted = List<CategoryAnalyticItem>.from(categories);
|
||||||
|
sorted.sort((a, b) => b.totalRevenue.compareTo(a.totalRevenue));
|
||||||
|
return sorted;
|
||||||
|
}
|
||||||
|
|
||||||
|
double _calculatePercentage(int categoryRevenue, int totalRevenue) {
|
||||||
|
if (totalRevenue == 0) return 0;
|
||||||
|
return (categoryRevenue / totalRevenue) * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color _getCategoryColor(int index) {
|
||||||
|
// Predefined color palette for categories
|
||||||
|
const colors = [
|
||||||
|
AppColor.primary,
|
||||||
|
AppColor.secondary,
|
||||||
|
AppColor.success,
|
||||||
|
AppColor.warning,
|
||||||
|
AppColor.error,
|
||||||
|
AppColor.info,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Generate additional colors if needed
|
||||||
|
if (index < colors.length) {
|
||||||
|
return colors[index];
|
||||||
|
} else {
|
||||||
|
// Generate colors based on index for unlimited categories
|
||||||
|
final hue = (index * 137.5) % 360; // Golden angle approximation
|
||||||
|
return HSLColor.fromAHSL(1.0, hue, 0.7, 0.5).toColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user