Compare commits
14 Commits
5387d7b7a6
...
577adb7964
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
577adb7964 | ||
|
|
b3c72cbbc0 | ||
|
|
8eef620c16 | ||
|
|
50b06da627 | ||
|
|
beb9ead4da | ||
|
|
6b1e56a46b | ||
|
|
d3543149f2 | ||
|
|
7d24b3296d | ||
|
|
82c0eaf5fe | ||
|
|
6cc2026f6f | ||
|
|
6df5380aa3 | ||
|
|
4a4f6388d7 | ||
|
|
f84090c0e6 | ||
|
|
e7525238fe |
@ -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(),
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
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 'profit_loss_loader_event.dart';
|
||||
part 'profit_loss_loader_state.dart';
|
||||
part 'profit_loss_loader_bloc.freezed.dart';
|
||||
|
||||
@injectable
|
||||
class ProfitLossLoaderBloc
|
||||
extends Bloc<ProfitLossLoaderEvent, ProfitLossLoaderState> {
|
||||
final IAnalyticRepository _repository;
|
||||
ProfitLossLoaderBloc(this._repository)
|
||||
: super(ProfitLossLoaderState.initial()) {
|
||||
on<ProfitLossLoaderEvent>(_onProfitLossLoaderEvent);
|
||||
}
|
||||
|
||||
Future<void> _onProfitLossLoaderEvent(
|
||||
ProfitLossLoaderEvent event,
|
||||
Emitter<ProfitLossLoaderState> emit,
|
||||
) {
|
||||
return event.map(
|
||||
fetched: (e) async {
|
||||
emit(state.copyWith(isFetching: true, failureOptionProfitLoss: none()));
|
||||
|
||||
final result = await _repository.getProfitLoss(
|
||||
dateFrom: DateTime.now().subtract(const Duration(days: 30)),
|
||||
dateTo: DateTime.now(),
|
||||
);
|
||||
|
||||
var data = result.fold(
|
||||
(f) => state.copyWith(failureOptionProfitLoss: optionOf(f)),
|
||||
(profitLoss) => state.copyWith(profitLoss: profitLoss),
|
||||
);
|
||||
|
||||
emit(data.copyWith(isFetching: false));
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,384 @@
|
||||
// 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 'profit_loss_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 _$ProfitLossLoaderEvent {
|
||||
@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 $ProfitLossLoaderEventCopyWith<$Res> {
|
||||
factory $ProfitLossLoaderEventCopyWith(
|
||||
ProfitLossLoaderEvent value,
|
||||
$Res Function(ProfitLossLoaderEvent) then,
|
||||
) = _$ProfitLossLoaderEventCopyWithImpl<$Res, ProfitLossLoaderEvent>;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$ProfitLossLoaderEventCopyWithImpl<
|
||||
$Res,
|
||||
$Val extends ProfitLossLoaderEvent
|
||||
>
|
||||
implements $ProfitLossLoaderEventCopyWith<$Res> {
|
||||
_$ProfitLossLoaderEventCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of ProfitLossLoaderEvent
|
||||
/// 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 _$ProfitLossLoaderEventCopyWithImpl<$Res, _$FetchedImpl>
|
||||
implements _$$FetchedImplCopyWith<$Res> {
|
||||
__$$FetchedImplCopyWithImpl(
|
||||
_$FetchedImpl _value,
|
||||
$Res Function(_$FetchedImpl) _then,
|
||||
) : super(_value, _then);
|
||||
|
||||
/// Create a copy of ProfitLossLoaderEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$FetchedImpl implements _Fetched {
|
||||
const _$FetchedImpl();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ProfitLossLoaderEvent.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 ProfitLossLoaderEvent {
|
||||
const factory _Fetched() = _$FetchedImpl;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ProfitLossLoaderState {
|
||||
ProfitLossAnalytic get profitLoss => throw _privateConstructorUsedError;
|
||||
Option<AnalyticFailure> get failureOptionProfitLoss =>
|
||||
throw _privateConstructorUsedError;
|
||||
bool get isFetching => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of ProfitLossLoaderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$ProfitLossLoaderStateCopyWith<ProfitLossLoaderState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $ProfitLossLoaderStateCopyWith<$Res> {
|
||||
factory $ProfitLossLoaderStateCopyWith(
|
||||
ProfitLossLoaderState value,
|
||||
$Res Function(ProfitLossLoaderState) then,
|
||||
) = _$ProfitLossLoaderStateCopyWithImpl<$Res, ProfitLossLoaderState>;
|
||||
@useResult
|
||||
$Res call({
|
||||
ProfitLossAnalytic profitLoss,
|
||||
Option<AnalyticFailure> failureOptionProfitLoss,
|
||||
bool isFetching,
|
||||
});
|
||||
|
||||
$ProfitLossAnalyticCopyWith<$Res> get profitLoss;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$ProfitLossLoaderStateCopyWithImpl<
|
||||
$Res,
|
||||
$Val extends ProfitLossLoaderState
|
||||
>
|
||||
implements $ProfitLossLoaderStateCopyWith<$Res> {
|
||||
_$ProfitLossLoaderStateCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of ProfitLossLoaderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? profitLoss = null,
|
||||
Object? failureOptionProfitLoss = null,
|
||||
Object? isFetching = null,
|
||||
}) {
|
||||
return _then(
|
||||
_value.copyWith(
|
||||
profitLoss: null == profitLoss
|
||||
? _value.profitLoss
|
||||
: profitLoss // ignore: cast_nullable_to_non_nullable
|
||||
as ProfitLossAnalytic,
|
||||
failureOptionProfitLoss: null == failureOptionProfitLoss
|
||||
? _value.failureOptionProfitLoss
|
||||
: failureOptionProfitLoss // 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 ProfitLossLoaderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$ProfitLossAnalyticCopyWith<$Res> get profitLoss {
|
||||
return $ProfitLossAnalyticCopyWith<$Res>(_value.profitLoss, (value) {
|
||||
return _then(_value.copyWith(profitLoss: value) as $Val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$ProfitLossLoaderStateImplCopyWith<$Res>
|
||||
implements $ProfitLossLoaderStateCopyWith<$Res> {
|
||||
factory _$$ProfitLossLoaderStateImplCopyWith(
|
||||
_$ProfitLossLoaderStateImpl value,
|
||||
$Res Function(_$ProfitLossLoaderStateImpl) then,
|
||||
) = __$$ProfitLossLoaderStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({
|
||||
ProfitLossAnalytic profitLoss,
|
||||
Option<AnalyticFailure> failureOptionProfitLoss,
|
||||
bool isFetching,
|
||||
});
|
||||
|
||||
@override
|
||||
$ProfitLossAnalyticCopyWith<$Res> get profitLoss;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$ProfitLossLoaderStateImplCopyWithImpl<$Res>
|
||||
extends
|
||||
_$ProfitLossLoaderStateCopyWithImpl<$Res, _$ProfitLossLoaderStateImpl>
|
||||
implements _$$ProfitLossLoaderStateImplCopyWith<$Res> {
|
||||
__$$ProfitLossLoaderStateImplCopyWithImpl(
|
||||
_$ProfitLossLoaderStateImpl _value,
|
||||
$Res Function(_$ProfitLossLoaderStateImpl) _then,
|
||||
) : super(_value, _then);
|
||||
|
||||
/// Create a copy of ProfitLossLoaderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? profitLoss = null,
|
||||
Object? failureOptionProfitLoss = null,
|
||||
Object? isFetching = null,
|
||||
}) {
|
||||
return _then(
|
||||
_$ProfitLossLoaderStateImpl(
|
||||
profitLoss: null == profitLoss
|
||||
? _value.profitLoss
|
||||
: profitLoss // ignore: cast_nullable_to_non_nullable
|
||||
as ProfitLossAnalytic,
|
||||
failureOptionProfitLoss: null == failureOptionProfitLoss
|
||||
? _value.failureOptionProfitLoss
|
||||
: failureOptionProfitLoss // 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 _$ProfitLossLoaderStateImpl implements _ProfitLossLoaderState {
|
||||
const _$ProfitLossLoaderStateImpl({
|
||||
required this.profitLoss,
|
||||
required this.failureOptionProfitLoss,
|
||||
this.isFetching = false,
|
||||
});
|
||||
|
||||
@override
|
||||
final ProfitLossAnalytic profitLoss;
|
||||
@override
|
||||
final Option<AnalyticFailure> failureOptionProfitLoss;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool isFetching;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ProfitLossLoaderState(profitLoss: $profitLoss, failureOptionProfitLoss: $failureOptionProfitLoss, isFetching: $isFetching)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$ProfitLossLoaderStateImpl &&
|
||||
(identical(other.profitLoss, profitLoss) ||
|
||||
other.profitLoss == profitLoss) &&
|
||||
(identical(
|
||||
other.failureOptionProfitLoss,
|
||||
failureOptionProfitLoss,
|
||||
) ||
|
||||
other.failureOptionProfitLoss == failureOptionProfitLoss) &&
|
||||
(identical(other.isFetching, isFetching) ||
|
||||
other.isFetching == isFetching));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, profitLoss, failureOptionProfitLoss, isFetching);
|
||||
|
||||
/// Create a copy of ProfitLossLoaderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ProfitLossLoaderStateImplCopyWith<_$ProfitLossLoaderStateImpl>
|
||||
get copyWith =>
|
||||
__$$ProfitLossLoaderStateImplCopyWithImpl<_$ProfitLossLoaderStateImpl>(
|
||||
this,
|
||||
_$identity,
|
||||
);
|
||||
}
|
||||
|
||||
abstract class _ProfitLossLoaderState implements ProfitLossLoaderState {
|
||||
const factory _ProfitLossLoaderState({
|
||||
required final ProfitLossAnalytic profitLoss,
|
||||
required final Option<AnalyticFailure> failureOptionProfitLoss,
|
||||
final bool isFetching,
|
||||
}) = _$ProfitLossLoaderStateImpl;
|
||||
|
||||
@override
|
||||
ProfitLossAnalytic get profitLoss;
|
||||
@override
|
||||
Option<AnalyticFailure> get failureOptionProfitLoss;
|
||||
@override
|
||||
bool get isFetching;
|
||||
|
||||
/// Create a copy of ProfitLossLoaderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$ProfitLossLoaderStateImplCopyWith<_$ProfitLossLoaderStateImpl>
|
||||
get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
part of 'profit_loss_loader_bloc.dart';
|
||||
|
||||
@freezed
|
||||
class ProfitLossLoaderEvent with _$ProfitLossLoaderEvent {
|
||||
const factory ProfitLossLoaderEvent.fetched() = _Fetched;
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
part of 'profit_loss_loader_bloc.dart';
|
||||
|
||||
@freezed
|
||||
class ProfitLossLoaderState with _$ProfitLossLoaderState {
|
||||
const factory ProfitLossLoaderState({
|
||||
required ProfitLossAnalytic profitLoss,
|
||||
required Option<AnalyticFailure> failureOptionProfitLoss,
|
||||
@Default(false) bool isFetching,
|
||||
}) = _ProfitLossLoaderState;
|
||||
|
||||
factory ProfitLossLoaderState.initial() => ProfitLossLoaderState(
|
||||
profitLoss: ProfitLossAnalytic.empty(),
|
||||
failureOptionProfitLoss: none(),
|
||||
);
|
||||
}
|
||||
39
lib/application/analytic/sales_loader/sales_loader_bloc.dart
Normal file
39
lib/application/analytic/sales_loader/sales_loader_bloc.dart
Normal file
@ -0,0 +1,39 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:dartz/dartz.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 'sales_loader_event.dart';
|
||||
part 'sales_loader_state.dart';
|
||||
part 'sales_loader_bloc.freezed.dart';
|
||||
|
||||
@injectable
|
||||
class SalesLoaderBloc extends Bloc<SalesLoaderEvent, SalesLoaderState> {
|
||||
final IAnalyticRepository _analyticRepository;
|
||||
SalesLoaderBloc(this._analyticRepository)
|
||||
: super(SalesLoaderState.initial()) {
|
||||
on<SalesLoaderEvent>(_onSalesLoaderEvent);
|
||||
}
|
||||
|
||||
Future<void> _onSalesLoaderEvent(
|
||||
SalesLoaderEvent event,
|
||||
Emitter<SalesLoaderState> emit,
|
||||
) async {
|
||||
emit(state.copyWith(isFetching: true, failureOptionSales: none()));
|
||||
|
||||
final result = await _analyticRepository.getSales(
|
||||
dateFrom: DateTime.now().subtract(const Duration(days: 30)),
|
||||
dateTo: DateTime.now(),
|
||||
);
|
||||
|
||||
var data = result.fold(
|
||||
(f) => state.copyWith(failureOptionSales: optionOf(f)),
|
||||
(sales) => state.copyWith(sales: sales),
|
||||
);
|
||||
|
||||
emit(data.copyWith(isFetching: false));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,376 @@
|
||||
// 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 'sales_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 _$SalesLoaderEvent {
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function() fectched,
|
||||
}) => throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function()? fectched,
|
||||
}) => throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function()? fectched,
|
||||
required TResult orElse(),
|
||||
}) => throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(_Fectched value) fectched,
|
||||
}) => throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(_Fectched value)? fectched,
|
||||
}) => throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(_Fectched value)? fectched,
|
||||
required TResult orElse(),
|
||||
}) => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $SalesLoaderEventCopyWith<$Res> {
|
||||
factory $SalesLoaderEventCopyWith(
|
||||
SalesLoaderEvent value,
|
||||
$Res Function(SalesLoaderEvent) then,
|
||||
) = _$SalesLoaderEventCopyWithImpl<$Res, SalesLoaderEvent>;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$SalesLoaderEventCopyWithImpl<$Res, $Val extends SalesLoaderEvent>
|
||||
implements $SalesLoaderEventCopyWith<$Res> {
|
||||
_$SalesLoaderEventCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of SalesLoaderEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$FectchedImplCopyWith<$Res> {
|
||||
factory _$$FectchedImplCopyWith(
|
||||
_$FectchedImpl value,
|
||||
$Res Function(_$FectchedImpl) then,
|
||||
) = __$$FectchedImplCopyWithImpl<$Res>;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$FectchedImplCopyWithImpl<$Res>
|
||||
extends _$SalesLoaderEventCopyWithImpl<$Res, _$FectchedImpl>
|
||||
implements _$$FectchedImplCopyWith<$Res> {
|
||||
__$$FectchedImplCopyWithImpl(
|
||||
_$FectchedImpl _value,
|
||||
$Res Function(_$FectchedImpl) _then,
|
||||
) : super(_value, _then);
|
||||
|
||||
/// Create a copy of SalesLoaderEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$FectchedImpl implements _Fectched {
|
||||
const _$FectchedImpl();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SalesLoaderEvent.fectched()';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType && other is _$FectchedImpl);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => runtimeType.hashCode;
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function() fectched,
|
||||
}) {
|
||||
return fectched();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function()? fectched,
|
||||
}) {
|
||||
return fectched?.call();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function()? fectched,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (fectched != null) {
|
||||
return fectched();
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(_Fectched value) fectched,
|
||||
}) {
|
||||
return fectched(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(_Fectched value)? fectched,
|
||||
}) {
|
||||
return fectched?.call(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(_Fectched value)? fectched,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (fectched != null) {
|
||||
return fectched(this);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _Fectched implements SalesLoaderEvent {
|
||||
const factory _Fectched() = _$FectchedImpl;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$SalesLoaderState {
|
||||
SalesAnalytic get sales => throw _privateConstructorUsedError;
|
||||
Option<AnalyticFailure> get failureOptionSales =>
|
||||
throw _privateConstructorUsedError;
|
||||
bool get isFetching => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of SalesLoaderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$SalesLoaderStateCopyWith<SalesLoaderState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $SalesLoaderStateCopyWith<$Res> {
|
||||
factory $SalesLoaderStateCopyWith(
|
||||
SalesLoaderState value,
|
||||
$Res Function(SalesLoaderState) then,
|
||||
) = _$SalesLoaderStateCopyWithImpl<$Res, SalesLoaderState>;
|
||||
@useResult
|
||||
$Res call({
|
||||
SalesAnalytic sales,
|
||||
Option<AnalyticFailure> failureOptionSales,
|
||||
bool isFetching,
|
||||
});
|
||||
|
||||
$SalesAnalyticCopyWith<$Res> get sales;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$SalesLoaderStateCopyWithImpl<$Res, $Val extends SalesLoaderState>
|
||||
implements $SalesLoaderStateCopyWith<$Res> {
|
||||
_$SalesLoaderStateCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of SalesLoaderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? sales = null,
|
||||
Object? failureOptionSales = null,
|
||||
Object? isFetching = null,
|
||||
}) {
|
||||
return _then(
|
||||
_value.copyWith(
|
||||
sales: null == sales
|
||||
? _value.sales
|
||||
: sales // ignore: cast_nullable_to_non_nullable
|
||||
as SalesAnalytic,
|
||||
failureOptionSales: null == failureOptionSales
|
||||
? _value.failureOptionSales
|
||||
: failureOptionSales // 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 SalesLoaderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SalesAnalyticCopyWith<$Res> get sales {
|
||||
return $SalesAnalyticCopyWith<$Res>(_value.sales, (value) {
|
||||
return _then(_value.copyWith(sales: value) as $Val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$SalesLoaderStateImplCopyWith<$Res>
|
||||
implements $SalesLoaderStateCopyWith<$Res> {
|
||||
factory _$$SalesLoaderStateImplCopyWith(
|
||||
_$SalesLoaderStateImpl value,
|
||||
$Res Function(_$SalesLoaderStateImpl) then,
|
||||
) = __$$SalesLoaderStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({
|
||||
SalesAnalytic sales,
|
||||
Option<AnalyticFailure> failureOptionSales,
|
||||
bool isFetching,
|
||||
});
|
||||
|
||||
@override
|
||||
$SalesAnalyticCopyWith<$Res> get sales;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$SalesLoaderStateImplCopyWithImpl<$Res>
|
||||
extends _$SalesLoaderStateCopyWithImpl<$Res, _$SalesLoaderStateImpl>
|
||||
implements _$$SalesLoaderStateImplCopyWith<$Res> {
|
||||
__$$SalesLoaderStateImplCopyWithImpl(
|
||||
_$SalesLoaderStateImpl _value,
|
||||
$Res Function(_$SalesLoaderStateImpl) _then,
|
||||
) : super(_value, _then);
|
||||
|
||||
/// Create a copy of SalesLoaderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? sales = null,
|
||||
Object? failureOptionSales = null,
|
||||
Object? isFetching = null,
|
||||
}) {
|
||||
return _then(
|
||||
_$SalesLoaderStateImpl(
|
||||
sales: null == sales
|
||||
? _value.sales
|
||||
: sales // ignore: cast_nullable_to_non_nullable
|
||||
as SalesAnalytic,
|
||||
failureOptionSales: null == failureOptionSales
|
||||
? _value.failureOptionSales
|
||||
: failureOptionSales // 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 _$SalesLoaderStateImpl implements _SalesLoaderState {
|
||||
const _$SalesLoaderStateImpl({
|
||||
required this.sales,
|
||||
required this.failureOptionSales,
|
||||
this.isFetching = false,
|
||||
});
|
||||
|
||||
@override
|
||||
final SalesAnalytic sales;
|
||||
@override
|
||||
final Option<AnalyticFailure> failureOptionSales;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool isFetching;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SalesLoaderState(sales: $sales, failureOptionSales: $failureOptionSales, isFetching: $isFetching)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$SalesLoaderStateImpl &&
|
||||
(identical(other.sales, sales) || other.sales == sales) &&
|
||||
(identical(other.failureOptionSales, failureOptionSales) ||
|
||||
other.failureOptionSales == failureOptionSales) &&
|
||||
(identical(other.isFetching, isFetching) ||
|
||||
other.isFetching == isFetching));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, sales, failureOptionSales, isFetching);
|
||||
|
||||
/// Create a copy of SalesLoaderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$SalesLoaderStateImplCopyWith<_$SalesLoaderStateImpl> get copyWith =>
|
||||
__$$SalesLoaderStateImplCopyWithImpl<_$SalesLoaderStateImpl>(
|
||||
this,
|
||||
_$identity,
|
||||
);
|
||||
}
|
||||
|
||||
abstract class _SalesLoaderState implements SalesLoaderState {
|
||||
const factory _SalesLoaderState({
|
||||
required final SalesAnalytic sales,
|
||||
required final Option<AnalyticFailure> failureOptionSales,
|
||||
final bool isFetching,
|
||||
}) = _$SalesLoaderStateImpl;
|
||||
|
||||
@override
|
||||
SalesAnalytic get sales;
|
||||
@override
|
||||
Option<AnalyticFailure> get failureOptionSales;
|
||||
@override
|
||||
bool get isFetching;
|
||||
|
||||
/// Create a copy of SalesLoaderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$SalesLoaderStateImplCopyWith<_$SalesLoaderStateImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
part of 'sales_loader_bloc.dart';
|
||||
|
||||
@freezed
|
||||
class SalesLoaderEvent with _$SalesLoaderEvent {
|
||||
const factory SalesLoaderEvent.fectched() = _Fectched;
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
part of 'sales_loader_bloc.dart';
|
||||
|
||||
@freezed
|
||||
class SalesLoaderState with _$SalesLoaderState {
|
||||
const factory SalesLoaderState({
|
||||
required SalesAnalytic sales,
|
||||
required Option<AnalyticFailure> failureOptionSales,
|
||||
@Default(false) bool isFetching,
|
||||
}) = _SalesLoaderState;
|
||||
|
||||
factory SalesLoaderState.initial() => SalesLoaderState(
|
||||
sales: SalesAnalytic.empty(),
|
||||
failureOptionSales: none(),
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
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/category/category.dart';
|
||||
|
||||
part 'category_loader_event.dart';
|
||||
part 'category_loader_state.dart';
|
||||
part 'category_loader_bloc.freezed.dart';
|
||||
|
||||
@injectable
|
||||
class CategoryLoaderBloc
|
||||
extends Bloc<CategoryLoaderEvent, CategoryLoaderState> {
|
||||
final ICategoryRepository _repository;
|
||||
CategoryLoaderBloc(this._repository) : super(CategoryLoaderState.initial()) {
|
||||
on<CategoryLoaderEvent>(_onCategoryLoaderEvent);
|
||||
}
|
||||
|
||||
Future<void> _onCategoryLoaderEvent(
|
||||
CategoryLoaderEvent event,
|
||||
Emitter<CategoryLoaderState> emit,
|
||||
) {
|
||||
return event.map(
|
||||
fetched: (e) async {
|
||||
emit(state.copyWith(isFetching: true, failureOptionCategory: none()));
|
||||
|
||||
final result = await _repository.get();
|
||||
|
||||
result.fold(
|
||||
(f) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
isFetching: false,
|
||||
failureOptionCategory: optionOf(f),
|
||||
),
|
||||
);
|
||||
},
|
||||
(categories) {
|
||||
// tambahkan "All Data" di awal list
|
||||
final updatedCategories = [Category.addAllData(), ...categories];
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
isFetching: false,
|
||||
categories: updatedCategories,
|
||||
failureOptionCategory: none(),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,370 @@
|
||||
// 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_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 _$CategoryLoaderEvent {
|
||||
@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 $CategoryLoaderEventCopyWith<$Res> {
|
||||
factory $CategoryLoaderEventCopyWith(
|
||||
CategoryLoaderEvent value,
|
||||
$Res Function(CategoryLoaderEvent) then,
|
||||
) = _$CategoryLoaderEventCopyWithImpl<$Res, CategoryLoaderEvent>;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$CategoryLoaderEventCopyWithImpl<$Res, $Val extends CategoryLoaderEvent>
|
||||
implements $CategoryLoaderEventCopyWith<$Res> {
|
||||
_$CategoryLoaderEventCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of CategoryLoaderEvent
|
||||
/// 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 _$CategoryLoaderEventCopyWithImpl<$Res, _$FetchedImpl>
|
||||
implements _$$FetchedImplCopyWith<$Res> {
|
||||
__$$FetchedImplCopyWithImpl(
|
||||
_$FetchedImpl _value,
|
||||
$Res Function(_$FetchedImpl) _then,
|
||||
) : super(_value, _then);
|
||||
|
||||
/// Create a copy of CategoryLoaderEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$FetchedImpl implements _Fetched {
|
||||
const _$FetchedImpl();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CategoryLoaderEvent.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 CategoryLoaderEvent {
|
||||
const factory _Fetched() = _$FetchedImpl;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$CategoryLoaderState {
|
||||
List<Category> get categories => throw _privateConstructorUsedError;
|
||||
Option<CategoryFailure> get failureOptionCategory =>
|
||||
throw _privateConstructorUsedError;
|
||||
bool get isFetching => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of CategoryLoaderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$CategoryLoaderStateCopyWith<CategoryLoaderState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $CategoryLoaderStateCopyWith<$Res> {
|
||||
factory $CategoryLoaderStateCopyWith(
|
||||
CategoryLoaderState value,
|
||||
$Res Function(CategoryLoaderState) then,
|
||||
) = _$CategoryLoaderStateCopyWithImpl<$Res, CategoryLoaderState>;
|
||||
@useResult
|
||||
$Res call({
|
||||
List<Category> categories,
|
||||
Option<CategoryFailure> failureOptionCategory,
|
||||
bool isFetching,
|
||||
});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$CategoryLoaderStateCopyWithImpl<$Res, $Val extends CategoryLoaderState>
|
||||
implements $CategoryLoaderStateCopyWith<$Res> {
|
||||
_$CategoryLoaderStateCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of CategoryLoaderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? categories = null,
|
||||
Object? failureOptionCategory = null,
|
||||
Object? isFetching = null,
|
||||
}) {
|
||||
return _then(
|
||||
_value.copyWith(
|
||||
categories: null == categories
|
||||
? _value.categories
|
||||
: categories // ignore: cast_nullable_to_non_nullable
|
||||
as List<Category>,
|
||||
failureOptionCategory: null == failureOptionCategory
|
||||
? _value.failureOptionCategory
|
||||
: failureOptionCategory // ignore: cast_nullable_to_non_nullable
|
||||
as Option<CategoryFailure>,
|
||||
isFetching: null == isFetching
|
||||
? _value.isFetching
|
||||
: isFetching // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
)
|
||||
as $Val,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$CategoryLoaderStateImplCopyWith<$Res>
|
||||
implements $CategoryLoaderStateCopyWith<$Res> {
|
||||
factory _$$CategoryLoaderStateImplCopyWith(
|
||||
_$CategoryLoaderStateImpl value,
|
||||
$Res Function(_$CategoryLoaderStateImpl) then,
|
||||
) = __$$CategoryLoaderStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({
|
||||
List<Category> categories,
|
||||
Option<CategoryFailure> failureOptionCategory,
|
||||
bool isFetching,
|
||||
});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$CategoryLoaderStateImplCopyWithImpl<$Res>
|
||||
extends _$CategoryLoaderStateCopyWithImpl<$Res, _$CategoryLoaderStateImpl>
|
||||
implements _$$CategoryLoaderStateImplCopyWith<$Res> {
|
||||
__$$CategoryLoaderStateImplCopyWithImpl(
|
||||
_$CategoryLoaderStateImpl _value,
|
||||
$Res Function(_$CategoryLoaderStateImpl) _then,
|
||||
) : super(_value, _then);
|
||||
|
||||
/// Create a copy of CategoryLoaderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? categories = null,
|
||||
Object? failureOptionCategory = null,
|
||||
Object? isFetching = null,
|
||||
}) {
|
||||
return _then(
|
||||
_$CategoryLoaderStateImpl(
|
||||
categories: null == categories
|
||||
? _value._categories
|
||||
: categories // ignore: cast_nullable_to_non_nullable
|
||||
as List<Category>,
|
||||
failureOptionCategory: null == failureOptionCategory
|
||||
? _value.failureOptionCategory
|
||||
: failureOptionCategory // ignore: cast_nullable_to_non_nullable
|
||||
as Option<CategoryFailure>,
|
||||
isFetching: null == isFetching
|
||||
? _value.isFetching
|
||||
: isFetching // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$CategoryLoaderStateImpl implements _CategoryLoaderState {
|
||||
const _$CategoryLoaderStateImpl({
|
||||
required final List<Category> categories,
|
||||
required this.failureOptionCategory,
|
||||
this.isFetching = false,
|
||||
}) : _categories = categories;
|
||||
|
||||
final List<Category> _categories;
|
||||
@override
|
||||
List<Category> get categories {
|
||||
if (_categories is EqualUnmodifiableListView) return _categories;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_categories);
|
||||
}
|
||||
|
||||
@override
|
||||
final Option<CategoryFailure> failureOptionCategory;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool isFetching;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CategoryLoaderState(categories: $categories, failureOptionCategory: $failureOptionCategory, isFetching: $isFetching)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$CategoryLoaderStateImpl &&
|
||||
const DeepCollectionEquality().equals(
|
||||
other._categories,
|
||||
_categories,
|
||||
) &&
|
||||
(identical(other.failureOptionCategory, failureOptionCategory) ||
|
||||
other.failureOptionCategory == failureOptionCategory) &&
|
||||
(identical(other.isFetching, isFetching) ||
|
||||
other.isFetching == isFetching));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(_categories),
|
||||
failureOptionCategory,
|
||||
isFetching,
|
||||
);
|
||||
|
||||
/// Create a copy of CategoryLoaderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$CategoryLoaderStateImplCopyWith<_$CategoryLoaderStateImpl> get copyWith =>
|
||||
__$$CategoryLoaderStateImplCopyWithImpl<_$CategoryLoaderStateImpl>(
|
||||
this,
|
||||
_$identity,
|
||||
);
|
||||
}
|
||||
|
||||
abstract class _CategoryLoaderState implements CategoryLoaderState {
|
||||
const factory _CategoryLoaderState({
|
||||
required final List<Category> categories,
|
||||
required final Option<CategoryFailure> failureOptionCategory,
|
||||
final bool isFetching,
|
||||
}) = _$CategoryLoaderStateImpl;
|
||||
|
||||
@override
|
||||
List<Category> get categories;
|
||||
@override
|
||||
Option<CategoryFailure> get failureOptionCategory;
|
||||
@override
|
||||
bool get isFetching;
|
||||
|
||||
/// Create a copy of CategoryLoaderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$CategoryLoaderStateImplCopyWith<_$CategoryLoaderStateImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
part of 'category_loader_bloc.dart';
|
||||
|
||||
@freezed
|
||||
class CategoryLoaderEvent with _$CategoryLoaderEvent {
|
||||
const factory CategoryLoaderEvent.fetched() = _Fetched;
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
part of 'category_loader_bloc.dart';
|
||||
|
||||
@freezed
|
||||
class CategoryLoaderState with _$CategoryLoaderState {
|
||||
const factory CategoryLoaderState({
|
||||
required List<Category> categories,
|
||||
required Option<CategoryFailure> failureOptionCategory,
|
||||
@Default(false) bool isFetching,
|
||||
}) = _CategoryLoaderState;
|
||||
|
||||
factory CategoryLoaderState.initial() =>
|
||||
CategoryLoaderState(categories: [], failureOptionCategory: none());
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
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/product/product.dart';
|
||||
|
||||
part 'product_loader_event.dart';
|
||||
part 'product_loader_state.dart';
|
||||
part 'product_loader_bloc.freezed.dart';
|
||||
|
||||
@injectable
|
||||
class ProductLoaderBloc extends Bloc<ProductLoaderEvent, ProductLoaderState> {
|
||||
final IProductRepository _productRepository;
|
||||
|
||||
ProductLoaderBloc(this._productRepository)
|
||||
: super(ProductLoaderState.initial()) {
|
||||
on<ProductLoaderEvent>(_onProductLoaderEvent);
|
||||
}
|
||||
|
||||
Future<void> _onProductLoaderEvent(
|
||||
ProductLoaderEvent event,
|
||||
Emitter<ProductLoaderState> emit,
|
||||
) {
|
||||
return event.map(
|
||||
categoryIdChanged: (e) async {
|
||||
emit(state.copyWith(categoryId: e.categoryId));
|
||||
},
|
||||
searchChanged: (e) async {
|
||||
emit(state.copyWith(search: e.search));
|
||||
},
|
||||
fetched: (e) async {
|
||||
var newState = state;
|
||||
|
||||
if (e.isRefresh) {
|
||||
newState = state.copyWith(isFetching: true);
|
||||
|
||||
emit(newState);
|
||||
}
|
||||
|
||||
newState = await _mapFetchedToState(state, isRefresh: e.isRefresh);
|
||||
|
||||
emit(newState);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<ProductLoaderState> _mapFetchedToState(
|
||||
ProductLoaderState state, {
|
||||
bool isRefresh = false,
|
||||
}) async {
|
||||
state = state.copyWith(isFetching: false);
|
||||
|
||||
if (state.hasReachedMax && state.products.isNotEmpty && !isRefresh) {
|
||||
return state;
|
||||
}
|
||||
|
||||
if (isRefresh) {
|
||||
state = state.copyWith(
|
||||
page: 1,
|
||||
failureOptionProduct: none(),
|
||||
hasReachedMax: false,
|
||||
products: [],
|
||||
);
|
||||
}
|
||||
|
||||
final failureOrProduct = await _productRepository.get(
|
||||
categoryId: state.categoryId,
|
||||
page: state.page,
|
||||
search: state.search,
|
||||
);
|
||||
|
||||
state = failureOrProduct.fold(
|
||||
(f) {
|
||||
if (state.products.isNotEmpty) {
|
||||
return state.copyWith(hasReachedMax: true);
|
||||
}
|
||||
return state.copyWith(failureOptionProduct: optionOf(f));
|
||||
},
|
||||
(products) {
|
||||
return state.copyWith(
|
||||
products: List.from(state.products)..addAll(products),
|
||||
failureOptionProduct: none(),
|
||||
page: state.page + 1,
|
||||
hasReachedMax: products.length < 10,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,821 @@
|
||||
// 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 'product_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 _$ProductLoaderEvent {
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function(String categoryId) categoryIdChanged,
|
||||
required TResult Function(String search) searchChanged,
|
||||
required TResult Function(bool isRefresh) fetched,
|
||||
}) => throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function(String categoryId)? categoryIdChanged,
|
||||
TResult? Function(String search)? searchChanged,
|
||||
TResult? Function(bool isRefresh)? fetched,
|
||||
}) => throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function(String categoryId)? categoryIdChanged,
|
||||
TResult Function(String search)? searchChanged,
|
||||
TResult Function(bool isRefresh)? fetched,
|
||||
required TResult orElse(),
|
||||
}) => throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(_CategoryIdChanged value) categoryIdChanged,
|
||||
required TResult Function(_SearchChanged value) searchChanged,
|
||||
required TResult Function(_Fetched value) fetched,
|
||||
}) => throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(_CategoryIdChanged value)? categoryIdChanged,
|
||||
TResult? Function(_SearchChanged value)? searchChanged,
|
||||
TResult? Function(_Fetched value)? fetched,
|
||||
}) => throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(_CategoryIdChanged value)? categoryIdChanged,
|
||||
TResult Function(_SearchChanged value)? searchChanged,
|
||||
TResult Function(_Fetched value)? fetched,
|
||||
required TResult orElse(),
|
||||
}) => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $ProductLoaderEventCopyWith<$Res> {
|
||||
factory $ProductLoaderEventCopyWith(
|
||||
ProductLoaderEvent value,
|
||||
$Res Function(ProductLoaderEvent) then,
|
||||
) = _$ProductLoaderEventCopyWithImpl<$Res, ProductLoaderEvent>;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$ProductLoaderEventCopyWithImpl<$Res, $Val extends ProductLoaderEvent>
|
||||
implements $ProductLoaderEventCopyWith<$Res> {
|
||||
_$ProductLoaderEventCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of ProductLoaderEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$CategoryIdChangedImplCopyWith<$Res> {
|
||||
factory _$$CategoryIdChangedImplCopyWith(
|
||||
_$CategoryIdChangedImpl value,
|
||||
$Res Function(_$CategoryIdChangedImpl) then,
|
||||
) = __$$CategoryIdChangedImplCopyWithImpl<$Res>;
|
||||
@useResult
|
||||
$Res call({String categoryId});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$CategoryIdChangedImplCopyWithImpl<$Res>
|
||||
extends _$ProductLoaderEventCopyWithImpl<$Res, _$CategoryIdChangedImpl>
|
||||
implements _$$CategoryIdChangedImplCopyWith<$Res> {
|
||||
__$$CategoryIdChangedImplCopyWithImpl(
|
||||
_$CategoryIdChangedImpl _value,
|
||||
$Res Function(_$CategoryIdChangedImpl) _then,
|
||||
) : super(_value, _then);
|
||||
|
||||
/// Create a copy of ProductLoaderEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({Object? categoryId = null}) {
|
||||
return _then(
|
||||
_$CategoryIdChangedImpl(
|
||||
null == categoryId
|
||||
? _value.categoryId
|
||||
: categoryId // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$CategoryIdChangedImpl implements _CategoryIdChanged {
|
||||
const _$CategoryIdChangedImpl(this.categoryId);
|
||||
|
||||
@override
|
||||
final String categoryId;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ProductLoaderEvent.categoryIdChanged(categoryId: $categoryId)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$CategoryIdChangedImpl &&
|
||||
(identical(other.categoryId, categoryId) ||
|
||||
other.categoryId == categoryId));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, categoryId);
|
||||
|
||||
/// Create a copy of ProductLoaderEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$CategoryIdChangedImplCopyWith<_$CategoryIdChangedImpl> get copyWith =>
|
||||
__$$CategoryIdChangedImplCopyWithImpl<_$CategoryIdChangedImpl>(
|
||||
this,
|
||||
_$identity,
|
||||
);
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function(String categoryId) categoryIdChanged,
|
||||
required TResult Function(String search) searchChanged,
|
||||
required TResult Function(bool isRefresh) fetched,
|
||||
}) {
|
||||
return categoryIdChanged(categoryId);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function(String categoryId)? categoryIdChanged,
|
||||
TResult? Function(String search)? searchChanged,
|
||||
TResult? Function(bool isRefresh)? fetched,
|
||||
}) {
|
||||
return categoryIdChanged?.call(categoryId);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function(String categoryId)? categoryIdChanged,
|
||||
TResult Function(String search)? searchChanged,
|
||||
TResult Function(bool isRefresh)? fetched,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (categoryIdChanged != null) {
|
||||
return categoryIdChanged(categoryId);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(_CategoryIdChanged value) categoryIdChanged,
|
||||
required TResult Function(_SearchChanged value) searchChanged,
|
||||
required TResult Function(_Fetched value) fetched,
|
||||
}) {
|
||||
return categoryIdChanged(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(_CategoryIdChanged value)? categoryIdChanged,
|
||||
TResult? Function(_SearchChanged value)? searchChanged,
|
||||
TResult? Function(_Fetched value)? fetched,
|
||||
}) {
|
||||
return categoryIdChanged?.call(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(_CategoryIdChanged value)? categoryIdChanged,
|
||||
TResult Function(_SearchChanged value)? searchChanged,
|
||||
TResult Function(_Fetched value)? fetched,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (categoryIdChanged != null) {
|
||||
return categoryIdChanged(this);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _CategoryIdChanged implements ProductLoaderEvent {
|
||||
const factory _CategoryIdChanged(final String categoryId) =
|
||||
_$CategoryIdChangedImpl;
|
||||
|
||||
String get categoryId;
|
||||
|
||||
/// Create a copy of ProductLoaderEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$CategoryIdChangedImplCopyWith<_$CategoryIdChangedImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$SearchChangedImplCopyWith<$Res> {
|
||||
factory _$$SearchChangedImplCopyWith(
|
||||
_$SearchChangedImpl value,
|
||||
$Res Function(_$SearchChangedImpl) then,
|
||||
) = __$$SearchChangedImplCopyWithImpl<$Res>;
|
||||
@useResult
|
||||
$Res call({String search});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$SearchChangedImplCopyWithImpl<$Res>
|
||||
extends _$ProductLoaderEventCopyWithImpl<$Res, _$SearchChangedImpl>
|
||||
implements _$$SearchChangedImplCopyWith<$Res> {
|
||||
__$$SearchChangedImplCopyWithImpl(
|
||||
_$SearchChangedImpl _value,
|
||||
$Res Function(_$SearchChangedImpl) _then,
|
||||
) : super(_value, _then);
|
||||
|
||||
/// Create a copy of ProductLoaderEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({Object? search = null}) {
|
||||
return _then(
|
||||
_$SearchChangedImpl(
|
||||
null == search
|
||||
? _value.search
|
||||
: search // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$SearchChangedImpl implements _SearchChanged {
|
||||
const _$SearchChangedImpl(this.search);
|
||||
|
||||
@override
|
||||
final String search;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ProductLoaderEvent.searchChanged(search: $search)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$SearchChangedImpl &&
|
||||
(identical(other.search, search) || other.search == search));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, search);
|
||||
|
||||
/// Create a copy of ProductLoaderEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$SearchChangedImplCopyWith<_$SearchChangedImpl> get copyWith =>
|
||||
__$$SearchChangedImplCopyWithImpl<_$SearchChangedImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function(String categoryId) categoryIdChanged,
|
||||
required TResult Function(String search) searchChanged,
|
||||
required TResult Function(bool isRefresh) fetched,
|
||||
}) {
|
||||
return searchChanged(search);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function(String categoryId)? categoryIdChanged,
|
||||
TResult? Function(String search)? searchChanged,
|
||||
TResult? Function(bool isRefresh)? fetched,
|
||||
}) {
|
||||
return searchChanged?.call(search);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function(String categoryId)? categoryIdChanged,
|
||||
TResult Function(String search)? searchChanged,
|
||||
TResult Function(bool isRefresh)? fetched,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (searchChanged != null) {
|
||||
return searchChanged(search);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(_CategoryIdChanged value) categoryIdChanged,
|
||||
required TResult Function(_SearchChanged value) searchChanged,
|
||||
required TResult Function(_Fetched value) fetched,
|
||||
}) {
|
||||
return searchChanged(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(_CategoryIdChanged value)? categoryIdChanged,
|
||||
TResult? Function(_SearchChanged value)? searchChanged,
|
||||
TResult? Function(_Fetched value)? fetched,
|
||||
}) {
|
||||
return searchChanged?.call(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(_CategoryIdChanged value)? categoryIdChanged,
|
||||
TResult Function(_SearchChanged value)? searchChanged,
|
||||
TResult Function(_Fetched value)? fetched,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (searchChanged != null) {
|
||||
return searchChanged(this);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _SearchChanged implements ProductLoaderEvent {
|
||||
const factory _SearchChanged(final String search) = _$SearchChangedImpl;
|
||||
|
||||
String get search;
|
||||
|
||||
/// Create a copy of ProductLoaderEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$SearchChangedImplCopyWith<_$SearchChangedImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$FetchedImplCopyWith<$Res> {
|
||||
factory _$$FetchedImplCopyWith(
|
||||
_$FetchedImpl value,
|
||||
$Res Function(_$FetchedImpl) then,
|
||||
) = __$$FetchedImplCopyWithImpl<$Res>;
|
||||
@useResult
|
||||
$Res call({bool isRefresh});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$FetchedImplCopyWithImpl<$Res>
|
||||
extends _$ProductLoaderEventCopyWithImpl<$Res, _$FetchedImpl>
|
||||
implements _$$FetchedImplCopyWith<$Res> {
|
||||
__$$FetchedImplCopyWithImpl(
|
||||
_$FetchedImpl _value,
|
||||
$Res Function(_$FetchedImpl) _then,
|
||||
) : super(_value, _then);
|
||||
|
||||
/// Create a copy of ProductLoaderEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({Object? isRefresh = null}) {
|
||||
return _then(
|
||||
_$FetchedImpl(
|
||||
isRefresh: null == isRefresh
|
||||
? _value.isRefresh
|
||||
: isRefresh // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$FetchedImpl implements _Fetched {
|
||||
const _$FetchedImpl({this.isRefresh = false});
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool isRefresh;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ProductLoaderEvent.fetched(isRefresh: $isRefresh)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$FetchedImpl &&
|
||||
(identical(other.isRefresh, isRefresh) ||
|
||||
other.isRefresh == isRefresh));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, isRefresh);
|
||||
|
||||
/// Create a copy of ProductLoaderEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$FetchedImplCopyWith<_$FetchedImpl> get copyWith =>
|
||||
__$$FetchedImplCopyWithImpl<_$FetchedImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function(String categoryId) categoryIdChanged,
|
||||
required TResult Function(String search) searchChanged,
|
||||
required TResult Function(bool isRefresh) fetched,
|
||||
}) {
|
||||
return fetched(isRefresh);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function(String categoryId)? categoryIdChanged,
|
||||
TResult? Function(String search)? searchChanged,
|
||||
TResult? Function(bool isRefresh)? fetched,
|
||||
}) {
|
||||
return fetched?.call(isRefresh);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function(String categoryId)? categoryIdChanged,
|
||||
TResult Function(String search)? searchChanged,
|
||||
TResult Function(bool isRefresh)? fetched,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (fetched != null) {
|
||||
return fetched(isRefresh);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(_CategoryIdChanged value) categoryIdChanged,
|
||||
required TResult Function(_SearchChanged value) searchChanged,
|
||||
required TResult Function(_Fetched value) fetched,
|
||||
}) {
|
||||
return fetched(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(_CategoryIdChanged value)? categoryIdChanged,
|
||||
TResult? Function(_SearchChanged value)? searchChanged,
|
||||
TResult? Function(_Fetched value)? fetched,
|
||||
}) {
|
||||
return fetched?.call(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(_CategoryIdChanged value)? categoryIdChanged,
|
||||
TResult Function(_SearchChanged value)? searchChanged,
|
||||
TResult Function(_Fetched value)? fetched,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (fetched != null) {
|
||||
return fetched(this);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _Fetched implements ProductLoaderEvent {
|
||||
const factory _Fetched({final bool isRefresh}) = _$FetchedImpl;
|
||||
|
||||
bool get isRefresh;
|
||||
|
||||
/// Create a copy of ProductLoaderEvent
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$FetchedImplCopyWith<_$FetchedImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ProductLoaderState {
|
||||
List<Product> get products => throw _privateConstructorUsedError;
|
||||
Option<ProductFailure> get failureOptionProduct =>
|
||||
throw _privateConstructorUsedError;
|
||||
String? get categoryId => throw _privateConstructorUsedError;
|
||||
String? get search => throw _privateConstructorUsedError;
|
||||
bool get isFetching => throw _privateConstructorUsedError;
|
||||
bool get hasReachedMax => throw _privateConstructorUsedError;
|
||||
int get page => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of ProductLoaderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$ProductLoaderStateCopyWith<ProductLoaderState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $ProductLoaderStateCopyWith<$Res> {
|
||||
factory $ProductLoaderStateCopyWith(
|
||||
ProductLoaderState value,
|
||||
$Res Function(ProductLoaderState) then,
|
||||
) = _$ProductLoaderStateCopyWithImpl<$Res, ProductLoaderState>;
|
||||
@useResult
|
||||
$Res call({
|
||||
List<Product> products,
|
||||
Option<ProductFailure> failureOptionProduct,
|
||||
String? categoryId,
|
||||
String? search,
|
||||
bool isFetching,
|
||||
bool hasReachedMax,
|
||||
int page,
|
||||
});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$ProductLoaderStateCopyWithImpl<$Res, $Val extends ProductLoaderState>
|
||||
implements $ProductLoaderStateCopyWith<$Res> {
|
||||
_$ProductLoaderStateCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of ProductLoaderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? products = null,
|
||||
Object? failureOptionProduct = null,
|
||||
Object? categoryId = freezed,
|
||||
Object? search = freezed,
|
||||
Object? isFetching = null,
|
||||
Object? hasReachedMax = null,
|
||||
Object? page = null,
|
||||
}) {
|
||||
return _then(
|
||||
_value.copyWith(
|
||||
products: null == products
|
||||
? _value.products
|
||||
: products // ignore: cast_nullable_to_non_nullable
|
||||
as List<Product>,
|
||||
failureOptionProduct: null == failureOptionProduct
|
||||
? _value.failureOptionProduct
|
||||
: failureOptionProduct // ignore: cast_nullable_to_non_nullable
|
||||
as Option<ProductFailure>,
|
||||
categoryId: freezed == categoryId
|
||||
? _value.categoryId
|
||||
: categoryId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
search: freezed == search
|
||||
? _value.search
|
||||
: search // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
isFetching: null == isFetching
|
||||
? _value.isFetching
|
||||
: isFetching // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
hasReachedMax: null == hasReachedMax
|
||||
? _value.hasReachedMax
|
||||
: hasReachedMax // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
page: null == page
|
||||
? _value.page
|
||||
: page // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
)
|
||||
as $Val,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$ProductLoaderStateImplCopyWith<$Res>
|
||||
implements $ProductLoaderStateCopyWith<$Res> {
|
||||
factory _$$ProductLoaderStateImplCopyWith(
|
||||
_$ProductLoaderStateImpl value,
|
||||
$Res Function(_$ProductLoaderStateImpl) then,
|
||||
) = __$$ProductLoaderStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({
|
||||
List<Product> products,
|
||||
Option<ProductFailure> failureOptionProduct,
|
||||
String? categoryId,
|
||||
String? search,
|
||||
bool isFetching,
|
||||
bool hasReachedMax,
|
||||
int page,
|
||||
});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$ProductLoaderStateImplCopyWithImpl<$Res>
|
||||
extends _$ProductLoaderStateCopyWithImpl<$Res, _$ProductLoaderStateImpl>
|
||||
implements _$$ProductLoaderStateImplCopyWith<$Res> {
|
||||
__$$ProductLoaderStateImplCopyWithImpl(
|
||||
_$ProductLoaderStateImpl _value,
|
||||
$Res Function(_$ProductLoaderStateImpl) _then,
|
||||
) : super(_value, _then);
|
||||
|
||||
/// Create a copy of ProductLoaderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? products = null,
|
||||
Object? failureOptionProduct = null,
|
||||
Object? categoryId = freezed,
|
||||
Object? search = freezed,
|
||||
Object? isFetching = null,
|
||||
Object? hasReachedMax = null,
|
||||
Object? page = null,
|
||||
}) {
|
||||
return _then(
|
||||
_$ProductLoaderStateImpl(
|
||||
products: null == products
|
||||
? _value._products
|
||||
: products // ignore: cast_nullable_to_non_nullable
|
||||
as List<Product>,
|
||||
failureOptionProduct: null == failureOptionProduct
|
||||
? _value.failureOptionProduct
|
||||
: failureOptionProduct // ignore: cast_nullable_to_non_nullable
|
||||
as Option<ProductFailure>,
|
||||
categoryId: freezed == categoryId
|
||||
? _value.categoryId
|
||||
: categoryId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
search: freezed == search
|
||||
? _value.search
|
||||
: search // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
isFetching: null == isFetching
|
||||
? _value.isFetching
|
||||
: isFetching // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
hasReachedMax: null == hasReachedMax
|
||||
? _value.hasReachedMax
|
||||
: hasReachedMax // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
page: null == page
|
||||
? _value.page
|
||||
: page // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$ProductLoaderStateImpl implements _ProductLoaderState {
|
||||
const _$ProductLoaderStateImpl({
|
||||
required final List<Product> products,
|
||||
required this.failureOptionProduct,
|
||||
this.categoryId,
|
||||
this.search,
|
||||
this.isFetching = false,
|
||||
this.hasReachedMax = false,
|
||||
this.page = 1,
|
||||
}) : _products = products;
|
||||
|
||||
final List<Product> _products;
|
||||
@override
|
||||
List<Product> get products {
|
||||
if (_products is EqualUnmodifiableListView) return _products;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_products);
|
||||
}
|
||||
|
||||
@override
|
||||
final Option<ProductFailure> failureOptionProduct;
|
||||
@override
|
||||
final String? categoryId;
|
||||
@override
|
||||
final String? search;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool isFetching;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool hasReachedMax;
|
||||
@override
|
||||
@JsonKey()
|
||||
final int page;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ProductLoaderState(products: $products, failureOptionProduct: $failureOptionProduct, categoryId: $categoryId, search: $search, isFetching: $isFetching, hasReachedMax: $hasReachedMax, page: $page)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$ProductLoaderStateImpl &&
|
||||
const DeepCollectionEquality().equals(other._products, _products) &&
|
||||
(identical(other.failureOptionProduct, failureOptionProduct) ||
|
||||
other.failureOptionProduct == failureOptionProduct) &&
|
||||
(identical(other.categoryId, categoryId) ||
|
||||
other.categoryId == categoryId) &&
|
||||
(identical(other.search, search) || other.search == search) &&
|
||||
(identical(other.isFetching, isFetching) ||
|
||||
other.isFetching == isFetching) &&
|
||||
(identical(other.hasReachedMax, hasReachedMax) ||
|
||||
other.hasReachedMax == hasReachedMax) &&
|
||||
(identical(other.page, page) || other.page == page));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(_products),
|
||||
failureOptionProduct,
|
||||
categoryId,
|
||||
search,
|
||||
isFetching,
|
||||
hasReachedMax,
|
||||
page,
|
||||
);
|
||||
|
||||
/// Create a copy of ProductLoaderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ProductLoaderStateImplCopyWith<_$ProductLoaderStateImpl> get copyWith =>
|
||||
__$$ProductLoaderStateImplCopyWithImpl<_$ProductLoaderStateImpl>(
|
||||
this,
|
||||
_$identity,
|
||||
);
|
||||
}
|
||||
|
||||
abstract class _ProductLoaderState implements ProductLoaderState {
|
||||
const factory _ProductLoaderState({
|
||||
required final List<Product> products,
|
||||
required final Option<ProductFailure> failureOptionProduct,
|
||||
final String? categoryId,
|
||||
final String? search,
|
||||
final bool isFetching,
|
||||
final bool hasReachedMax,
|
||||
final int page,
|
||||
}) = _$ProductLoaderStateImpl;
|
||||
|
||||
@override
|
||||
List<Product> get products;
|
||||
@override
|
||||
Option<ProductFailure> get failureOptionProduct;
|
||||
@override
|
||||
String? get categoryId;
|
||||
@override
|
||||
String? get search;
|
||||
@override
|
||||
bool get isFetching;
|
||||
@override
|
||||
bool get hasReachedMax;
|
||||
@override
|
||||
int get page;
|
||||
|
||||
/// Create a copy of ProductLoaderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$ProductLoaderStateImplCopyWith<_$ProductLoaderStateImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
part of 'product_loader_bloc.dart';
|
||||
|
||||
@freezed
|
||||
class ProductLoaderEvent with _$ProductLoaderEvent {
|
||||
const factory ProductLoaderEvent.categoryIdChanged(String categoryId) =
|
||||
_CategoryIdChanged;
|
||||
const factory ProductLoaderEvent.searchChanged(String search) =
|
||||
_SearchChanged;
|
||||
const factory ProductLoaderEvent.fetched({@Default(false) bool isRefresh}) =
|
||||
_Fetched;
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
part of 'product_loader_bloc.dart';
|
||||
|
||||
@freezed
|
||||
class ProductLoaderState with _$ProductLoaderState {
|
||||
const factory ProductLoaderState({
|
||||
required List<Product> products,
|
||||
required Option<ProductFailure> failureOptionProduct,
|
||||
String? categoryId,
|
||||
String? search,
|
||||
@Default(false) bool isFetching,
|
||||
@Default(false) bool hasReachedMax,
|
||||
@Default(1) int page,
|
||||
}) = _ProductLoaderState;
|
||||
|
||||
factory ProductLoaderState.initial() =>
|
||||
ProductLoaderState(products: [], failureOptionProduct: none());
|
||||
}
|
||||
@ -2,10 +2,8 @@ import 'package:awesome_dio_interceptor/awesome_dio_interceptor.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../../env.dart';
|
||||
import '../constant/local_storage_key.dart';
|
||||
import 'api_failure.dart';
|
||||
import 'errors/bad_network_error.dart';
|
||||
import 'errors/bad_request_error.dart';
|
||||
@ -24,16 +22,11 @@ import 'interceptors/unauthorized_interceptor.dart';
|
||||
class ApiClient {
|
||||
final Dio _dio;
|
||||
final Env _env;
|
||||
final SharedPreferences _preferences;
|
||||
|
||||
ApiClient(this._dio, this._env, this._preferences) {
|
||||
ApiClient(this._dio, this._env) {
|
||||
_dio.options.baseUrl = _env.baseUrl;
|
||||
_dio.options.validateStatus = (status) => status! < 500;
|
||||
_dio.options.connectTimeout = const Duration(seconds: 20);
|
||||
_dio.options.headers = {
|
||||
'authorization':
|
||||
'Bearer ${_preferences.getString(LocalStorageKey.token)}',
|
||||
};
|
||||
_dio.interceptors.add(BadNetworkErrorInterceptor());
|
||||
_dio.interceptors.add(BadRequestErrorInterceptor());
|
||||
_dio.interceptors.add(InternalServerErrorInterceptor());
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../../injection.dart';
|
||||
import '../constant/local_storage_key.dart';
|
||||
|
||||
void dismissKeyboard(BuildContext context) {
|
||||
final currentFocus = FocusScope.of(context);
|
||||
@ -6,3 +10,10 @@ void dismissKeyboard(BuildContext context) {
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> getAuthorizationHeader() {
|
||||
return {
|
||||
'Authorization':
|
||||
'Bearer ${getIt<SharedPreferences>().getString(LocalStorageKey.token)}',
|
||||
};
|
||||
}
|
||||
|
||||
@ -2,4 +2,15 @@ class ApiPath {
|
||||
// Auth
|
||||
static const String login = '/api/v1/auth/login';
|
||||
static const String logout = '/api/v1/auth/logout';
|
||||
|
||||
// Analytic
|
||||
static const String salesAnalytic = '/api/v1/analytics/sales';
|
||||
static const String profitLossAnalytic = '/api/v1/analytics/profit-loss';
|
||||
static const String categoryAnalytic = '/api/v1/analytics/categories';
|
||||
|
||||
// Category
|
||||
static const String category = '/api/v1/categories';
|
||||
|
||||
// Product
|
||||
static const String product = '/api/v1/products';
|
||||
}
|
||||
|
||||
10
lib/domain/analytic/analytic.dart
Normal file
10
lib/domain/analytic/analytic.dart
Normal file
@ -0,0 +1,10 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import '../../common/api/api_failure.dart';
|
||||
|
||||
part 'analytic.freezed.dart';
|
||||
|
||||
part 'entities/sales_analytic_entity.dart';
|
||||
part 'entities/profit_loss_analytic_entity.dart';
|
||||
part 'entities/category_analytic_entity.dart';
|
||||
part 'failures/analytic_failure.dart';
|
||||
3437
lib/domain/analytic/analytic.freezed.dart
Normal file
3437
lib/domain/analytic/analytic.freezed.dart
Normal file
File diff suppressed because it is too large
Load Diff
41
lib/domain/analytic/entities/category_analytic_entity.dart
Normal file
41
lib/domain/analytic/entities/category_analytic_entity.dart
Normal file
@ -0,0 +1,41 @@
|
||||
part of '../analytic.dart';
|
||||
|
||||
@freezed
|
||||
class CategoryAnalytic with _$CategoryAnalytic {
|
||||
const factory CategoryAnalytic({
|
||||
required String organizationId,
|
||||
required String outletId,
|
||||
required String dateFrom,
|
||||
required String dateTo,
|
||||
required List<CategoryAnalyticItem> data,
|
||||
}) = _CategoryAnalytic;
|
||||
|
||||
factory CategoryAnalytic.empty() => const CategoryAnalytic(
|
||||
organizationId: "",
|
||||
outletId: "",
|
||||
dateFrom: "",
|
||||
dateTo: "",
|
||||
data: [],
|
||||
);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class CategoryAnalyticItem with _$CategoryAnalyticItem {
|
||||
const factory CategoryAnalyticItem({
|
||||
required String categoryId,
|
||||
required String categoryName,
|
||||
required int totalRevenue,
|
||||
required int totalQuantity,
|
||||
required int productCount,
|
||||
required int orderCount,
|
||||
}) = _CategoryAnalyticItem;
|
||||
|
||||
factory CategoryAnalyticItem.empty() => const CategoryAnalyticItem(
|
||||
categoryId: "",
|
||||
categoryName: "",
|
||||
totalRevenue: 0,
|
||||
totalQuantity: 0,
|
||||
productCount: 0,
|
||||
orderCount: 0,
|
||||
);
|
||||
}
|
||||
117
lib/domain/analytic/entities/profit_loss_analytic_entity.dart
Normal file
117
lib/domain/analytic/entities/profit_loss_analytic_entity.dart
Normal file
@ -0,0 +1,117 @@
|
||||
part of '../analytic.dart';
|
||||
|
||||
@freezed
|
||||
class ProfitLossAnalytic with _$ProfitLossAnalytic {
|
||||
const factory ProfitLossAnalytic({
|
||||
required String organizationId,
|
||||
required String dateFrom,
|
||||
required String dateTo,
|
||||
required String groupBy,
|
||||
required ProfitLossSummary summary,
|
||||
required List<ProfitLossDailyData> data,
|
||||
required List<ProfitLossProductData> productData,
|
||||
}) = _ProfitLossAnalytic;
|
||||
|
||||
factory ProfitLossAnalytic.empty() => ProfitLossAnalytic(
|
||||
organizationId: '',
|
||||
dateFrom: '',
|
||||
dateTo: '',
|
||||
groupBy: '',
|
||||
summary: ProfitLossSummary.empty(),
|
||||
data: [],
|
||||
productData: [],
|
||||
);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ProfitLossSummary with _$ProfitLossSummary {
|
||||
const factory ProfitLossSummary({
|
||||
required int totalRevenue,
|
||||
required int totalCost,
|
||||
required int grossProfit,
|
||||
required double grossProfitMargin,
|
||||
required int totalTax,
|
||||
required int totalDiscount,
|
||||
required int netProfit,
|
||||
required double netProfitMargin,
|
||||
required int totalOrders,
|
||||
required double averageProfit,
|
||||
required double profitabilityRatio,
|
||||
}) = _ProfitLossSummary;
|
||||
|
||||
factory ProfitLossSummary.empty() => ProfitLossSummary(
|
||||
totalRevenue: 0,
|
||||
totalCost: 0,
|
||||
grossProfit: 0,
|
||||
grossProfitMargin: 0,
|
||||
totalTax: 0,
|
||||
totalDiscount: 0,
|
||||
netProfit: 0,
|
||||
netProfitMargin: 0,
|
||||
totalOrders: 0,
|
||||
averageProfit: 0,
|
||||
profitabilityRatio: 0,
|
||||
);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ProfitLossDailyData with _$ProfitLossDailyData {
|
||||
const factory ProfitLossDailyData({
|
||||
required String date,
|
||||
required int revenue,
|
||||
required int cost,
|
||||
required int grossProfit,
|
||||
required double grossProfitMargin,
|
||||
required int tax,
|
||||
required int discount,
|
||||
required int netProfit,
|
||||
required double netProfitMargin,
|
||||
required int orders,
|
||||
}) = _ProfitLossDailyData;
|
||||
|
||||
factory ProfitLossDailyData.empty() => ProfitLossDailyData(
|
||||
date: '',
|
||||
revenue: 0,
|
||||
cost: 0,
|
||||
grossProfit: 0,
|
||||
grossProfitMargin: 0,
|
||||
tax: 0,
|
||||
discount: 0,
|
||||
netProfit: 0,
|
||||
netProfitMargin: 0,
|
||||
orders: 0,
|
||||
);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ProfitLossProductData with _$ProfitLossProductData {
|
||||
const factory ProfitLossProductData({
|
||||
required String productId,
|
||||
required String productName,
|
||||
required String categoryId,
|
||||
required String categoryName,
|
||||
required int quantitySold,
|
||||
required int revenue,
|
||||
required int cost,
|
||||
required int grossProfit,
|
||||
required double grossProfitMargin,
|
||||
required int averagePrice,
|
||||
required int averageCost,
|
||||
required int profitPerUnit,
|
||||
}) = _ProfitLossProductData;
|
||||
|
||||
factory ProfitLossProductData.empty() => ProfitLossProductData(
|
||||
productId: '',
|
||||
productName: '',
|
||||
categoryId: '',
|
||||
categoryName: '',
|
||||
quantitySold: 0,
|
||||
revenue: 0,
|
||||
cost: 0,
|
||||
grossProfit: 0,
|
||||
grossProfitMargin: 0,
|
||||
averagePrice: 0,
|
||||
averageCost: 0,
|
||||
profitPerUnit: 0,
|
||||
);
|
||||
}
|
||||
70
lib/domain/analytic/entities/sales_analytic_entity.dart
Normal file
70
lib/domain/analytic/entities/sales_analytic_entity.dart
Normal file
@ -0,0 +1,70 @@
|
||||
part of '../analytic.dart';
|
||||
|
||||
@freezed
|
||||
class SalesAnalytic with _$SalesAnalytic {
|
||||
const factory SalesAnalytic({
|
||||
required String organizationId,
|
||||
required String outletId,
|
||||
required DateTime dateFrom,
|
||||
required DateTime dateTo,
|
||||
required String groupBy,
|
||||
required SalesAnalyticSummary summary,
|
||||
required List<SalesAnalyticData> data,
|
||||
}) = _SalesAnalytic;
|
||||
|
||||
factory SalesAnalytic.empty() => SalesAnalytic(
|
||||
organizationId: '',
|
||||
outletId: '',
|
||||
dateFrom: DateTime.fromMillisecondsSinceEpoch(0),
|
||||
dateTo: DateTime.fromMillisecondsSinceEpoch(0),
|
||||
groupBy: '',
|
||||
summary: SalesAnalyticSummary.empty(),
|
||||
data: [],
|
||||
);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SalesAnalyticSummary with _$SalesAnalyticSummary {
|
||||
const factory SalesAnalyticSummary({
|
||||
required int totalSales,
|
||||
required int totalOrders,
|
||||
required int totalItems,
|
||||
required double averageOrderValue,
|
||||
required int totalTax,
|
||||
required int totalDiscount,
|
||||
required int netSales,
|
||||
}) = _SalesAnalyticSummary;
|
||||
|
||||
factory SalesAnalyticSummary.empty() => const SalesAnalyticSummary(
|
||||
totalSales: 0,
|
||||
totalOrders: 0,
|
||||
totalItems: 0,
|
||||
averageOrderValue: 0,
|
||||
totalTax: 0,
|
||||
totalDiscount: 0,
|
||||
netSales: 0,
|
||||
);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SalesAnalyticData with _$SalesAnalyticData {
|
||||
const factory SalesAnalyticData({
|
||||
required DateTime date,
|
||||
required int sales,
|
||||
required int orders,
|
||||
required int items,
|
||||
required int tax,
|
||||
required int discount,
|
||||
required int netSales,
|
||||
}) = _SalesAnalyticData;
|
||||
|
||||
factory SalesAnalyticData.empty() => SalesAnalyticData(
|
||||
date: DateTime.fromMillisecondsSinceEpoch(0),
|
||||
sales: 0,
|
||||
orders: 0,
|
||||
items: 0,
|
||||
tax: 0,
|
||||
discount: 0,
|
||||
netSales: 0,
|
||||
);
|
||||
}
|
||||
10
lib/domain/analytic/failures/analytic_failure.dart
Normal file
10
lib/domain/analytic/failures/analytic_failure.dart
Normal file
@ -0,0 +1,10 @@
|
||||
part of '../analytic.dart';
|
||||
|
||||
@freezed
|
||||
sealed class AnalyticFailure with _$AnalyticFailure {
|
||||
const factory AnalyticFailure.serverError(ApiFailure failure) = _ServerError;
|
||||
const factory AnalyticFailure.unexpectedError() = _UnexpectedError;
|
||||
const factory AnalyticFailure.empty() = _Empty;
|
||||
const factory AnalyticFailure.dynamicErrorMessage(String erroMessage) =
|
||||
_DynamicErrorMessage;
|
||||
}
|
||||
20
lib/domain/analytic/repositories/i_analytic_repository.dart
Normal file
20
lib/domain/analytic/repositories/i_analytic_repository.dart
Normal file
@ -0,0 +1,20 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
import '../analytic.dart';
|
||||
|
||||
abstract class IAnalyticRepository {
|
||||
Future<Either<AnalyticFailure, SalesAnalytic>> getSales({
|
||||
required DateTime dateFrom,
|
||||
required DateTime dateTo,
|
||||
});
|
||||
|
||||
Future<Either<AnalyticFailure, ProfitLossAnalytic>> getProfitLoss({
|
||||
required DateTime dateFrom,
|
||||
required DateTime dateTo,
|
||||
});
|
||||
|
||||
Future<Either<AnalyticFailure, CategoryAnalytic>> getCategory({
|
||||
required DateTime dateFrom,
|
||||
required DateTime dateTo,
|
||||
});
|
||||
}
|
||||
10
lib/domain/category/category.dart
Normal file
10
lib/domain/category/category.dart
Normal file
@ -0,0 +1,10 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import '../../common/api/api_failure.dart';
|
||||
|
||||
part 'category.freezed.dart';
|
||||
|
||||
part 'entities/category_entity.dart';
|
||||
part 'failures/category_failure.dart';
|
||||
part 'repositories/i_auth_repository.dart';
|
||||
952
lib/domain/category/category.freezed.dart
Normal file
952
lib/domain/category/category.freezed.dart
Normal file
@ -0,0 +1,952 @@
|
||||
// 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.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 _$Category {
|
||||
String get id => throw _privateConstructorUsedError;
|
||||
String get organizationId => throw _privateConstructorUsedError;
|
||||
String get name => throw _privateConstructorUsedError;
|
||||
String get description => throw _privateConstructorUsedError;
|
||||
String get businessType => throw _privateConstructorUsedError;
|
||||
Map<String, dynamic> get metadata => throw _privateConstructorUsedError;
|
||||
DateTime? get createdAt => throw _privateConstructorUsedError;
|
||||
DateTime? get updatedAt => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of Category
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$CategoryCopyWith<Category> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $CategoryCopyWith<$Res> {
|
||||
factory $CategoryCopyWith(Category value, $Res Function(Category) then) =
|
||||
_$CategoryCopyWithImpl<$Res, Category>;
|
||||
@useResult
|
||||
$Res call({
|
||||
String id,
|
||||
String organizationId,
|
||||
String name,
|
||||
String description,
|
||||
String businessType,
|
||||
Map<String, dynamic> metadata,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$CategoryCopyWithImpl<$Res, $Val extends Category>
|
||||
implements $CategoryCopyWith<$Res> {
|
||||
_$CategoryCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of Category
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = null,
|
||||
Object? organizationId = null,
|
||||
Object? name = null,
|
||||
Object? description = null,
|
||||
Object? businessType = null,
|
||||
Object? metadata = null,
|
||||
Object? createdAt = freezed,
|
||||
Object? updatedAt = freezed,
|
||||
}) {
|
||||
return _then(
|
||||
_value.copyWith(
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
organizationId: null == organizationId
|
||||
? _value.organizationId
|
||||
: organizationId // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
description: null == description
|
||||
? _value.description
|
||||
: description // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
businessType: null == businessType
|
||||
? _value.businessType
|
||||
: businessType // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
metadata: null == metadata
|
||||
? _value.metadata
|
||||
: metadata // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>,
|
||||
createdAt: freezed == createdAt
|
||||
? _value.createdAt
|
||||
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
updatedAt: freezed == updatedAt
|
||||
? _value.updatedAt
|
||||
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
)
|
||||
as $Val,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$CategoryImplCopyWith<$Res>
|
||||
implements $CategoryCopyWith<$Res> {
|
||||
factory _$$CategoryImplCopyWith(
|
||||
_$CategoryImpl value,
|
||||
$Res Function(_$CategoryImpl) then,
|
||||
) = __$$CategoryImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({
|
||||
String id,
|
||||
String organizationId,
|
||||
String name,
|
||||
String description,
|
||||
String businessType,
|
||||
Map<String, dynamic> metadata,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$CategoryImplCopyWithImpl<$Res>
|
||||
extends _$CategoryCopyWithImpl<$Res, _$CategoryImpl>
|
||||
implements _$$CategoryImplCopyWith<$Res> {
|
||||
__$$CategoryImplCopyWithImpl(
|
||||
_$CategoryImpl _value,
|
||||
$Res Function(_$CategoryImpl) _then,
|
||||
) : super(_value, _then);
|
||||
|
||||
/// Create a copy of Category
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = null,
|
||||
Object? organizationId = null,
|
||||
Object? name = null,
|
||||
Object? description = null,
|
||||
Object? businessType = null,
|
||||
Object? metadata = null,
|
||||
Object? createdAt = freezed,
|
||||
Object? updatedAt = freezed,
|
||||
}) {
|
||||
return _then(
|
||||
_$CategoryImpl(
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
organizationId: null == organizationId
|
||||
? _value.organizationId
|
||||
: organizationId // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
description: null == description
|
||||
? _value.description
|
||||
: description // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
businessType: null == businessType
|
||||
? _value.businessType
|
||||
: businessType // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
metadata: null == metadata
|
||||
? _value._metadata
|
||||
: metadata // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>,
|
||||
createdAt: freezed == createdAt
|
||||
? _value.createdAt
|
||||
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
updatedAt: freezed == updatedAt
|
||||
? _value.updatedAt
|
||||
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$CategoryImpl implements _Category {
|
||||
const _$CategoryImpl({
|
||||
required this.id,
|
||||
required this.organizationId,
|
||||
required this.name,
|
||||
required this.description,
|
||||
required this.businessType,
|
||||
required final Map<String, dynamic> metadata,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
}) : _metadata = metadata;
|
||||
|
||||
@override
|
||||
final String id;
|
||||
@override
|
||||
final String organizationId;
|
||||
@override
|
||||
final String name;
|
||||
@override
|
||||
final String description;
|
||||
@override
|
||||
final String businessType;
|
||||
final Map<String, dynamic> _metadata;
|
||||
@override
|
||||
Map<String, dynamic> get metadata {
|
||||
if (_metadata is EqualUnmodifiableMapView) return _metadata;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableMapView(_metadata);
|
||||
}
|
||||
|
||||
@override
|
||||
final DateTime? createdAt;
|
||||
@override
|
||||
final DateTime? updatedAt;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Category(id: $id, organizationId: $organizationId, name: $name, description: $description, businessType: $businessType, metadata: $metadata, createdAt: $createdAt, updatedAt: $updatedAt)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$CategoryImpl &&
|
||||
(identical(other.id, id) || other.id == id) &&
|
||||
(identical(other.organizationId, organizationId) ||
|
||||
other.organizationId == organizationId) &&
|
||||
(identical(other.name, name) || other.name == name) &&
|
||||
(identical(other.description, description) ||
|
||||
other.description == description) &&
|
||||
(identical(other.businessType, businessType) ||
|
||||
other.businessType == businessType) &&
|
||||
const DeepCollectionEquality().equals(other._metadata, _metadata) &&
|
||||
(identical(other.createdAt, createdAt) ||
|
||||
other.createdAt == createdAt) &&
|
||||
(identical(other.updatedAt, updatedAt) ||
|
||||
other.updatedAt == updatedAt));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
id,
|
||||
organizationId,
|
||||
name,
|
||||
description,
|
||||
businessType,
|
||||
const DeepCollectionEquality().hash(_metadata),
|
||||
createdAt,
|
||||
updatedAt,
|
||||
);
|
||||
|
||||
/// Create a copy of Category
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$CategoryImplCopyWith<_$CategoryImpl> get copyWith =>
|
||||
__$$CategoryImplCopyWithImpl<_$CategoryImpl>(this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _Category implements Category {
|
||||
const factory _Category({
|
||||
required final String id,
|
||||
required final String organizationId,
|
||||
required final String name,
|
||||
required final String description,
|
||||
required final String businessType,
|
||||
required final Map<String, dynamic> metadata,
|
||||
final DateTime? createdAt,
|
||||
final DateTime? updatedAt,
|
||||
}) = _$CategoryImpl;
|
||||
|
||||
@override
|
||||
String get id;
|
||||
@override
|
||||
String get organizationId;
|
||||
@override
|
||||
String get name;
|
||||
@override
|
||||
String get description;
|
||||
@override
|
||||
String get businessType;
|
||||
@override
|
||||
Map<String, dynamic> get metadata;
|
||||
@override
|
||||
DateTime? get createdAt;
|
||||
@override
|
||||
DateTime? get updatedAt;
|
||||
|
||||
/// Create a copy of Category
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$CategoryImplCopyWith<_$CategoryImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$CategoryFailure {
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function(ApiFailure failure) serverError,
|
||||
required TResult Function() unexpectedError,
|
||||
required TResult Function() empty,
|
||||
required TResult Function(String erroMessage) dynamicErrorMessage,
|
||||
}) => throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function(ApiFailure failure)? serverError,
|
||||
TResult? Function()? unexpectedError,
|
||||
TResult? Function()? empty,
|
||||
TResult? Function(String erroMessage)? dynamicErrorMessage,
|
||||
}) => throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function(ApiFailure failure)? serverError,
|
||||
TResult Function()? unexpectedError,
|
||||
TResult Function()? empty,
|
||||
TResult Function(String erroMessage)? dynamicErrorMessage,
|
||||
required TResult orElse(),
|
||||
}) => throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(_ServerError value) serverError,
|
||||
required TResult Function(_UnexpectedError value) unexpectedError,
|
||||
required TResult Function(_Empty value) empty,
|
||||
required TResult Function(_DynamicErrorMessage value) dynamicErrorMessage,
|
||||
}) => throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(_ServerError value)? serverError,
|
||||
TResult? Function(_UnexpectedError value)? unexpectedError,
|
||||
TResult? Function(_Empty value)? empty,
|
||||
TResult? Function(_DynamicErrorMessage value)? dynamicErrorMessage,
|
||||
}) => throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(_ServerError value)? serverError,
|
||||
TResult Function(_UnexpectedError value)? unexpectedError,
|
||||
TResult Function(_Empty value)? empty,
|
||||
TResult Function(_DynamicErrorMessage value)? dynamicErrorMessage,
|
||||
required TResult orElse(),
|
||||
}) => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $CategoryFailureCopyWith<$Res> {
|
||||
factory $CategoryFailureCopyWith(
|
||||
CategoryFailure value,
|
||||
$Res Function(CategoryFailure) then,
|
||||
) = _$CategoryFailureCopyWithImpl<$Res, CategoryFailure>;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$CategoryFailureCopyWithImpl<$Res, $Val extends CategoryFailure>
|
||||
implements $CategoryFailureCopyWith<$Res> {
|
||||
_$CategoryFailureCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of CategoryFailure
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$ServerErrorImplCopyWith<$Res> {
|
||||
factory _$$ServerErrorImplCopyWith(
|
||||
_$ServerErrorImpl value,
|
||||
$Res Function(_$ServerErrorImpl) then,
|
||||
) = __$$ServerErrorImplCopyWithImpl<$Res>;
|
||||
@useResult
|
||||
$Res call({ApiFailure failure});
|
||||
|
||||
$ApiFailureCopyWith<$Res> get failure;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$ServerErrorImplCopyWithImpl<$Res>
|
||||
extends _$CategoryFailureCopyWithImpl<$Res, _$ServerErrorImpl>
|
||||
implements _$$ServerErrorImplCopyWith<$Res> {
|
||||
__$$ServerErrorImplCopyWithImpl(
|
||||
_$ServerErrorImpl _value,
|
||||
$Res Function(_$ServerErrorImpl) _then,
|
||||
) : super(_value, _then);
|
||||
|
||||
/// Create a copy of CategoryFailure
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({Object? failure = null}) {
|
||||
return _then(
|
||||
_$ServerErrorImpl(
|
||||
null == failure
|
||||
? _value.failure
|
||||
: failure // ignore: cast_nullable_to_non_nullable
|
||||
as ApiFailure,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Create a copy of CategoryFailure
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$ApiFailureCopyWith<$Res> get failure {
|
||||
return $ApiFailureCopyWith<$Res>(_value.failure, (value) {
|
||||
return _then(_value.copyWith(failure: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$ServerErrorImpl implements _ServerError {
|
||||
const _$ServerErrorImpl(this.failure);
|
||||
|
||||
@override
|
||||
final ApiFailure failure;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CategoryFailure.serverError(failure: $failure)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$ServerErrorImpl &&
|
||||
(identical(other.failure, failure) || other.failure == failure));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, failure);
|
||||
|
||||
/// Create a copy of CategoryFailure
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ServerErrorImplCopyWith<_$ServerErrorImpl> get copyWith =>
|
||||
__$$ServerErrorImplCopyWithImpl<_$ServerErrorImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function(ApiFailure failure) serverError,
|
||||
required TResult Function() unexpectedError,
|
||||
required TResult Function() empty,
|
||||
required TResult Function(String erroMessage) dynamicErrorMessage,
|
||||
}) {
|
||||
return serverError(failure);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function(ApiFailure failure)? serverError,
|
||||
TResult? Function()? unexpectedError,
|
||||
TResult? Function()? empty,
|
||||
TResult? Function(String erroMessage)? dynamicErrorMessage,
|
||||
}) {
|
||||
return serverError?.call(failure);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function(ApiFailure failure)? serverError,
|
||||
TResult Function()? unexpectedError,
|
||||
TResult Function()? empty,
|
||||
TResult Function(String erroMessage)? dynamicErrorMessage,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (serverError != null) {
|
||||
return serverError(failure);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(_ServerError value) serverError,
|
||||
required TResult Function(_UnexpectedError value) unexpectedError,
|
||||
required TResult Function(_Empty value) empty,
|
||||
required TResult Function(_DynamicErrorMessage value) dynamicErrorMessage,
|
||||
}) {
|
||||
return serverError(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(_ServerError value)? serverError,
|
||||
TResult? Function(_UnexpectedError value)? unexpectedError,
|
||||
TResult? Function(_Empty value)? empty,
|
||||
TResult? Function(_DynamicErrorMessage value)? dynamicErrorMessage,
|
||||
}) {
|
||||
return serverError?.call(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(_ServerError value)? serverError,
|
||||
TResult Function(_UnexpectedError value)? unexpectedError,
|
||||
TResult Function(_Empty value)? empty,
|
||||
TResult Function(_DynamicErrorMessage value)? dynamicErrorMessage,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (serverError != null) {
|
||||
return serverError(this);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _ServerError implements CategoryFailure {
|
||||
const factory _ServerError(final ApiFailure failure) = _$ServerErrorImpl;
|
||||
|
||||
ApiFailure get failure;
|
||||
|
||||
/// Create a copy of CategoryFailure
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$ServerErrorImplCopyWith<_$ServerErrorImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$UnexpectedErrorImplCopyWith<$Res> {
|
||||
factory _$$UnexpectedErrorImplCopyWith(
|
||||
_$UnexpectedErrorImpl value,
|
||||
$Res Function(_$UnexpectedErrorImpl) then,
|
||||
) = __$$UnexpectedErrorImplCopyWithImpl<$Res>;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$UnexpectedErrorImplCopyWithImpl<$Res>
|
||||
extends _$CategoryFailureCopyWithImpl<$Res, _$UnexpectedErrorImpl>
|
||||
implements _$$UnexpectedErrorImplCopyWith<$Res> {
|
||||
__$$UnexpectedErrorImplCopyWithImpl(
|
||||
_$UnexpectedErrorImpl _value,
|
||||
$Res Function(_$UnexpectedErrorImpl) _then,
|
||||
) : super(_value, _then);
|
||||
|
||||
/// Create a copy of CategoryFailure
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$UnexpectedErrorImpl implements _UnexpectedError {
|
||||
const _$UnexpectedErrorImpl();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CategoryFailure.unexpectedError()';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType && other is _$UnexpectedErrorImpl);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => runtimeType.hashCode;
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function(ApiFailure failure) serverError,
|
||||
required TResult Function() unexpectedError,
|
||||
required TResult Function() empty,
|
||||
required TResult Function(String erroMessage) dynamicErrorMessage,
|
||||
}) {
|
||||
return unexpectedError();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function(ApiFailure failure)? serverError,
|
||||
TResult? Function()? unexpectedError,
|
||||
TResult? Function()? empty,
|
||||
TResult? Function(String erroMessage)? dynamicErrorMessage,
|
||||
}) {
|
||||
return unexpectedError?.call();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function(ApiFailure failure)? serverError,
|
||||
TResult Function()? unexpectedError,
|
||||
TResult Function()? empty,
|
||||
TResult Function(String erroMessage)? dynamicErrorMessage,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (unexpectedError != null) {
|
||||
return unexpectedError();
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(_ServerError value) serverError,
|
||||
required TResult Function(_UnexpectedError value) unexpectedError,
|
||||
required TResult Function(_Empty value) empty,
|
||||
required TResult Function(_DynamicErrorMessage value) dynamicErrorMessage,
|
||||
}) {
|
||||
return unexpectedError(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(_ServerError value)? serverError,
|
||||
TResult? Function(_UnexpectedError value)? unexpectedError,
|
||||
TResult? Function(_Empty value)? empty,
|
||||
TResult? Function(_DynamicErrorMessage value)? dynamicErrorMessage,
|
||||
}) {
|
||||
return unexpectedError?.call(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(_ServerError value)? serverError,
|
||||
TResult Function(_UnexpectedError value)? unexpectedError,
|
||||
TResult Function(_Empty value)? empty,
|
||||
TResult Function(_DynamicErrorMessage value)? dynamicErrorMessage,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (unexpectedError != null) {
|
||||
return unexpectedError(this);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _UnexpectedError implements CategoryFailure {
|
||||
const factory _UnexpectedError() = _$UnexpectedErrorImpl;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$EmptyImplCopyWith<$Res> {
|
||||
factory _$$EmptyImplCopyWith(
|
||||
_$EmptyImpl value,
|
||||
$Res Function(_$EmptyImpl) then,
|
||||
) = __$$EmptyImplCopyWithImpl<$Res>;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$EmptyImplCopyWithImpl<$Res>
|
||||
extends _$CategoryFailureCopyWithImpl<$Res, _$EmptyImpl>
|
||||
implements _$$EmptyImplCopyWith<$Res> {
|
||||
__$$EmptyImplCopyWithImpl(
|
||||
_$EmptyImpl _value,
|
||||
$Res Function(_$EmptyImpl) _then,
|
||||
) : super(_value, _then);
|
||||
|
||||
/// Create a copy of CategoryFailure
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$EmptyImpl implements _Empty {
|
||||
const _$EmptyImpl();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CategoryFailure.empty()';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType && other is _$EmptyImpl);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => runtimeType.hashCode;
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function(ApiFailure failure) serverError,
|
||||
required TResult Function() unexpectedError,
|
||||
required TResult Function() empty,
|
||||
required TResult Function(String erroMessage) dynamicErrorMessage,
|
||||
}) {
|
||||
return empty();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function(ApiFailure failure)? serverError,
|
||||
TResult? Function()? unexpectedError,
|
||||
TResult? Function()? empty,
|
||||
TResult? Function(String erroMessage)? dynamicErrorMessage,
|
||||
}) {
|
||||
return empty?.call();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function(ApiFailure failure)? serverError,
|
||||
TResult Function()? unexpectedError,
|
||||
TResult Function()? empty,
|
||||
TResult Function(String erroMessage)? dynamicErrorMessage,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (empty != null) {
|
||||
return empty();
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(_ServerError value) serverError,
|
||||
required TResult Function(_UnexpectedError value) unexpectedError,
|
||||
required TResult Function(_Empty value) empty,
|
||||
required TResult Function(_DynamicErrorMessage value) dynamicErrorMessage,
|
||||
}) {
|
||||
return empty(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(_ServerError value)? serverError,
|
||||
TResult? Function(_UnexpectedError value)? unexpectedError,
|
||||
TResult? Function(_Empty value)? empty,
|
||||
TResult? Function(_DynamicErrorMessage value)? dynamicErrorMessage,
|
||||
}) {
|
||||
return empty?.call(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(_ServerError value)? serverError,
|
||||
TResult Function(_UnexpectedError value)? unexpectedError,
|
||||
TResult Function(_Empty value)? empty,
|
||||
TResult Function(_DynamicErrorMessage value)? dynamicErrorMessage,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (empty != null) {
|
||||
return empty(this);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _Empty implements CategoryFailure {
|
||||
const factory _Empty() = _$EmptyImpl;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$DynamicErrorMessageImplCopyWith<$Res> {
|
||||
factory _$$DynamicErrorMessageImplCopyWith(
|
||||
_$DynamicErrorMessageImpl value,
|
||||
$Res Function(_$DynamicErrorMessageImpl) then,
|
||||
) = __$$DynamicErrorMessageImplCopyWithImpl<$Res>;
|
||||
@useResult
|
||||
$Res call({String erroMessage});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$DynamicErrorMessageImplCopyWithImpl<$Res>
|
||||
extends _$CategoryFailureCopyWithImpl<$Res, _$DynamicErrorMessageImpl>
|
||||
implements _$$DynamicErrorMessageImplCopyWith<$Res> {
|
||||
__$$DynamicErrorMessageImplCopyWithImpl(
|
||||
_$DynamicErrorMessageImpl _value,
|
||||
$Res Function(_$DynamicErrorMessageImpl) _then,
|
||||
) : super(_value, _then);
|
||||
|
||||
/// Create a copy of CategoryFailure
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({Object? erroMessage = null}) {
|
||||
return _then(
|
||||
_$DynamicErrorMessageImpl(
|
||||
null == erroMessage
|
||||
? _value.erroMessage
|
||||
: erroMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$DynamicErrorMessageImpl implements _DynamicErrorMessage {
|
||||
const _$DynamicErrorMessageImpl(this.erroMessage);
|
||||
|
||||
@override
|
||||
final String erroMessage;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CategoryFailure.dynamicErrorMessage(erroMessage: $erroMessage)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$DynamicErrorMessageImpl &&
|
||||
(identical(other.erroMessage, erroMessage) ||
|
||||
other.erroMessage == erroMessage));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, erroMessage);
|
||||
|
||||
/// Create a copy of CategoryFailure
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$DynamicErrorMessageImplCopyWith<_$DynamicErrorMessageImpl> get copyWith =>
|
||||
__$$DynamicErrorMessageImplCopyWithImpl<_$DynamicErrorMessageImpl>(
|
||||
this,
|
||||
_$identity,
|
||||
);
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function(ApiFailure failure) serverError,
|
||||
required TResult Function() unexpectedError,
|
||||
required TResult Function() empty,
|
||||
required TResult Function(String erroMessage) dynamicErrorMessage,
|
||||
}) {
|
||||
return dynamicErrorMessage(erroMessage);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult? Function(ApiFailure failure)? serverError,
|
||||
TResult? Function()? unexpectedError,
|
||||
TResult? Function()? empty,
|
||||
TResult? Function(String erroMessage)? dynamicErrorMessage,
|
||||
}) {
|
||||
return dynamicErrorMessage?.call(erroMessage);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function(ApiFailure failure)? serverError,
|
||||
TResult Function()? unexpectedError,
|
||||
TResult Function()? empty,
|
||||
TResult Function(String erroMessage)? dynamicErrorMessage,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (dynamicErrorMessage != null) {
|
||||
return dynamicErrorMessage(erroMessage);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(_ServerError value) serverError,
|
||||
required TResult Function(_UnexpectedError value) unexpectedError,
|
||||
required TResult Function(_Empty value) empty,
|
||||
required TResult Function(_DynamicErrorMessage value) dynamicErrorMessage,
|
||||
}) {
|
||||
return dynamicErrorMessage(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult? Function(_ServerError value)? serverError,
|
||||
TResult? Function(_UnexpectedError value)? unexpectedError,
|
||||
TResult? Function(_Empty value)? empty,
|
||||
TResult? Function(_DynamicErrorMessage value)? dynamicErrorMessage,
|
||||
}) {
|
||||
return dynamicErrorMessage?.call(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(_ServerError value)? serverError,
|
||||
TResult Function(_UnexpectedError value)? unexpectedError,
|
||||
TResult Function(_Empty value)? empty,
|
||||
TResult Function(_DynamicErrorMessage value)? dynamicErrorMessage,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (dynamicErrorMessage != null) {
|
||||
return dynamicErrorMessage(this);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _DynamicErrorMessage implements CategoryFailure {
|
||||
const factory _DynamicErrorMessage(final String erroMessage) =
|
||||
_$DynamicErrorMessageImpl;
|
||||
|
||||
String get erroMessage;
|
||||
|
||||
/// Create a copy of CategoryFailure
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$DynamicErrorMessageImplCopyWith<_$DynamicErrorMessageImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
33
lib/domain/category/entities/category_entity.dart
Normal file
33
lib/domain/category/entities/category_entity.dart
Normal file
@ -0,0 +1,33 @@
|
||||
part of '../category.dart';
|
||||
|
||||
@freezed
|
||||
class Category with _$Category {
|
||||
const factory Category({
|
||||
required String id,
|
||||
required String organizationId,
|
||||
required String name,
|
||||
required String description,
|
||||
required String businessType,
|
||||
required Map<String, dynamic> metadata,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
}) = _Category;
|
||||
|
||||
factory Category.empty() => const Category(
|
||||
id: '',
|
||||
organizationId: '',
|
||||
name: '',
|
||||
description: '',
|
||||
businessType: '',
|
||||
metadata: {},
|
||||
);
|
||||
|
||||
factory Category.addAllData() => Category(
|
||||
id: 'all',
|
||||
organizationId: '',
|
||||
name: 'Semua',
|
||||
description: '',
|
||||
businessType: '',
|
||||
metadata: {},
|
||||
);
|
||||
}
|
||||
10
lib/domain/category/failures/category_failure.dart
Normal file
10
lib/domain/category/failures/category_failure.dart
Normal file
@ -0,0 +1,10 @@
|
||||
part of '../category.dart';
|
||||
|
||||
@freezed
|
||||
sealed class CategoryFailure with _$CategoryFailure {
|
||||
const factory CategoryFailure.serverError(ApiFailure failure) = _ServerError;
|
||||
const factory CategoryFailure.unexpectedError() = _UnexpectedError;
|
||||
const factory CategoryFailure.empty() = _Empty;
|
||||
const factory CategoryFailure.dynamicErrorMessage(String erroMessage) =
|
||||
_DynamicErrorMessage;
|
||||
}
|
||||
9
lib/domain/category/repositories/i_auth_repository.dart
Normal file
9
lib/domain/category/repositories/i_auth_repository.dart
Normal file
@ -0,0 +1,9 @@
|
||||
part of '../category.dart';
|
||||
|
||||
abstract class ICategoryRepository {
|
||||
Future<Either<CategoryFailure, List<Category>>> get({
|
||||
int page = 1,
|
||||
int limit = 20,
|
||||
bool isActive = true,
|
||||
});
|
||||
}
|
||||
43
lib/domain/product/entities/product_entity.dart
Normal file
43
lib/domain/product/entities/product_entity.dart
Normal file
@ -0,0 +1,43 @@
|
||||
part of '../product.dart';
|
||||
|
||||
@freezed
|
||||
class Product with _$Product {
|
||||
const factory Product({
|
||||
required String id,
|
||||
required String organizationId,
|
||||
required String categoryId,
|
||||
required String sku,
|
||||
required String name,
|
||||
required String description,
|
||||
required int price,
|
||||
required int cost,
|
||||
required String businessType,
|
||||
required String imageUrl,
|
||||
required String printerType,
|
||||
required Map<String, dynamic> metadata,
|
||||
required bool isActive,
|
||||
required DateTime createdAt,
|
||||
required DateTime updatedAt,
|
||||
required List<ProductVariant> variants,
|
||||
}) = _Product;
|
||||
|
||||
/// âś… factory kosong untuk default state
|
||||
factory Product.empty() => Product(
|
||||
id: '',
|
||||
organizationId: '',
|
||||
categoryId: '',
|
||||
sku: '',
|
||||
name: '',
|
||||
description: '',
|
||||
price: 0,
|
||||
cost: 0,
|
||||
businessType: '',
|
||||
imageUrl: '',
|
||||
printerType: '',
|
||||
metadata: {},
|
||||
isActive: false,
|
||||
createdAt: DateTime.now(),
|
||||
updatedAt: DateTime.now(),
|
||||
variants: [],
|
||||
);
|
||||
}
|
||||
26
lib/domain/product/entities/product_variant_entity.dart
Normal file
26
lib/domain/product/entities/product_variant_entity.dart
Normal file
@ -0,0 +1,26 @@
|
||||
part of '../product.dart';
|
||||
|
||||
@freezed
|
||||
class ProductVariant with _$ProductVariant {
|
||||
const factory ProductVariant({
|
||||
required String id,
|
||||
required String productId,
|
||||
required String name,
|
||||
required int priceModifier,
|
||||
required int cost,
|
||||
required Map<String, dynamic> metadata,
|
||||
required DateTime createdAt,
|
||||
required DateTime updatedAt,
|
||||
}) = _ProductVariant;
|
||||
|
||||
factory ProductVariant.empty() => ProductVariant(
|
||||
id: '',
|
||||
productId: '',
|
||||
name: '',
|
||||
priceModifier: 0,
|
||||
cost: 0,
|
||||
metadata: {},
|
||||
createdAt: DateTime.now(),
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
}
|
||||
10
lib/domain/product/failures/product_failure.dart
Normal file
10
lib/domain/product/failures/product_failure.dart
Normal file
@ -0,0 +1,10 @@
|
||||
part of '../product.dart';
|
||||
|
||||
@freezed
|
||||
sealed class ProductFailure with _$ProductFailure {
|
||||
const factory ProductFailure.serverError(ApiFailure failure) = _ServerError;
|
||||
const factory ProductFailure.unexpectedError() = _UnexpectedError;
|
||||
const factory ProductFailure.empty() = _Empty;
|
||||
const factory ProductFailure.dynamicErrorMessage(String erroMessage) =
|
||||
_DynamicErrorMessage;
|
||||
}
|
||||
11
lib/domain/product/product.dart
Normal file
11
lib/domain/product/product.dart
Normal file
@ -0,0 +1,11 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import '../../common/api/api_failure.dart';
|
||||
|
||||
part 'product.freezed.dart';
|
||||
|
||||
part 'entities/product_entity.dart';
|
||||
part 'entities/product_variant_entity.dart';
|
||||
part 'failures/product_failure.dart';
|
||||
part 'repositories/i_product_repository.dart';
|
||||
1435
lib/domain/product/product.freezed.dart
Normal file
1435
lib/domain/product/product.freezed.dart
Normal file
File diff suppressed because it is too large
Load Diff
10
lib/domain/product/repositories/i_product_repository.dart
Normal file
10
lib/domain/product/repositories/i_product_repository.dart
Normal file
@ -0,0 +1,10 @@
|
||||
part of '../product.dart';
|
||||
|
||||
abstract class IProductRepository {
|
||||
Future<Either<ProductFailure, List<Product>>> get({
|
||||
int page = 1,
|
||||
int limit = 10,
|
||||
String? categoryId,
|
||||
String? search,
|
||||
});
|
||||
}
|
||||
10
lib/infrastructure/analytic/analytic_dtos.dart
Normal file
10
lib/infrastructure/analytic/analytic_dtos.dart
Normal file
@ -0,0 +1,10 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import '../../domain/analytic/analytic.dart';
|
||||
|
||||
part 'analytic_dtos.freezed.dart';
|
||||
part 'analytic_dtos.g.dart';
|
||||
|
||||
part 'dto/sales_analytic_dto.dart';
|
||||
part 'dto/profit_loss_analytic_dto.dart';
|
||||
part 'dto/category_analytic_dto.dart';
|
||||
3260
lib/infrastructure/analytic/analytic_dtos.freezed.dart
Normal file
3260
lib/infrastructure/analytic/analytic_dtos.freezed.dart
Normal file
File diff suppressed because it is too large
Load Diff
259
lib/infrastructure/analytic/analytic_dtos.g.dart
Normal file
259
lib/infrastructure/analytic/analytic_dtos.g.dart
Normal file
@ -0,0 +1,259 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'analytic_dtos.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$SalesAnalyticDtoImpl _$$SalesAnalyticDtoImplFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _$SalesAnalyticDtoImpl(
|
||||
organizationId: json['organization_id'] as String?,
|
||||
outletId: json['outlet_id'] as String?,
|
||||
dateFrom: json['date_from'] == null
|
||||
? null
|
||||
: DateTime.parse(json['date_from'] as String),
|
||||
dateTo: json['date_to'] == null
|
||||
? null
|
||||
: DateTime.parse(json['date_to'] as String),
|
||||
groupBy: json['group_by'] as String?,
|
||||
summary: json['summary'] == null
|
||||
? null
|
||||
: SalesAnalyticSummaryDto.fromJson(
|
||||
json['summary'] as Map<String, dynamic>,
|
||||
),
|
||||
data: (json['data'] as List<dynamic>?)
|
||||
?.map((e) => SalesAnalyticDataDto.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SalesAnalyticDtoImplToJson(
|
||||
_$SalesAnalyticDtoImpl instance,
|
||||
) => <String, dynamic>{
|
||||
'organization_id': instance.organizationId,
|
||||
'outlet_id': instance.outletId,
|
||||
'date_from': instance.dateFrom?.toIso8601String(),
|
||||
'date_to': instance.dateTo?.toIso8601String(),
|
||||
'group_by': instance.groupBy,
|
||||
'summary': instance.summary,
|
||||
'data': instance.data,
|
||||
};
|
||||
|
||||
_$SalesAnalyticSummaryDtoImpl _$$SalesAnalyticSummaryDtoImplFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _$SalesAnalyticSummaryDtoImpl(
|
||||
totalSales: json['total_sales'] as num?,
|
||||
totalOrders: json['total_orders'] as num?,
|
||||
totalItems: json['total_items'] as num?,
|
||||
averageOrderValue: json['average_order_value'] as num?,
|
||||
totalTax: json['total_tax'] as num?,
|
||||
totalDiscount: json['total_discount'] as num?,
|
||||
netSales: json['net_sales'] as num?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SalesAnalyticSummaryDtoImplToJson(
|
||||
_$SalesAnalyticSummaryDtoImpl instance,
|
||||
) => <String, dynamic>{
|
||||
'total_sales': instance.totalSales,
|
||||
'total_orders': instance.totalOrders,
|
||||
'total_items': instance.totalItems,
|
||||
'average_order_value': instance.averageOrderValue,
|
||||
'total_tax': instance.totalTax,
|
||||
'total_discount': instance.totalDiscount,
|
||||
'net_sales': instance.netSales,
|
||||
};
|
||||
|
||||
_$SalesAnalyticDataDtoImpl _$$SalesAnalyticDataDtoImplFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _$SalesAnalyticDataDtoImpl(
|
||||
date: json['date'] == null ? null : DateTime.parse(json['date'] as String),
|
||||
sales: json['sales'] as num?,
|
||||
orders: json['orders'] as num?,
|
||||
items: json['items'] as num?,
|
||||
tax: json['tax'] as num?,
|
||||
discount: json['discount'] as num?,
|
||||
netSales: json['net_sales'] as num?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SalesAnalyticDataDtoImplToJson(
|
||||
_$SalesAnalyticDataDtoImpl instance,
|
||||
) => <String, dynamic>{
|
||||
'date': instance.date?.toIso8601String(),
|
||||
'sales': instance.sales,
|
||||
'orders': instance.orders,
|
||||
'items': instance.items,
|
||||
'tax': instance.tax,
|
||||
'discount': instance.discount,
|
||||
'net_sales': instance.netSales,
|
||||
};
|
||||
|
||||
_$ProfitLossAnalyticDtoImpl _$$ProfitLossAnalyticDtoImplFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _$ProfitLossAnalyticDtoImpl(
|
||||
organizationId: json['organization_id'] as String?,
|
||||
dateFrom: json['date_from'] as String?,
|
||||
dateTo: json['date_to'] as String?,
|
||||
groupBy: json['group_by'] as String?,
|
||||
summary: json['summary'] == null
|
||||
? null
|
||||
: ProfitLossSummaryDto.fromJson(json['summary'] as Map<String, dynamic>),
|
||||
data: (json['data'] as List<dynamic>?)
|
||||
?.map((e) => ProfitLossDailyDataDto.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
productData: (json['product_data'] as List<dynamic>?)
|
||||
?.map((e) => ProfitLossProductDataDto.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ProfitLossAnalyticDtoImplToJson(
|
||||
_$ProfitLossAnalyticDtoImpl instance,
|
||||
) => <String, dynamic>{
|
||||
'organization_id': instance.organizationId,
|
||||
'date_from': instance.dateFrom,
|
||||
'date_to': instance.dateTo,
|
||||
'group_by': instance.groupBy,
|
||||
'summary': instance.summary,
|
||||
'data': instance.data,
|
||||
'product_data': instance.productData,
|
||||
};
|
||||
|
||||
_$ProfitLossSummaryDtoImpl _$$ProfitLossSummaryDtoImplFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _$ProfitLossSummaryDtoImpl(
|
||||
totalRevenue: (json['total_revenue'] as num?)?.toInt(),
|
||||
totalCost: (json['total_cost'] as num?)?.toInt(),
|
||||
grossProfit: (json['gross_profit'] as num?)?.toInt(),
|
||||
grossProfitMargin: (json['gross_profit_margin'] as num?)?.toDouble(),
|
||||
totalTax: (json['total_tax'] as num?)?.toInt(),
|
||||
totalDiscount: (json['total_discount'] as num?)?.toInt(),
|
||||
netProfit: (json['net_profit'] as num?)?.toInt(),
|
||||
netProfitMargin: (json['net_profit_margin'] as num?)?.toDouble(),
|
||||
totalOrders: (json['total_orders'] as num?)?.toInt(),
|
||||
averageProfit: (json['average_profit'] as num?)?.toDouble(),
|
||||
profitabilityRatio: (json['profitability_ratio'] as num?)?.toDouble(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ProfitLossSummaryDtoImplToJson(
|
||||
_$ProfitLossSummaryDtoImpl instance,
|
||||
) => <String, dynamic>{
|
||||
'total_revenue': instance.totalRevenue,
|
||||
'total_cost': instance.totalCost,
|
||||
'gross_profit': instance.grossProfit,
|
||||
'gross_profit_margin': instance.grossProfitMargin,
|
||||
'total_tax': instance.totalTax,
|
||||
'total_discount': instance.totalDiscount,
|
||||
'net_profit': instance.netProfit,
|
||||
'net_profit_margin': instance.netProfitMargin,
|
||||
'total_orders': instance.totalOrders,
|
||||
'average_profit': instance.averageProfit,
|
||||
'profitability_ratio': instance.profitabilityRatio,
|
||||
};
|
||||
|
||||
_$ProfitLossDailyDataDtoImpl _$$ProfitLossDailyDataDtoImplFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _$ProfitLossDailyDataDtoImpl(
|
||||
date: json['date'] as String?,
|
||||
revenue: (json['revenue'] as num?)?.toInt(),
|
||||
cost: (json['cost'] as num?)?.toInt(),
|
||||
grossProfit: (json['gross_profit'] as num?)?.toInt(),
|
||||
grossProfitMargin: (json['gross_profit_margin'] as num?)?.toDouble(),
|
||||
tax: (json['tax'] as num?)?.toInt(),
|
||||
discount: (json['discount'] as num?)?.toInt(),
|
||||
netProfit: (json['net_profit'] as num?)?.toInt(),
|
||||
netProfitMargin: (json['net_profit_margin'] as num?)?.toDouble(),
|
||||
orders: (json['orders'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ProfitLossDailyDataDtoImplToJson(
|
||||
_$ProfitLossDailyDataDtoImpl instance,
|
||||
) => <String, dynamic>{
|
||||
'date': instance.date,
|
||||
'revenue': instance.revenue,
|
||||
'cost': instance.cost,
|
||||
'gross_profit': instance.grossProfit,
|
||||
'gross_profit_margin': instance.grossProfitMargin,
|
||||
'tax': instance.tax,
|
||||
'discount': instance.discount,
|
||||
'net_profit': instance.netProfit,
|
||||
'net_profit_margin': instance.netProfitMargin,
|
||||
'orders': instance.orders,
|
||||
};
|
||||
|
||||
_$ProfitLossProductDataDtoImpl _$$ProfitLossProductDataDtoImplFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _$ProfitLossProductDataDtoImpl(
|
||||
productId: json['product_id'] as String?,
|
||||
productName: json['product_name'] as String?,
|
||||
categoryId: json['category_id'] as String?,
|
||||
categoryName: json['category_name'] as String?,
|
||||
quantitySold: (json['quantity_sold'] as num?)?.toInt(),
|
||||
revenue: (json['revenue'] as num?)?.toInt(),
|
||||
cost: (json['cost'] as num?)?.toInt(),
|
||||
grossProfit: (json['gross_profit'] as num?)?.toInt(),
|
||||
grossProfitMargin: (json['gross_profit_margin'] as num?)?.toDouble(),
|
||||
averagePrice: (json['average_price'] as num?)?.toInt(),
|
||||
averageCost: (json['average_cost'] as num?)?.toInt(),
|
||||
profitPerUnit: (json['profit_per_unit'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ProfitLossProductDataDtoImplToJson(
|
||||
_$ProfitLossProductDataDtoImpl instance,
|
||||
) => <String, dynamic>{
|
||||
'product_id': instance.productId,
|
||||
'product_name': instance.productName,
|
||||
'category_id': instance.categoryId,
|
||||
'category_name': instance.categoryName,
|
||||
'quantity_sold': instance.quantitySold,
|
||||
'revenue': instance.revenue,
|
||||
'cost': instance.cost,
|
||||
'gross_profit': instance.grossProfit,
|
||||
'gross_profit_margin': instance.grossProfitMargin,
|
||||
'average_price': instance.averagePrice,
|
||||
'average_cost': instance.averageCost,
|
||||
'profit_per_unit': instance.profitPerUnit,
|
||||
};
|
||||
|
||||
_$CategoryAnalyticDtoImpl _$$CategoryAnalyticDtoImplFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _$CategoryAnalyticDtoImpl(
|
||||
organizationId: json['organization_id'] as String?,
|
||||
outletId: json['outlet_id'] as String?,
|
||||
dateFrom: json['date_from'] as String?,
|
||||
dateTo: json['date_to'] as String?,
|
||||
data: (json['data'] as List<dynamic>?)
|
||||
?.map((e) => CategoryAnalyticItemDto.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$CategoryAnalyticDtoImplToJson(
|
||||
_$CategoryAnalyticDtoImpl instance,
|
||||
) => <String, dynamic>{
|
||||
'organization_id': instance.organizationId,
|
||||
'outlet_id': instance.outletId,
|
||||
'date_from': instance.dateFrom,
|
||||
'date_to': instance.dateTo,
|
||||
'data': instance.data,
|
||||
};
|
||||
|
||||
_$CategoryAnalyticItemDtoImpl _$$CategoryAnalyticItemDtoImplFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _$CategoryAnalyticItemDtoImpl(
|
||||
categoryId: json['category_id'] as String?,
|
||||
categoryName: json['category_name'] as String?,
|
||||
totalRevenue: (json['total_revenue'] as num?)?.toInt(),
|
||||
totalQuantity: (json['total_quantity'] as num?)?.toInt(),
|
||||
productCount: (json['product_count'] as num?)?.toInt(),
|
||||
orderCount: (json['order_count'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$CategoryAnalyticItemDtoImplToJson(
|
||||
_$CategoryAnalyticItemDtoImpl instance,
|
||||
) => <String, dynamic>{
|
||||
'category_id': instance.categoryId,
|
||||
'category_name': instance.categoryName,
|
||||
'total_revenue': instance.totalRevenue,
|
||||
'total_quantity': instance.totalQuantity,
|
||||
'product_count': instance.productCount,
|
||||
'order_count': instance.orderCount,
|
||||
};
|
||||
101
lib/infrastructure/analytic/datasource/remote_data_provider.dart
Normal file
101
lib/infrastructure/analytic/datasource/remote_data_provider.dart
Normal file
@ -0,0 +1,101 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:data_channel/data_channel.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
|
||||
import '../../../common/api/api_client.dart';
|
||||
import '../../../common/api/api_failure.dart';
|
||||
import '../../../common/extension/extension.dart';
|
||||
import '../../../common/function/app_function.dart';
|
||||
import '../../../common/url/api_path.dart';
|
||||
import '../../../domain/analytic/analytic.dart';
|
||||
import '../analytic_dtos.dart';
|
||||
|
||||
@injectable
|
||||
class AnalyticRemoteDataProvider {
|
||||
final ApiClient _apiClient;
|
||||
final String _logName = "AnalyticRemoteDataProvider";
|
||||
|
||||
AnalyticRemoteDataProvider(this._apiClient);
|
||||
|
||||
Future<DC<AnalyticFailure, SalesAnalyticDto>> fetchSales({
|
||||
required DateTime dateFrom,
|
||||
required DateTime dateTo,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _apiClient.get(
|
||||
ApiPath.salesAnalytic,
|
||||
params: {
|
||||
'date_from': dateFrom.toServerDate,
|
||||
'date_to': dateTo.toServerDate,
|
||||
},
|
||||
headers: getAuthorizationHeader(),
|
||||
);
|
||||
|
||||
if (response.data['data'] == null) {
|
||||
return DC.error(AnalyticFailure.empty());
|
||||
}
|
||||
|
||||
final dto = SalesAnalyticDto.fromJson(response.data['data']);
|
||||
|
||||
return DC.data(dto);
|
||||
} on ApiFailure catch (e, s) {
|
||||
log('fetchSalesError', name: _logName, error: e, stackTrace: s);
|
||||
return DC.error(AnalyticFailure.serverError(e));
|
||||
}
|
||||
}
|
||||
|
||||
Future<DC<AnalyticFailure, ProfitLossAnalyticDto>> fetchProfitLoss({
|
||||
required DateTime dateFrom,
|
||||
required DateTime dateTo,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _apiClient.get(
|
||||
ApiPath.profitLossAnalytic,
|
||||
params: {
|
||||
'date_from': dateFrom.toServerDate,
|
||||
'date_to': dateTo.toServerDate,
|
||||
},
|
||||
headers: getAuthorizationHeader(),
|
||||
);
|
||||
|
||||
if (response.data['data'] == null) {
|
||||
return DC.error(AnalyticFailure.empty());
|
||||
}
|
||||
|
||||
final dto = ProfitLossAnalyticDto.fromJson(response.data['data']);
|
||||
|
||||
return DC.data(dto);
|
||||
} on ApiFailure catch (e, s) {
|
||||
log('fetchProfitLossError', name: _logName, error: e, stackTrace: s);
|
||||
return DC.error(AnalyticFailure.serverError(e));
|
||||
}
|
||||
}
|
||||
|
||||
Future<DC<AnalyticFailure, CategoryAnalyticDto>> fetchCategory({
|
||||
required DateTime dateFrom,
|
||||
required DateTime dateTo,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _apiClient.get(
|
||||
ApiPath.categoryAnalytic,
|
||||
params: {
|
||||
'date_from': dateFrom.toServerDate,
|
||||
'date_to': dateTo.toServerDate,
|
||||
},
|
||||
headers: getAuthorizationHeader(),
|
||||
);
|
||||
|
||||
if (response.data['data'] == null) {
|
||||
return DC.error(AnalyticFailure.empty());
|
||||
}
|
||||
|
||||
final dto = CategoryAnalyticDto.fromJson(response.data['data']);
|
||||
|
||||
return DC.data(dto);
|
||||
} on ApiFailure catch (e, s) {
|
||||
log('fetchCategoryError', name: _logName, error: e, stackTrace: s);
|
||||
return DC.error(AnalyticFailure.serverError(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
51
lib/infrastructure/analytic/dto/category_analytic_dto.dart
Normal file
51
lib/infrastructure/analytic/dto/category_analytic_dto.dart
Normal file
@ -0,0 +1,51 @@
|
||||
part of '../analytic_dtos.dart';
|
||||
|
||||
@freezed
|
||||
class CategoryAnalyticDto with _$CategoryAnalyticDto {
|
||||
const CategoryAnalyticDto._();
|
||||
|
||||
const factory CategoryAnalyticDto({
|
||||
@JsonKey(name: 'organization_id') String? organizationId,
|
||||
@JsonKey(name: 'outlet_id') String? outletId,
|
||||
@JsonKey(name: 'date_from') String? dateFrom,
|
||||
@JsonKey(name: 'date_to') String? dateTo,
|
||||
@JsonKey(name: 'data') List<CategoryAnalyticItemDto>? data,
|
||||
}) = _CategoryAnalyticDto;
|
||||
|
||||
factory CategoryAnalyticDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$CategoryAnalyticDtoFromJson(json);
|
||||
|
||||
CategoryAnalytic toDomain() => CategoryAnalytic(
|
||||
organizationId: organizationId ?? "",
|
||||
outletId: outletId ?? "",
|
||||
dateFrom: dateFrom ?? "",
|
||||
dateTo: dateTo ?? "",
|
||||
data: data?.map((e) => e.toDomain()).toList() ?? [],
|
||||
);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class CategoryAnalyticItemDto with _$CategoryAnalyticItemDto {
|
||||
const CategoryAnalyticItemDto._();
|
||||
|
||||
const factory CategoryAnalyticItemDto({
|
||||
@JsonKey(name: 'category_id') String? categoryId,
|
||||
@JsonKey(name: 'category_name') String? categoryName,
|
||||
@JsonKey(name: 'total_revenue') int? totalRevenue,
|
||||
@JsonKey(name: 'total_quantity') int? totalQuantity,
|
||||
@JsonKey(name: 'product_count') int? productCount,
|
||||
@JsonKey(name: 'order_count') int? orderCount,
|
||||
}) = _CategoryAnalyticItemDto;
|
||||
|
||||
factory CategoryAnalyticItemDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$CategoryAnalyticItemDtoFromJson(json);
|
||||
|
||||
CategoryAnalyticItem toDomain() => CategoryAnalyticItem(
|
||||
categoryId: categoryId ?? "",
|
||||
categoryName: categoryName ?? "",
|
||||
totalRevenue: totalRevenue ?? 0,
|
||||
totalQuantity: totalQuantity ?? 0,
|
||||
productCount: productCount ?? 0,
|
||||
orderCount: orderCount ?? 0,
|
||||
);
|
||||
}
|
||||
137
lib/infrastructure/analytic/dto/profit_loss_analytic_dto.dart
Normal file
137
lib/infrastructure/analytic/dto/profit_loss_analytic_dto.dart
Normal file
@ -0,0 +1,137 @@
|
||||
part of '../analytic_dtos.dart';
|
||||
|
||||
@freezed
|
||||
class ProfitLossAnalyticDto with _$ProfitLossAnalyticDto {
|
||||
const ProfitLossAnalyticDto._();
|
||||
|
||||
const factory ProfitLossAnalyticDto({
|
||||
@JsonKey(name: 'organization_id') String? organizationId,
|
||||
@JsonKey(name: 'date_from') String? dateFrom,
|
||||
@JsonKey(name: 'date_to') String? dateTo,
|
||||
@JsonKey(name: 'group_by') String? groupBy,
|
||||
@JsonKey(name: 'summary') ProfitLossSummaryDto? summary,
|
||||
@JsonKey(name: 'data') List<ProfitLossDailyDataDto>? data,
|
||||
@JsonKey(name: 'product_data') List<ProfitLossProductDataDto>? productData,
|
||||
}) = _ProfitLossAnalyticDto;
|
||||
|
||||
factory ProfitLossAnalyticDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$ProfitLossAnalyticDtoFromJson(json);
|
||||
|
||||
ProfitLossAnalytic toDomain() => ProfitLossAnalytic(
|
||||
organizationId: organizationId ?? '',
|
||||
dateFrom: dateFrom ?? '',
|
||||
dateTo: dateTo ?? '',
|
||||
groupBy: groupBy ?? '',
|
||||
summary: summary?.toDomain() ?? ProfitLossSummary.empty(),
|
||||
data: (data ?? []).map((e) => e.toDomain()).toList(),
|
||||
productData: (productData ?? []).map((e) => e.toDomain()).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ProfitLossSummaryDto with _$ProfitLossSummaryDto {
|
||||
const ProfitLossSummaryDto._();
|
||||
|
||||
const factory ProfitLossSummaryDto({
|
||||
@JsonKey(name: 'total_revenue') int? totalRevenue,
|
||||
@JsonKey(name: 'total_cost') int? totalCost,
|
||||
@JsonKey(name: 'gross_profit') int? grossProfit,
|
||||
@JsonKey(name: 'gross_profit_margin') double? grossProfitMargin,
|
||||
@JsonKey(name: 'total_tax') int? totalTax,
|
||||
@JsonKey(name: 'total_discount') int? totalDiscount,
|
||||
@JsonKey(name: 'net_profit') int? netProfit,
|
||||
@JsonKey(name: 'net_profit_margin') double? netProfitMargin,
|
||||
@JsonKey(name: 'total_orders') int? totalOrders,
|
||||
@JsonKey(name: 'average_profit') double? averageProfit,
|
||||
@JsonKey(name: 'profitability_ratio') double? profitabilityRatio,
|
||||
}) = _ProfitLossSummaryDto;
|
||||
|
||||
factory ProfitLossSummaryDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$ProfitLossSummaryDtoFromJson(json);
|
||||
|
||||
ProfitLossSummary toDomain() => ProfitLossSummary(
|
||||
totalRevenue: totalRevenue ?? 0,
|
||||
totalCost: totalCost ?? 0,
|
||||
grossProfit: grossProfit ?? 0,
|
||||
grossProfitMargin: grossProfitMargin ?? 0.0,
|
||||
totalTax: totalTax ?? 0,
|
||||
totalDiscount: totalDiscount ?? 0,
|
||||
netProfit: netProfit ?? 0,
|
||||
netProfitMargin: netProfitMargin ?? 0.0,
|
||||
totalOrders: totalOrders ?? 0,
|
||||
averageProfit: averageProfit ?? 0.0,
|
||||
profitabilityRatio: profitabilityRatio ?? 0.0,
|
||||
);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ProfitLossDailyDataDto with _$ProfitLossDailyDataDto {
|
||||
const ProfitLossDailyDataDto._();
|
||||
|
||||
const factory ProfitLossDailyDataDto({
|
||||
@JsonKey(name: 'date') String? date,
|
||||
@JsonKey(name: 'revenue') int? revenue,
|
||||
@JsonKey(name: 'cost') int? cost,
|
||||
@JsonKey(name: 'gross_profit') int? grossProfit,
|
||||
@JsonKey(name: 'gross_profit_margin') double? grossProfitMargin,
|
||||
@JsonKey(name: 'tax') int? tax,
|
||||
@JsonKey(name: 'discount') int? discount,
|
||||
@JsonKey(name: 'net_profit') int? netProfit,
|
||||
@JsonKey(name: 'net_profit_margin') double? netProfitMargin,
|
||||
@JsonKey(name: 'orders') int? orders,
|
||||
}) = _ProfitLossDailyDataDto;
|
||||
|
||||
factory ProfitLossDailyDataDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$ProfitLossDailyDataDtoFromJson(json);
|
||||
|
||||
ProfitLossDailyData toDomain() => ProfitLossDailyData(
|
||||
date: date ?? '',
|
||||
revenue: revenue ?? 0,
|
||||
cost: cost ?? 0,
|
||||
grossProfit: grossProfit ?? 0,
|
||||
grossProfitMargin: grossProfitMargin ?? 0.0,
|
||||
tax: tax ?? 0,
|
||||
discount: discount ?? 0,
|
||||
netProfit: netProfit ?? 0,
|
||||
netProfitMargin: netProfitMargin ?? 0.0,
|
||||
orders: orders ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ProfitLossProductDataDto with _$ProfitLossProductDataDto {
|
||||
const ProfitLossProductDataDto._();
|
||||
|
||||
const factory ProfitLossProductDataDto({
|
||||
@JsonKey(name: 'product_id') String? productId,
|
||||
@JsonKey(name: 'product_name') String? productName,
|
||||
@JsonKey(name: 'category_id') String? categoryId,
|
||||
@JsonKey(name: 'category_name') String? categoryName,
|
||||
@JsonKey(name: 'quantity_sold') int? quantitySold,
|
||||
@JsonKey(name: 'revenue') int? revenue,
|
||||
@JsonKey(name: 'cost') int? cost,
|
||||
@JsonKey(name: 'gross_profit') int? grossProfit,
|
||||
@JsonKey(name: 'gross_profit_margin') double? grossProfitMargin,
|
||||
@JsonKey(name: 'average_price') int? averagePrice,
|
||||
@JsonKey(name: 'average_cost') int? averageCost,
|
||||
@JsonKey(name: 'profit_per_unit') int? profitPerUnit,
|
||||
}) = _ProfitLossProductDataDto;
|
||||
|
||||
factory ProfitLossProductDataDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$ProfitLossProductDataDtoFromJson(json);
|
||||
|
||||
ProfitLossProductData toDomain() => ProfitLossProductData(
|
||||
productId: productId ?? '',
|
||||
productName: productName ?? '',
|
||||
categoryId: categoryId ?? '',
|
||||
categoryName: categoryName ?? '',
|
||||
quantitySold: quantitySold ?? 0,
|
||||
revenue: revenue ?? 0,
|
||||
cost: cost ?? 0,
|
||||
grossProfit: grossProfit ?? 0,
|
||||
grossProfitMargin: grossProfitMargin ?? 0.0,
|
||||
averagePrice: averagePrice ?? 0,
|
||||
averageCost: averageCost ?? 0,
|
||||
profitPerUnit: profitPerUnit ?? 0,
|
||||
);
|
||||
}
|
||||
85
lib/infrastructure/analytic/dto/sales_analytic_dto.dart
Normal file
85
lib/infrastructure/analytic/dto/sales_analytic_dto.dart
Normal file
@ -0,0 +1,85 @@
|
||||
part of '../analytic_dtos.dart';
|
||||
|
||||
@freezed
|
||||
class SalesAnalyticDto with _$SalesAnalyticDto {
|
||||
const SalesAnalyticDto._();
|
||||
|
||||
const factory SalesAnalyticDto({
|
||||
@JsonKey(name: 'organization_id') String? organizationId,
|
||||
@JsonKey(name: 'outlet_id') String? outletId,
|
||||
@JsonKey(name: 'date_from') DateTime? dateFrom,
|
||||
@JsonKey(name: 'date_to') DateTime? dateTo,
|
||||
@JsonKey(name: 'group_by') String? groupBy,
|
||||
@JsonKey(name: 'summary') SalesAnalyticSummaryDto? summary,
|
||||
@JsonKey(name: 'data') List<SalesAnalyticDataDto>? data,
|
||||
}) = _SalesAnalyticDto;
|
||||
|
||||
factory SalesAnalyticDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$SalesAnalyticDtoFromJson(json);
|
||||
|
||||
SalesAnalytic toDomain() => SalesAnalytic(
|
||||
organizationId: organizationId ?? '',
|
||||
outletId: outletId ?? '',
|
||||
dateFrom: dateFrom ?? DateTime.fromMillisecondsSinceEpoch(0),
|
||||
dateTo: dateTo ?? DateTime.fromMillisecondsSinceEpoch(0),
|
||||
groupBy: groupBy ?? '',
|
||||
summary: summary?.toDomain() ?? SalesAnalyticSummary.empty(),
|
||||
data: data?.map((e) => e.toDomain()).toList() ?? [],
|
||||
);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SalesAnalyticSummaryDto with _$SalesAnalyticSummaryDto {
|
||||
const SalesAnalyticSummaryDto._();
|
||||
|
||||
const factory SalesAnalyticSummaryDto({
|
||||
@JsonKey(name: 'total_sales') num? totalSales,
|
||||
@JsonKey(name: 'total_orders') num? totalOrders,
|
||||
@JsonKey(name: 'total_items') num? totalItems,
|
||||
@JsonKey(name: 'average_order_value') num? averageOrderValue,
|
||||
@JsonKey(name: 'total_tax') num? totalTax,
|
||||
@JsonKey(name: 'total_discount') num? totalDiscount,
|
||||
@JsonKey(name: 'net_sales') num? netSales,
|
||||
}) = _SalesAnalyticSummaryDto;
|
||||
|
||||
factory SalesAnalyticSummaryDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$SalesAnalyticSummaryDtoFromJson(json);
|
||||
|
||||
SalesAnalyticSummary toDomain() => SalesAnalyticSummary(
|
||||
totalSales: totalSales?.toInt() ?? 0,
|
||||
totalOrders: totalOrders?.toInt() ?? 0,
|
||||
totalItems: totalItems?.toInt() ?? 0,
|
||||
averageOrderValue: averageOrderValue?.toDouble() ?? 0,
|
||||
totalTax: totalTax?.toInt() ?? 0,
|
||||
totalDiscount: totalDiscount?.toInt() ?? 0,
|
||||
netSales: netSales?.toInt() ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SalesAnalyticDataDto with _$SalesAnalyticDataDto {
|
||||
const SalesAnalyticDataDto._();
|
||||
|
||||
const factory SalesAnalyticDataDto({
|
||||
@JsonKey(name: 'date') DateTime? date,
|
||||
@JsonKey(name: 'sales') num? sales,
|
||||
@JsonKey(name: 'orders') num? orders,
|
||||
@JsonKey(name: 'items') num? items,
|
||||
@JsonKey(name: 'tax') num? tax,
|
||||
@JsonKey(name: 'discount') num? discount,
|
||||
@JsonKey(name: 'net_sales') num? netSales,
|
||||
}) = _SalesAnalyticDataDto;
|
||||
|
||||
factory SalesAnalyticDataDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$SalesAnalyticDataDtoFromJson(json);
|
||||
|
||||
SalesAnalyticData toDomain() => SalesAnalyticData(
|
||||
date: date ?? DateTime.fromMillisecondsSinceEpoch(0),
|
||||
sales: sales?.toInt() ?? 0,
|
||||
orders: orders?.toInt() ?? 0,
|
||||
items: items?.toInt() ?? 0,
|
||||
tax: tax?.toInt() ?? 0,
|
||||
discount: discount?.toInt() ?? 0,
|
||||
netSales: netSales?.toInt() ?? 0,
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
|
||||
import '../../../domain/analytic/analytic.dart';
|
||||
import '../../../domain/analytic/repositories/i_analytic_repository.dart';
|
||||
import '../datasource/remote_data_provider.dart';
|
||||
|
||||
@Injectable(as: IAnalyticRepository)
|
||||
class AnalyticRepository implements IAnalyticRepository {
|
||||
final AnalyticRemoteDataProvider _dataProvider;
|
||||
final String _logName = 'AnalyticRepository';
|
||||
|
||||
AnalyticRepository(this._dataProvider);
|
||||
|
||||
@override
|
||||
Future<Either<AnalyticFailure, SalesAnalytic>> getSales({
|
||||
required DateTime dateFrom,
|
||||
required DateTime dateTo,
|
||||
}) async {
|
||||
try {
|
||||
final result = await _dataProvider.fetchSales(
|
||||
dateFrom: dateFrom,
|
||||
dateTo: dateTo,
|
||||
);
|
||||
|
||||
if (result.hasError) {
|
||||
return left(result.error!);
|
||||
}
|
||||
|
||||
final auth = result.data!.toDomain();
|
||||
|
||||
return right(auth);
|
||||
} catch (e, s) {
|
||||
log('getSalesError', name: _logName, error: e, stackTrace: s);
|
||||
return left(const AnalyticFailure.unexpectedError());
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<AnalyticFailure, ProfitLossAnalytic>> getProfitLoss({
|
||||
required DateTime dateFrom,
|
||||
required DateTime dateTo,
|
||||
}) async {
|
||||
try {
|
||||
final result = await _dataProvider.fetchProfitLoss(
|
||||
dateFrom: dateFrom,
|
||||
dateTo: dateTo,
|
||||
);
|
||||
|
||||
if (result.hasError) {
|
||||
return left(result.error!);
|
||||
}
|
||||
|
||||
final auth = result.data!.toDomain();
|
||||
|
||||
return right(auth);
|
||||
} catch (e, s) {
|
||||
log('getProfitLossError', name: _logName, error: e, stackTrace: s);
|
||||
return left(const AnalyticFailure.unexpectedError());
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<AnalyticFailure, CategoryAnalytic>> getCategory({
|
||||
required DateTime dateFrom,
|
||||
required DateTime dateTo,
|
||||
}) async {
|
||||
try {
|
||||
final result = await _dataProvider.fetchCategory(
|
||||
dateFrom: dateFrom,
|
||||
dateTo: dateTo,
|
||||
);
|
||||
|
||||
if (result.hasError) {
|
||||
return left(result.error!);
|
||||
}
|
||||
|
||||
final auth = result.data!.toDomain();
|
||||
|
||||
return right(auth);
|
||||
} catch (e, s) {
|
||||
log('getCategoryError', name: _logName, error: e, stackTrace: s);
|
||||
return left(const AnalyticFailure.unexpectedError());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
@ -10,6 +11,7 @@ import '../auth_dtos.dart';
|
||||
@injectable
|
||||
class AuthLocalDataProvider {
|
||||
final SharedPreferences _sharedPreferences;
|
||||
final String _logName = 'AuthLocalDataProvider';
|
||||
|
||||
AuthLocalDataProvider(this._sharedPreferences);
|
||||
|
||||
@ -48,7 +50,11 @@ class AuthLocalDataProvider {
|
||||
}
|
||||
|
||||
Future<void> deleteAllAuth() async {
|
||||
await _sharedPreferences.remove(LocalStorageKey.token);
|
||||
await _sharedPreferences.remove(LocalStorageKey.user);
|
||||
try {
|
||||
await _sharedPreferences.remove(LocalStorageKey.token);
|
||||
await _sharedPreferences.remove(LocalStorageKey.user);
|
||||
} catch (e) {
|
||||
log('deleteAllAuthError', name: _logName, error: e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import 'package:data_channel/data_channel.dart';
|
||||
|
||||
import '../../../common/api/api_client.dart';
|
||||
import '../../../common/api/api_failure.dart';
|
||||
import '../../../common/function/app_function.dart';
|
||||
import '../../../common/url/api_path.dart';
|
||||
import '../../../domain/auth/auth.dart';
|
||||
import '../auth_dtos.dart';
|
||||
@ -45,7 +46,7 @@ class AuthRemoteDataProvider {
|
||||
|
||||
Future<DC<AuthFailure, Unit>> logout() async {
|
||||
try {
|
||||
await _apiClient.post(ApiPath.logout);
|
||||
await _apiClient.post(ApiPath.logout, headers: getAuthorizationHeader());
|
||||
return DC.data(unit);
|
||||
} on ApiFailure catch (e, s) {
|
||||
log('login', name: _logName, error: e, stackTrace: s);
|
||||
|
||||
8
lib/infrastructure/category/category_dtos.dart
Normal file
8
lib/infrastructure/category/category_dtos.dart
Normal file
@ -0,0 +1,8 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import '../../domain/category/category.dart';
|
||||
|
||||
part 'category_dtos.freezed.dart';
|
||||
part 'category_dtos.g.dart';
|
||||
|
||||
part 'dto/category_dto.dart';
|
||||
368
lib/infrastructure/category/category_dtos.freezed.dart
Normal file
368
lib/infrastructure/category/category_dtos.freezed.dart
Normal file
@ -0,0 +1,368 @@
|
||||
// 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_dtos.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',
|
||||
);
|
||||
|
||||
CategoryDto _$CategoryDtoFromJson(Map<String, dynamic> json) {
|
||||
return _CategoryDto.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$CategoryDto {
|
||||
@JsonKey(name: 'id')
|
||||
String? get id => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'organization_id')
|
||||
String? get organizationId => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'name')
|
||||
String? get name => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'description')
|
||||
String? get description => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'business_type')
|
||||
String? get businessType => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'metadata')
|
||||
Map<String, dynamic>? get metadata => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'created_at')
|
||||
DateTime? get createdAt => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'updated_at')
|
||||
DateTime? get updatedAt => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this CategoryDto to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of CategoryDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$CategoryDtoCopyWith<CategoryDto> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $CategoryDtoCopyWith<$Res> {
|
||||
factory $CategoryDtoCopyWith(
|
||||
CategoryDto value,
|
||||
$Res Function(CategoryDto) then,
|
||||
) = _$CategoryDtoCopyWithImpl<$Res, CategoryDto>;
|
||||
@useResult
|
||||
$Res call({
|
||||
@JsonKey(name: 'id') String? id,
|
||||
@JsonKey(name: 'organization_id') String? organizationId,
|
||||
@JsonKey(name: 'name') String? name,
|
||||
@JsonKey(name: 'description') String? description,
|
||||
@JsonKey(name: 'business_type') String? businessType,
|
||||
@JsonKey(name: 'metadata') Map<String, dynamic>? metadata,
|
||||
@JsonKey(name: 'created_at') DateTime? createdAt,
|
||||
@JsonKey(name: 'updated_at') DateTime? updatedAt,
|
||||
});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$CategoryDtoCopyWithImpl<$Res, $Val extends CategoryDto>
|
||||
implements $CategoryDtoCopyWith<$Res> {
|
||||
_$CategoryDtoCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of CategoryDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = freezed,
|
||||
Object? organizationId = freezed,
|
||||
Object? name = freezed,
|
||||
Object? description = freezed,
|
||||
Object? businessType = freezed,
|
||||
Object? metadata = freezed,
|
||||
Object? createdAt = freezed,
|
||||
Object? updatedAt = freezed,
|
||||
}) {
|
||||
return _then(
|
||||
_value.copyWith(
|
||||
id: freezed == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
organizationId: freezed == organizationId
|
||||
? _value.organizationId
|
||||
: organizationId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
name: freezed == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
description: freezed == description
|
||||
? _value.description
|
||||
: description // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
businessType: freezed == businessType
|
||||
? _value.businessType
|
||||
: businessType // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
metadata: freezed == metadata
|
||||
? _value.metadata
|
||||
: metadata // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>?,
|
||||
createdAt: freezed == createdAt
|
||||
? _value.createdAt
|
||||
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
updatedAt: freezed == updatedAt
|
||||
? _value.updatedAt
|
||||
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
)
|
||||
as $Val,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$CategoryDtoImplCopyWith<$Res>
|
||||
implements $CategoryDtoCopyWith<$Res> {
|
||||
factory _$$CategoryDtoImplCopyWith(
|
||||
_$CategoryDtoImpl value,
|
||||
$Res Function(_$CategoryDtoImpl) then,
|
||||
) = __$$CategoryDtoImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({
|
||||
@JsonKey(name: 'id') String? id,
|
||||
@JsonKey(name: 'organization_id') String? organizationId,
|
||||
@JsonKey(name: 'name') String? name,
|
||||
@JsonKey(name: 'description') String? description,
|
||||
@JsonKey(name: 'business_type') String? businessType,
|
||||
@JsonKey(name: 'metadata') Map<String, dynamic>? metadata,
|
||||
@JsonKey(name: 'created_at') DateTime? createdAt,
|
||||
@JsonKey(name: 'updated_at') DateTime? updatedAt,
|
||||
});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$CategoryDtoImplCopyWithImpl<$Res>
|
||||
extends _$CategoryDtoCopyWithImpl<$Res, _$CategoryDtoImpl>
|
||||
implements _$$CategoryDtoImplCopyWith<$Res> {
|
||||
__$$CategoryDtoImplCopyWithImpl(
|
||||
_$CategoryDtoImpl _value,
|
||||
$Res Function(_$CategoryDtoImpl) _then,
|
||||
) : super(_value, _then);
|
||||
|
||||
/// Create a copy of CategoryDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = freezed,
|
||||
Object? organizationId = freezed,
|
||||
Object? name = freezed,
|
||||
Object? description = freezed,
|
||||
Object? businessType = freezed,
|
||||
Object? metadata = freezed,
|
||||
Object? createdAt = freezed,
|
||||
Object? updatedAt = freezed,
|
||||
}) {
|
||||
return _then(
|
||||
_$CategoryDtoImpl(
|
||||
id: freezed == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
organizationId: freezed == organizationId
|
||||
? _value.organizationId
|
||||
: organizationId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
name: freezed == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
description: freezed == description
|
||||
? _value.description
|
||||
: description // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
businessType: freezed == businessType
|
||||
? _value.businessType
|
||||
: businessType // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
metadata: freezed == metadata
|
||||
? _value._metadata
|
||||
: metadata // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>?,
|
||||
createdAt: freezed == createdAt
|
||||
? _value.createdAt
|
||||
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
updatedAt: freezed == updatedAt
|
||||
? _value.updatedAt
|
||||
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$CategoryDtoImpl extends _CategoryDto {
|
||||
const _$CategoryDtoImpl({
|
||||
@JsonKey(name: 'id') this.id,
|
||||
@JsonKey(name: 'organization_id') this.organizationId,
|
||||
@JsonKey(name: 'name') this.name,
|
||||
@JsonKey(name: 'description') this.description,
|
||||
@JsonKey(name: 'business_type') this.businessType,
|
||||
@JsonKey(name: 'metadata') final Map<String, dynamic>? metadata,
|
||||
@JsonKey(name: 'created_at') this.createdAt,
|
||||
@JsonKey(name: 'updated_at') this.updatedAt,
|
||||
}) : _metadata = metadata,
|
||||
super._();
|
||||
|
||||
factory _$CategoryDtoImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$CategoryDtoImplFromJson(json);
|
||||
|
||||
@override
|
||||
@JsonKey(name: 'id')
|
||||
final String? id;
|
||||
@override
|
||||
@JsonKey(name: 'organization_id')
|
||||
final String? organizationId;
|
||||
@override
|
||||
@JsonKey(name: 'name')
|
||||
final String? name;
|
||||
@override
|
||||
@JsonKey(name: 'description')
|
||||
final String? description;
|
||||
@override
|
||||
@JsonKey(name: 'business_type')
|
||||
final String? businessType;
|
||||
final Map<String, dynamic>? _metadata;
|
||||
@override
|
||||
@JsonKey(name: 'metadata')
|
||||
Map<String, dynamic>? get metadata {
|
||||
final value = _metadata;
|
||||
if (value == null) return null;
|
||||
if (_metadata is EqualUnmodifiableMapView) return _metadata;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableMapView(value);
|
||||
}
|
||||
|
||||
@override
|
||||
@JsonKey(name: 'created_at')
|
||||
final DateTime? createdAt;
|
||||
@override
|
||||
@JsonKey(name: 'updated_at')
|
||||
final DateTime? updatedAt;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CategoryDto(id: $id, organizationId: $organizationId, name: $name, description: $description, businessType: $businessType, metadata: $metadata, createdAt: $createdAt, updatedAt: $updatedAt)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$CategoryDtoImpl &&
|
||||
(identical(other.id, id) || other.id == id) &&
|
||||
(identical(other.organizationId, organizationId) ||
|
||||
other.organizationId == organizationId) &&
|
||||
(identical(other.name, name) || other.name == name) &&
|
||||
(identical(other.description, description) ||
|
||||
other.description == description) &&
|
||||
(identical(other.businessType, businessType) ||
|
||||
other.businessType == businessType) &&
|
||||
const DeepCollectionEquality().equals(other._metadata, _metadata) &&
|
||||
(identical(other.createdAt, createdAt) ||
|
||||
other.createdAt == createdAt) &&
|
||||
(identical(other.updatedAt, updatedAt) ||
|
||||
other.updatedAt == updatedAt));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
id,
|
||||
organizationId,
|
||||
name,
|
||||
description,
|
||||
businessType,
|
||||
const DeepCollectionEquality().hash(_metadata),
|
||||
createdAt,
|
||||
updatedAt,
|
||||
);
|
||||
|
||||
/// Create a copy of CategoryDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$CategoryDtoImplCopyWith<_$CategoryDtoImpl> get copyWith =>
|
||||
__$$CategoryDtoImplCopyWithImpl<_$CategoryDtoImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$CategoryDtoImplToJson(this);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _CategoryDto extends CategoryDto {
|
||||
const factory _CategoryDto({
|
||||
@JsonKey(name: 'id') final String? id,
|
||||
@JsonKey(name: 'organization_id') final String? organizationId,
|
||||
@JsonKey(name: 'name') final String? name,
|
||||
@JsonKey(name: 'description') final String? description,
|
||||
@JsonKey(name: 'business_type') final String? businessType,
|
||||
@JsonKey(name: 'metadata') final Map<String, dynamic>? metadata,
|
||||
@JsonKey(name: 'created_at') final DateTime? createdAt,
|
||||
@JsonKey(name: 'updated_at') final DateTime? updatedAt,
|
||||
}) = _$CategoryDtoImpl;
|
||||
const _CategoryDto._() : super._();
|
||||
|
||||
factory _CategoryDto.fromJson(Map<String, dynamic> json) =
|
||||
_$CategoryDtoImpl.fromJson;
|
||||
|
||||
@override
|
||||
@JsonKey(name: 'id')
|
||||
String? get id;
|
||||
@override
|
||||
@JsonKey(name: 'organization_id')
|
||||
String? get organizationId;
|
||||
@override
|
||||
@JsonKey(name: 'name')
|
||||
String? get name;
|
||||
@override
|
||||
@JsonKey(name: 'description')
|
||||
String? get description;
|
||||
@override
|
||||
@JsonKey(name: 'business_type')
|
||||
String? get businessType;
|
||||
@override
|
||||
@JsonKey(name: 'metadata')
|
||||
Map<String, dynamic>? get metadata;
|
||||
@override
|
||||
@JsonKey(name: 'created_at')
|
||||
DateTime? get createdAt;
|
||||
@override
|
||||
@JsonKey(name: 'updated_at')
|
||||
DateTime? get updatedAt;
|
||||
|
||||
/// Create a copy of CategoryDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$CategoryDtoImplCopyWith<_$CategoryDtoImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
35
lib/infrastructure/category/category_dtos.g.dart
Normal file
35
lib/infrastructure/category/category_dtos.g.dart
Normal file
@ -0,0 +1,35 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'category_dtos.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$CategoryDtoImpl _$$CategoryDtoImplFromJson(Map<String, dynamic> json) =>
|
||||
_$CategoryDtoImpl(
|
||||
id: json['id'] as String?,
|
||||
organizationId: json['organization_id'] as String?,
|
||||
name: json['name'] as String?,
|
||||
description: json['description'] as String?,
|
||||
businessType: json['business_type'] as String?,
|
||||
metadata: json['metadata'] as Map<String, dynamic>?,
|
||||
createdAt: json['created_at'] == null
|
||||
? null
|
||||
: DateTime.parse(json['created_at'] as String),
|
||||
updatedAt: json['updated_at'] == null
|
||||
? null
|
||||
: DateTime.parse(json['updated_at'] as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$CategoryDtoImplToJson(_$CategoryDtoImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'organization_id': instance.organizationId,
|
||||
'name': instance.name,
|
||||
'description': instance.description,
|
||||
'business_type': instance.businessType,
|
||||
'metadata': instance.metadata,
|
||||
'created_at': instance.createdAt?.toIso8601String(),
|
||||
'updated_at': instance.updatedAt?.toIso8601String(),
|
||||
};
|
||||
@ -0,0 +1,46 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:data_channel/data_channel.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
|
||||
import '../../../common/api/api_client.dart';
|
||||
import '../../../common/api/api_failure.dart';
|
||||
import '../../../common/function/app_function.dart';
|
||||
import '../../../common/url/api_path.dart';
|
||||
import '../../../domain/category/category.dart';
|
||||
import '../category_dtos.dart';
|
||||
|
||||
@injectable
|
||||
class CategoryRemoteDataProvider {
|
||||
final ApiClient _apiClient;
|
||||
final String _logName = 'CategoryRemoteDataProvider';
|
||||
|
||||
CategoryRemoteDataProvider(this._apiClient);
|
||||
|
||||
Future<DC<CategoryFailure, List<CategoryDto>>> fetch({
|
||||
int page = 1,
|
||||
int limit = 20,
|
||||
bool isActive = true,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _apiClient.get(
|
||||
ApiPath.category,
|
||||
params: {'page': page, 'limit': limit, 'is_active': isActive},
|
||||
headers: getAuthorizationHeader(),
|
||||
);
|
||||
|
||||
if (response.data['data'] == null) {
|
||||
return DC.error(CategoryFailure.empty());
|
||||
}
|
||||
|
||||
final dto = (response.data['data']['categories'] as List)
|
||||
.map((item) => CategoryDto.fromJson(item))
|
||||
.toList();
|
||||
|
||||
return DC.data(dto);
|
||||
} on ApiFailure catch (e, s) {
|
||||
log('fetchCategoryError', name: _logName, error: e, stackTrace: s);
|
||||
return DC.error(CategoryFailure.serverError(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
31
lib/infrastructure/category/dto/category_dto.dart
Normal file
31
lib/infrastructure/category/dto/category_dto.dart
Normal file
@ -0,0 +1,31 @@
|
||||
part of '../category_dtos.dart';
|
||||
|
||||
@freezed
|
||||
class CategoryDto with _$CategoryDto {
|
||||
const CategoryDto._();
|
||||
|
||||
const factory CategoryDto({
|
||||
@JsonKey(name: 'id') String? id,
|
||||
@JsonKey(name: 'organization_id') String? organizationId,
|
||||
@JsonKey(name: 'name') String? name,
|
||||
@JsonKey(name: 'description') String? description,
|
||||
@JsonKey(name: 'business_type') String? businessType,
|
||||
@JsonKey(name: 'metadata') Map<String, dynamic>? metadata,
|
||||
@JsonKey(name: 'created_at') DateTime? createdAt,
|
||||
@JsonKey(name: 'updated_at') DateTime? updatedAt,
|
||||
}) = _CategoryDto;
|
||||
|
||||
factory CategoryDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$CategoryDtoFromJson(json);
|
||||
|
||||
Category toDomain() => Category(
|
||||
id: id ?? '',
|
||||
organizationId: organizationId ?? '',
|
||||
name: name ?? '',
|
||||
description: description ?? '',
|
||||
businessType: businessType ?? '',
|
||||
metadata: metadata ?? {},
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
|
||||
import '../../../domain/category/category.dart';
|
||||
import '../datasource/remote_data_provider.dart';
|
||||
|
||||
@Injectable(as: ICategoryRepository)
|
||||
class CategoryRepository implements ICategoryRepository {
|
||||
final CategoryRemoteDataProvider _dataProvider;
|
||||
final String _logName = 'CategoryRepository';
|
||||
|
||||
CategoryRepository(this._dataProvider);
|
||||
|
||||
@override
|
||||
Future<Either<CategoryFailure, List<Category>>> get({
|
||||
int page = 1,
|
||||
int limit = 20,
|
||||
bool isActive = true,
|
||||
}) async {
|
||||
try {
|
||||
final result = await _dataProvider.fetch(
|
||||
page: page,
|
||||
limit: limit,
|
||||
isActive: isActive,
|
||||
);
|
||||
|
||||
if (result.hasError) {
|
||||
return left(result.error!);
|
||||
}
|
||||
|
||||
final auth = result.data!.map((e) => e.toDomain()).toList();
|
||||
|
||||
return right(auth);
|
||||
} catch (e, s) {
|
||||
log('getCategoryError', name: _logName, error: e, stackTrace: s);
|
||||
return left(const CategoryFailure.unexpectedError());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:data_channel/data_channel.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
|
||||
import '../../../common/api/api_client.dart';
|
||||
import '../../../common/api/api_failure.dart';
|
||||
import '../../../common/function/app_function.dart';
|
||||
import '../../../common/url/api_path.dart';
|
||||
import '../../../domain/product/product.dart';
|
||||
import '../product_dtos.dart';
|
||||
|
||||
@injectable
|
||||
class ProductRemoteDataProvider {
|
||||
final ApiClient _apiClient;
|
||||
final String _logName = 'ProductRemoteDataProvider';
|
||||
|
||||
ProductRemoteDataProvider(this._apiClient);
|
||||
|
||||
Future<DC<ProductFailure, List<ProductDto>>> fetch({
|
||||
int page = 1,
|
||||
int limit = 10,
|
||||
String? categoryId,
|
||||
String? search,
|
||||
}) async {
|
||||
try {
|
||||
Map<String, dynamic> params = {'page': page, 'limit': limit};
|
||||
|
||||
if (categoryId != null) {
|
||||
params['category_id'] = categoryId;
|
||||
}
|
||||
|
||||
if (search != null) {
|
||||
params['search'] = search;
|
||||
}
|
||||
|
||||
final response = await _apiClient.get(
|
||||
ApiPath.product,
|
||||
params: params,
|
||||
headers: getAuthorizationHeader(),
|
||||
);
|
||||
|
||||
if (response.data['data'] == null) {
|
||||
return DC.error(ProductFailure.empty());
|
||||
}
|
||||
|
||||
final dto = (response.data['data']['products'] as List)
|
||||
.map((item) => ProductDto.fromJson(item))
|
||||
.toList();
|
||||
|
||||
return DC.data(dto);
|
||||
} on ApiFailure catch (e, s) {
|
||||
log('fetchProductError', name: _logName, error: e, stackTrace: s);
|
||||
return DC.error(ProductFailure.serverError(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
70
lib/infrastructure/product/dto/product_dto.dart
Normal file
70
lib/infrastructure/product/dto/product_dto.dart
Normal file
@ -0,0 +1,70 @@
|
||||
part of '../product_dtos.dart';
|
||||
|
||||
@freezed
|
||||
class ProductDto with _$ProductDto {
|
||||
const ProductDto._();
|
||||
|
||||
const factory ProductDto({
|
||||
@JsonKey(name: 'id') String? id,
|
||||
@JsonKey(name: 'organization_id') String? organizationId,
|
||||
@JsonKey(name: 'category_id') String? categoryId,
|
||||
@JsonKey(name: 'sku') String? sku,
|
||||
@JsonKey(name: 'name') String? name,
|
||||
@JsonKey(name: 'description') String? description,
|
||||
@JsonKey(name: 'price') int? price,
|
||||
@JsonKey(name: 'cost') int? cost,
|
||||
@JsonKey(name: 'business_type') String? businessType,
|
||||
@JsonKey(name: 'image_url') String? imageUrl,
|
||||
@JsonKey(name: 'printer_type') String? printerType,
|
||||
@JsonKey(name: 'metadata') Map<String, dynamic>? metadata,
|
||||
@JsonKey(name: 'is_active') bool? isActive,
|
||||
@JsonKey(name: 'created_at') DateTime? createdAt,
|
||||
@JsonKey(name: 'updated_at') DateTime? updatedAt,
|
||||
@JsonKey(name: 'variants') List<ProductVariantDto>? variants,
|
||||
}) = _ProductDto;
|
||||
|
||||
factory ProductDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$ProductDtoFromJson(json);
|
||||
|
||||
/// DTO -> Domain (isi default kalau null)
|
||||
Product toDomain() => Product(
|
||||
id: id ?? '',
|
||||
organizationId: organizationId ?? '',
|
||||
categoryId: categoryId ?? '',
|
||||
sku: sku ?? '',
|
||||
name: name ?? '',
|
||||
description: description ?? '',
|
||||
price: price ?? 0,
|
||||
cost: cost ?? 0,
|
||||
businessType: businessType ?? '',
|
||||
imageUrl: imageUrl ?? '',
|
||||
printerType: printerType ?? '',
|
||||
metadata: metadata ?? {},
|
||||
isActive: isActive ?? false,
|
||||
createdAt: createdAt ?? DateTime.now(),
|
||||
updatedAt: updatedAt ?? DateTime.now(),
|
||||
variants: variants?.map((v) => v.toDomain()).toList() ?? [],
|
||||
);
|
||||
|
||||
/// Domain -> DTO
|
||||
factory ProductDto.fromDomain(Product product) => ProductDto(
|
||||
id: product.id,
|
||||
organizationId: product.organizationId,
|
||||
categoryId: product.categoryId,
|
||||
sku: product.sku,
|
||||
name: product.name,
|
||||
description: product.description,
|
||||
price: product.price,
|
||||
cost: product.cost,
|
||||
businessType: product.businessType,
|
||||
imageUrl: product.imageUrl,
|
||||
printerType: product.printerType,
|
||||
metadata: product.metadata,
|
||||
isActive: product.isActive,
|
||||
createdAt: product.createdAt,
|
||||
updatedAt: product.updatedAt,
|
||||
variants: product.variants
|
||||
.map((v) => ProductVariantDto.fromDomain(v))
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
45
lib/infrastructure/product/dto/product_variant_dto.dart
Normal file
45
lib/infrastructure/product/dto/product_variant_dto.dart
Normal file
@ -0,0 +1,45 @@
|
||||
part of '../product_dtos.dart';
|
||||
|
||||
@freezed
|
||||
class ProductVariantDto with _$ProductVariantDto {
|
||||
const ProductVariantDto._();
|
||||
|
||||
const factory ProductVariantDto({
|
||||
@JsonKey(name: 'id') String? id,
|
||||
@JsonKey(name: 'product_id') String? productId,
|
||||
@JsonKey(name: 'name') String? name,
|
||||
@JsonKey(name: 'price_modifier') int? priceModifier,
|
||||
@JsonKey(name: 'cost') int? cost,
|
||||
@JsonKey(name: 'metadata') Map<String, dynamic>? metadata,
|
||||
@JsonKey(name: 'created_at') DateTime? createdAt,
|
||||
@JsonKey(name: 'updated_at') DateTime? updatedAt,
|
||||
}) = _ProductVariantDto;
|
||||
|
||||
factory ProductVariantDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$ProductVariantDtoFromJson(json);
|
||||
|
||||
/// DTO -> Domain
|
||||
ProductVariant toDomain() => ProductVariant(
|
||||
id: id ?? '',
|
||||
productId: productId ?? '',
|
||||
name: name ?? '',
|
||||
priceModifier: priceModifier ?? 0,
|
||||
cost: cost ?? 0,
|
||||
metadata: metadata ?? {},
|
||||
createdAt: createdAt ?? DateTime.now(),
|
||||
updatedAt: updatedAt ?? DateTime.now(),
|
||||
);
|
||||
|
||||
/// Domain -> DTO
|
||||
factory ProductVariantDto.fromDomain(ProductVariant variant) =>
|
||||
ProductVariantDto(
|
||||
id: variant.id,
|
||||
productId: variant.productId,
|
||||
name: variant.name,
|
||||
priceModifier: variant.priceModifier,
|
||||
cost: variant.cost,
|
||||
metadata: variant.metadata,
|
||||
createdAt: variant.createdAt,
|
||||
updatedAt: variant.updatedAt,
|
||||
);
|
||||
}
|
||||
9
lib/infrastructure/product/product_dtos.dart
Normal file
9
lib/infrastructure/product/product_dtos.dart
Normal file
@ -0,0 +1,9 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import '../../domain/product/product.dart';
|
||||
|
||||
part 'product_dtos.freezed.dart';
|
||||
part 'product_dtos.g.dart';
|
||||
|
||||
part 'dto/product_dto.dart';
|
||||
part 'dto/product_variant_dto.dart';
|
||||
926
lib/infrastructure/product/product_dtos.freezed.dart
Normal file
926
lib/infrastructure/product/product_dtos.freezed.dart
Normal file
@ -0,0 +1,926 @@
|
||||
// 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 'product_dtos.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',
|
||||
);
|
||||
|
||||
ProductDto _$ProductDtoFromJson(Map<String, dynamic> json) {
|
||||
return _ProductDto.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ProductDto {
|
||||
@JsonKey(name: 'id')
|
||||
String? get id => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'organization_id')
|
||||
String? get organizationId => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'category_id')
|
||||
String? get categoryId => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'sku')
|
||||
String? get sku => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'name')
|
||||
String? get name => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'description')
|
||||
String? get description => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'price')
|
||||
int? get price => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'cost')
|
||||
int? get cost => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'business_type')
|
||||
String? get businessType => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'image_url')
|
||||
String? get imageUrl => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'printer_type')
|
||||
String? get printerType => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'metadata')
|
||||
Map<String, dynamic>? get metadata => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'is_active')
|
||||
bool? get isActive => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'created_at')
|
||||
DateTime? get createdAt => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'updated_at')
|
||||
DateTime? get updatedAt => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'variants')
|
||||
List<ProductVariantDto>? get variants => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this ProductDto to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of ProductDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$ProductDtoCopyWith<ProductDto> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $ProductDtoCopyWith<$Res> {
|
||||
factory $ProductDtoCopyWith(
|
||||
ProductDto value,
|
||||
$Res Function(ProductDto) then,
|
||||
) = _$ProductDtoCopyWithImpl<$Res, ProductDto>;
|
||||
@useResult
|
||||
$Res call({
|
||||
@JsonKey(name: 'id') String? id,
|
||||
@JsonKey(name: 'organization_id') String? organizationId,
|
||||
@JsonKey(name: 'category_id') String? categoryId,
|
||||
@JsonKey(name: 'sku') String? sku,
|
||||
@JsonKey(name: 'name') String? name,
|
||||
@JsonKey(name: 'description') String? description,
|
||||
@JsonKey(name: 'price') int? price,
|
||||
@JsonKey(name: 'cost') int? cost,
|
||||
@JsonKey(name: 'business_type') String? businessType,
|
||||
@JsonKey(name: 'image_url') String? imageUrl,
|
||||
@JsonKey(name: 'printer_type') String? printerType,
|
||||
@JsonKey(name: 'metadata') Map<String, dynamic>? metadata,
|
||||
@JsonKey(name: 'is_active') bool? isActive,
|
||||
@JsonKey(name: 'created_at') DateTime? createdAt,
|
||||
@JsonKey(name: 'updated_at') DateTime? updatedAt,
|
||||
@JsonKey(name: 'variants') List<ProductVariantDto>? variants,
|
||||
});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$ProductDtoCopyWithImpl<$Res, $Val extends ProductDto>
|
||||
implements $ProductDtoCopyWith<$Res> {
|
||||
_$ProductDtoCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of ProductDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = freezed,
|
||||
Object? organizationId = freezed,
|
||||
Object? categoryId = freezed,
|
||||
Object? sku = freezed,
|
||||
Object? name = freezed,
|
||||
Object? description = freezed,
|
||||
Object? price = freezed,
|
||||
Object? cost = freezed,
|
||||
Object? businessType = freezed,
|
||||
Object? imageUrl = freezed,
|
||||
Object? printerType = freezed,
|
||||
Object? metadata = freezed,
|
||||
Object? isActive = freezed,
|
||||
Object? createdAt = freezed,
|
||||
Object? updatedAt = freezed,
|
||||
Object? variants = freezed,
|
||||
}) {
|
||||
return _then(
|
||||
_value.copyWith(
|
||||
id: freezed == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
organizationId: freezed == organizationId
|
||||
? _value.organizationId
|
||||
: organizationId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
categoryId: freezed == categoryId
|
||||
? _value.categoryId
|
||||
: categoryId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
sku: freezed == sku
|
||||
? _value.sku
|
||||
: sku // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
name: freezed == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
description: freezed == description
|
||||
? _value.description
|
||||
: description // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
price: freezed == price
|
||||
? _value.price
|
||||
: price // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
cost: freezed == cost
|
||||
? _value.cost
|
||||
: cost // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
businessType: freezed == businessType
|
||||
? _value.businessType
|
||||
: businessType // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
imageUrl: freezed == imageUrl
|
||||
? _value.imageUrl
|
||||
: imageUrl // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
printerType: freezed == printerType
|
||||
? _value.printerType
|
||||
: printerType // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
metadata: freezed == metadata
|
||||
? _value.metadata
|
||||
: metadata // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>?,
|
||||
isActive: freezed == isActive
|
||||
? _value.isActive
|
||||
: isActive // ignore: cast_nullable_to_non_nullable
|
||||
as bool?,
|
||||
createdAt: freezed == createdAt
|
||||
? _value.createdAt
|
||||
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
updatedAt: freezed == updatedAt
|
||||
? _value.updatedAt
|
||||
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
variants: freezed == variants
|
||||
? _value.variants
|
||||
: variants // ignore: cast_nullable_to_non_nullable
|
||||
as List<ProductVariantDto>?,
|
||||
)
|
||||
as $Val,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$ProductDtoImplCopyWith<$Res>
|
||||
implements $ProductDtoCopyWith<$Res> {
|
||||
factory _$$ProductDtoImplCopyWith(
|
||||
_$ProductDtoImpl value,
|
||||
$Res Function(_$ProductDtoImpl) then,
|
||||
) = __$$ProductDtoImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({
|
||||
@JsonKey(name: 'id') String? id,
|
||||
@JsonKey(name: 'organization_id') String? organizationId,
|
||||
@JsonKey(name: 'category_id') String? categoryId,
|
||||
@JsonKey(name: 'sku') String? sku,
|
||||
@JsonKey(name: 'name') String? name,
|
||||
@JsonKey(name: 'description') String? description,
|
||||
@JsonKey(name: 'price') int? price,
|
||||
@JsonKey(name: 'cost') int? cost,
|
||||
@JsonKey(name: 'business_type') String? businessType,
|
||||
@JsonKey(name: 'image_url') String? imageUrl,
|
||||
@JsonKey(name: 'printer_type') String? printerType,
|
||||
@JsonKey(name: 'metadata') Map<String, dynamic>? metadata,
|
||||
@JsonKey(name: 'is_active') bool? isActive,
|
||||
@JsonKey(name: 'created_at') DateTime? createdAt,
|
||||
@JsonKey(name: 'updated_at') DateTime? updatedAt,
|
||||
@JsonKey(name: 'variants') List<ProductVariantDto>? variants,
|
||||
});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$ProductDtoImplCopyWithImpl<$Res>
|
||||
extends _$ProductDtoCopyWithImpl<$Res, _$ProductDtoImpl>
|
||||
implements _$$ProductDtoImplCopyWith<$Res> {
|
||||
__$$ProductDtoImplCopyWithImpl(
|
||||
_$ProductDtoImpl _value,
|
||||
$Res Function(_$ProductDtoImpl) _then,
|
||||
) : super(_value, _then);
|
||||
|
||||
/// Create a copy of ProductDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = freezed,
|
||||
Object? organizationId = freezed,
|
||||
Object? categoryId = freezed,
|
||||
Object? sku = freezed,
|
||||
Object? name = freezed,
|
||||
Object? description = freezed,
|
||||
Object? price = freezed,
|
||||
Object? cost = freezed,
|
||||
Object? businessType = freezed,
|
||||
Object? imageUrl = freezed,
|
||||
Object? printerType = freezed,
|
||||
Object? metadata = freezed,
|
||||
Object? isActive = freezed,
|
||||
Object? createdAt = freezed,
|
||||
Object? updatedAt = freezed,
|
||||
Object? variants = freezed,
|
||||
}) {
|
||||
return _then(
|
||||
_$ProductDtoImpl(
|
||||
id: freezed == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
organizationId: freezed == organizationId
|
||||
? _value.organizationId
|
||||
: organizationId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
categoryId: freezed == categoryId
|
||||
? _value.categoryId
|
||||
: categoryId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
sku: freezed == sku
|
||||
? _value.sku
|
||||
: sku // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
name: freezed == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
description: freezed == description
|
||||
? _value.description
|
||||
: description // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
price: freezed == price
|
||||
? _value.price
|
||||
: price // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
cost: freezed == cost
|
||||
? _value.cost
|
||||
: cost // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
businessType: freezed == businessType
|
||||
? _value.businessType
|
||||
: businessType // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
imageUrl: freezed == imageUrl
|
||||
? _value.imageUrl
|
||||
: imageUrl // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
printerType: freezed == printerType
|
||||
? _value.printerType
|
||||
: printerType // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
metadata: freezed == metadata
|
||||
? _value._metadata
|
||||
: metadata // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>?,
|
||||
isActive: freezed == isActive
|
||||
? _value.isActive
|
||||
: isActive // ignore: cast_nullable_to_non_nullable
|
||||
as bool?,
|
||||
createdAt: freezed == createdAt
|
||||
? _value.createdAt
|
||||
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
updatedAt: freezed == updatedAt
|
||||
? _value.updatedAt
|
||||
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
variants: freezed == variants
|
||||
? _value._variants
|
||||
: variants // ignore: cast_nullable_to_non_nullable
|
||||
as List<ProductVariantDto>?,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$ProductDtoImpl extends _ProductDto {
|
||||
const _$ProductDtoImpl({
|
||||
@JsonKey(name: 'id') this.id,
|
||||
@JsonKey(name: 'organization_id') this.organizationId,
|
||||
@JsonKey(name: 'category_id') this.categoryId,
|
||||
@JsonKey(name: 'sku') this.sku,
|
||||
@JsonKey(name: 'name') this.name,
|
||||
@JsonKey(name: 'description') this.description,
|
||||
@JsonKey(name: 'price') this.price,
|
||||
@JsonKey(name: 'cost') this.cost,
|
||||
@JsonKey(name: 'business_type') this.businessType,
|
||||
@JsonKey(name: 'image_url') this.imageUrl,
|
||||
@JsonKey(name: 'printer_type') this.printerType,
|
||||
@JsonKey(name: 'metadata') final Map<String, dynamic>? metadata,
|
||||
@JsonKey(name: 'is_active') this.isActive,
|
||||
@JsonKey(name: 'created_at') this.createdAt,
|
||||
@JsonKey(name: 'updated_at') this.updatedAt,
|
||||
@JsonKey(name: 'variants') final List<ProductVariantDto>? variants,
|
||||
}) : _metadata = metadata,
|
||||
_variants = variants,
|
||||
super._();
|
||||
|
||||
factory _$ProductDtoImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$ProductDtoImplFromJson(json);
|
||||
|
||||
@override
|
||||
@JsonKey(name: 'id')
|
||||
final String? id;
|
||||
@override
|
||||
@JsonKey(name: 'organization_id')
|
||||
final String? organizationId;
|
||||
@override
|
||||
@JsonKey(name: 'category_id')
|
||||
final String? categoryId;
|
||||
@override
|
||||
@JsonKey(name: 'sku')
|
||||
final String? sku;
|
||||
@override
|
||||
@JsonKey(name: 'name')
|
||||
final String? name;
|
||||
@override
|
||||
@JsonKey(name: 'description')
|
||||
final String? description;
|
||||
@override
|
||||
@JsonKey(name: 'price')
|
||||
final int? price;
|
||||
@override
|
||||
@JsonKey(name: 'cost')
|
||||
final int? cost;
|
||||
@override
|
||||
@JsonKey(name: 'business_type')
|
||||
final String? businessType;
|
||||
@override
|
||||
@JsonKey(name: 'image_url')
|
||||
final String? imageUrl;
|
||||
@override
|
||||
@JsonKey(name: 'printer_type')
|
||||
final String? printerType;
|
||||
final Map<String, dynamic>? _metadata;
|
||||
@override
|
||||
@JsonKey(name: 'metadata')
|
||||
Map<String, dynamic>? get metadata {
|
||||
final value = _metadata;
|
||||
if (value == null) return null;
|
||||
if (_metadata is EqualUnmodifiableMapView) return _metadata;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableMapView(value);
|
||||
}
|
||||
|
||||
@override
|
||||
@JsonKey(name: 'is_active')
|
||||
final bool? isActive;
|
||||
@override
|
||||
@JsonKey(name: 'created_at')
|
||||
final DateTime? createdAt;
|
||||
@override
|
||||
@JsonKey(name: 'updated_at')
|
||||
final DateTime? updatedAt;
|
||||
final List<ProductVariantDto>? _variants;
|
||||
@override
|
||||
@JsonKey(name: 'variants')
|
||||
List<ProductVariantDto>? get variants {
|
||||
final value = _variants;
|
||||
if (value == null) return null;
|
||||
if (_variants is EqualUnmodifiableListView) return _variants;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(value);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ProductDto(id: $id, organizationId: $organizationId, categoryId: $categoryId, sku: $sku, name: $name, description: $description, price: $price, cost: $cost, businessType: $businessType, imageUrl: $imageUrl, printerType: $printerType, metadata: $metadata, isActive: $isActive, createdAt: $createdAt, updatedAt: $updatedAt, variants: $variants)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$ProductDtoImpl &&
|
||||
(identical(other.id, id) || other.id == id) &&
|
||||
(identical(other.organizationId, organizationId) ||
|
||||
other.organizationId == organizationId) &&
|
||||
(identical(other.categoryId, categoryId) ||
|
||||
other.categoryId == categoryId) &&
|
||||
(identical(other.sku, sku) || other.sku == sku) &&
|
||||
(identical(other.name, name) || other.name == name) &&
|
||||
(identical(other.description, description) ||
|
||||
other.description == description) &&
|
||||
(identical(other.price, price) || other.price == price) &&
|
||||
(identical(other.cost, cost) || other.cost == cost) &&
|
||||
(identical(other.businessType, businessType) ||
|
||||
other.businessType == businessType) &&
|
||||
(identical(other.imageUrl, imageUrl) ||
|
||||
other.imageUrl == imageUrl) &&
|
||||
(identical(other.printerType, printerType) ||
|
||||
other.printerType == printerType) &&
|
||||
const DeepCollectionEquality().equals(other._metadata, _metadata) &&
|
||||
(identical(other.isActive, isActive) ||
|
||||
other.isActive == isActive) &&
|
||||
(identical(other.createdAt, createdAt) ||
|
||||
other.createdAt == createdAt) &&
|
||||
(identical(other.updatedAt, updatedAt) ||
|
||||
other.updatedAt == updatedAt) &&
|
||||
const DeepCollectionEquality().equals(other._variants, _variants));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
id,
|
||||
organizationId,
|
||||
categoryId,
|
||||
sku,
|
||||
name,
|
||||
description,
|
||||
price,
|
||||
cost,
|
||||
businessType,
|
||||
imageUrl,
|
||||
printerType,
|
||||
const DeepCollectionEquality().hash(_metadata),
|
||||
isActive,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
const DeepCollectionEquality().hash(_variants),
|
||||
);
|
||||
|
||||
/// Create a copy of ProductDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ProductDtoImplCopyWith<_$ProductDtoImpl> get copyWith =>
|
||||
__$$ProductDtoImplCopyWithImpl<_$ProductDtoImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$ProductDtoImplToJson(this);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _ProductDto extends ProductDto {
|
||||
const factory _ProductDto({
|
||||
@JsonKey(name: 'id') final String? id,
|
||||
@JsonKey(name: 'organization_id') final String? organizationId,
|
||||
@JsonKey(name: 'category_id') final String? categoryId,
|
||||
@JsonKey(name: 'sku') final String? sku,
|
||||
@JsonKey(name: 'name') final String? name,
|
||||
@JsonKey(name: 'description') final String? description,
|
||||
@JsonKey(name: 'price') final int? price,
|
||||
@JsonKey(name: 'cost') final int? cost,
|
||||
@JsonKey(name: 'business_type') final String? businessType,
|
||||
@JsonKey(name: 'image_url') final String? imageUrl,
|
||||
@JsonKey(name: 'printer_type') final String? printerType,
|
||||
@JsonKey(name: 'metadata') final Map<String, dynamic>? metadata,
|
||||
@JsonKey(name: 'is_active') final bool? isActive,
|
||||
@JsonKey(name: 'created_at') final DateTime? createdAt,
|
||||
@JsonKey(name: 'updated_at') final DateTime? updatedAt,
|
||||
@JsonKey(name: 'variants') final List<ProductVariantDto>? variants,
|
||||
}) = _$ProductDtoImpl;
|
||||
const _ProductDto._() : super._();
|
||||
|
||||
factory _ProductDto.fromJson(Map<String, dynamic> json) =
|
||||
_$ProductDtoImpl.fromJson;
|
||||
|
||||
@override
|
||||
@JsonKey(name: 'id')
|
||||
String? get id;
|
||||
@override
|
||||
@JsonKey(name: 'organization_id')
|
||||
String? get organizationId;
|
||||
@override
|
||||
@JsonKey(name: 'category_id')
|
||||
String? get categoryId;
|
||||
@override
|
||||
@JsonKey(name: 'sku')
|
||||
String? get sku;
|
||||
@override
|
||||
@JsonKey(name: 'name')
|
||||
String? get name;
|
||||
@override
|
||||
@JsonKey(name: 'description')
|
||||
String? get description;
|
||||
@override
|
||||
@JsonKey(name: 'price')
|
||||
int? get price;
|
||||
@override
|
||||
@JsonKey(name: 'cost')
|
||||
int? get cost;
|
||||
@override
|
||||
@JsonKey(name: 'business_type')
|
||||
String? get businessType;
|
||||
@override
|
||||
@JsonKey(name: 'image_url')
|
||||
String? get imageUrl;
|
||||
@override
|
||||
@JsonKey(name: 'printer_type')
|
||||
String? get printerType;
|
||||
@override
|
||||
@JsonKey(name: 'metadata')
|
||||
Map<String, dynamic>? get metadata;
|
||||
@override
|
||||
@JsonKey(name: 'is_active')
|
||||
bool? get isActive;
|
||||
@override
|
||||
@JsonKey(name: 'created_at')
|
||||
DateTime? get createdAt;
|
||||
@override
|
||||
@JsonKey(name: 'updated_at')
|
||||
DateTime? get updatedAt;
|
||||
@override
|
||||
@JsonKey(name: 'variants')
|
||||
List<ProductVariantDto>? get variants;
|
||||
|
||||
/// Create a copy of ProductDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$ProductDtoImplCopyWith<_$ProductDtoImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
ProductVariantDto _$ProductVariantDtoFromJson(Map<String, dynamic> json) {
|
||||
return _ProductVariantDto.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ProductVariantDto {
|
||||
@JsonKey(name: 'id')
|
||||
String? get id => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'product_id')
|
||||
String? get productId => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'name')
|
||||
String? get name => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'price_modifier')
|
||||
int? get priceModifier => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'cost')
|
||||
int? get cost => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'metadata')
|
||||
Map<String, dynamic>? get metadata => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'created_at')
|
||||
DateTime? get createdAt => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: 'updated_at')
|
||||
DateTime? get updatedAt => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this ProductVariantDto to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of ProductVariantDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$ProductVariantDtoCopyWith<ProductVariantDto> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $ProductVariantDtoCopyWith<$Res> {
|
||||
factory $ProductVariantDtoCopyWith(
|
||||
ProductVariantDto value,
|
||||
$Res Function(ProductVariantDto) then,
|
||||
) = _$ProductVariantDtoCopyWithImpl<$Res, ProductVariantDto>;
|
||||
@useResult
|
||||
$Res call({
|
||||
@JsonKey(name: 'id') String? id,
|
||||
@JsonKey(name: 'product_id') String? productId,
|
||||
@JsonKey(name: 'name') String? name,
|
||||
@JsonKey(name: 'price_modifier') int? priceModifier,
|
||||
@JsonKey(name: 'cost') int? cost,
|
||||
@JsonKey(name: 'metadata') Map<String, dynamic>? metadata,
|
||||
@JsonKey(name: 'created_at') DateTime? createdAt,
|
||||
@JsonKey(name: 'updated_at') DateTime? updatedAt,
|
||||
});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$ProductVariantDtoCopyWithImpl<$Res, $Val extends ProductVariantDto>
|
||||
implements $ProductVariantDtoCopyWith<$Res> {
|
||||
_$ProductVariantDtoCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of ProductVariantDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = freezed,
|
||||
Object? productId = freezed,
|
||||
Object? name = freezed,
|
||||
Object? priceModifier = freezed,
|
||||
Object? cost = freezed,
|
||||
Object? metadata = freezed,
|
||||
Object? createdAt = freezed,
|
||||
Object? updatedAt = freezed,
|
||||
}) {
|
||||
return _then(
|
||||
_value.copyWith(
|
||||
id: freezed == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
productId: freezed == productId
|
||||
? _value.productId
|
||||
: productId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
name: freezed == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
priceModifier: freezed == priceModifier
|
||||
? _value.priceModifier
|
||||
: priceModifier // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
cost: freezed == cost
|
||||
? _value.cost
|
||||
: cost // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
metadata: freezed == metadata
|
||||
? _value.metadata
|
||||
: metadata // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>?,
|
||||
createdAt: freezed == createdAt
|
||||
? _value.createdAt
|
||||
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
updatedAt: freezed == updatedAt
|
||||
? _value.updatedAt
|
||||
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
)
|
||||
as $Val,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$ProductVariantDtoImplCopyWith<$Res>
|
||||
implements $ProductVariantDtoCopyWith<$Res> {
|
||||
factory _$$ProductVariantDtoImplCopyWith(
|
||||
_$ProductVariantDtoImpl value,
|
||||
$Res Function(_$ProductVariantDtoImpl) then,
|
||||
) = __$$ProductVariantDtoImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({
|
||||
@JsonKey(name: 'id') String? id,
|
||||
@JsonKey(name: 'product_id') String? productId,
|
||||
@JsonKey(name: 'name') String? name,
|
||||
@JsonKey(name: 'price_modifier') int? priceModifier,
|
||||
@JsonKey(name: 'cost') int? cost,
|
||||
@JsonKey(name: 'metadata') Map<String, dynamic>? metadata,
|
||||
@JsonKey(name: 'created_at') DateTime? createdAt,
|
||||
@JsonKey(name: 'updated_at') DateTime? updatedAt,
|
||||
});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$ProductVariantDtoImplCopyWithImpl<$Res>
|
||||
extends _$ProductVariantDtoCopyWithImpl<$Res, _$ProductVariantDtoImpl>
|
||||
implements _$$ProductVariantDtoImplCopyWith<$Res> {
|
||||
__$$ProductVariantDtoImplCopyWithImpl(
|
||||
_$ProductVariantDtoImpl _value,
|
||||
$Res Function(_$ProductVariantDtoImpl) _then,
|
||||
) : super(_value, _then);
|
||||
|
||||
/// Create a copy of ProductVariantDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = freezed,
|
||||
Object? productId = freezed,
|
||||
Object? name = freezed,
|
||||
Object? priceModifier = freezed,
|
||||
Object? cost = freezed,
|
||||
Object? metadata = freezed,
|
||||
Object? createdAt = freezed,
|
||||
Object? updatedAt = freezed,
|
||||
}) {
|
||||
return _then(
|
||||
_$ProductVariantDtoImpl(
|
||||
id: freezed == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
productId: freezed == productId
|
||||
? _value.productId
|
||||
: productId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
name: freezed == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
priceModifier: freezed == priceModifier
|
||||
? _value.priceModifier
|
||||
: priceModifier // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
cost: freezed == cost
|
||||
? _value.cost
|
||||
: cost // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
metadata: freezed == metadata
|
||||
? _value._metadata
|
||||
: metadata // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>?,
|
||||
createdAt: freezed == createdAt
|
||||
? _value.createdAt
|
||||
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
updatedAt: freezed == updatedAt
|
||||
? _value.updatedAt
|
||||
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$ProductVariantDtoImpl extends _ProductVariantDto {
|
||||
const _$ProductVariantDtoImpl({
|
||||
@JsonKey(name: 'id') this.id,
|
||||
@JsonKey(name: 'product_id') this.productId,
|
||||
@JsonKey(name: 'name') this.name,
|
||||
@JsonKey(name: 'price_modifier') this.priceModifier,
|
||||
@JsonKey(name: 'cost') this.cost,
|
||||
@JsonKey(name: 'metadata') final Map<String, dynamic>? metadata,
|
||||
@JsonKey(name: 'created_at') this.createdAt,
|
||||
@JsonKey(name: 'updated_at') this.updatedAt,
|
||||
}) : _metadata = metadata,
|
||||
super._();
|
||||
|
||||
factory _$ProductVariantDtoImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$ProductVariantDtoImplFromJson(json);
|
||||
|
||||
@override
|
||||
@JsonKey(name: 'id')
|
||||
final String? id;
|
||||
@override
|
||||
@JsonKey(name: 'product_id')
|
||||
final String? productId;
|
||||
@override
|
||||
@JsonKey(name: 'name')
|
||||
final String? name;
|
||||
@override
|
||||
@JsonKey(name: 'price_modifier')
|
||||
final int? priceModifier;
|
||||
@override
|
||||
@JsonKey(name: 'cost')
|
||||
final int? cost;
|
||||
final Map<String, dynamic>? _metadata;
|
||||
@override
|
||||
@JsonKey(name: 'metadata')
|
||||
Map<String, dynamic>? get metadata {
|
||||
final value = _metadata;
|
||||
if (value == null) return null;
|
||||
if (_metadata is EqualUnmodifiableMapView) return _metadata;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableMapView(value);
|
||||
}
|
||||
|
||||
@override
|
||||
@JsonKey(name: 'created_at')
|
||||
final DateTime? createdAt;
|
||||
@override
|
||||
@JsonKey(name: 'updated_at')
|
||||
final DateTime? updatedAt;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ProductVariantDto(id: $id, productId: $productId, name: $name, priceModifier: $priceModifier, cost: $cost, metadata: $metadata, createdAt: $createdAt, updatedAt: $updatedAt)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$ProductVariantDtoImpl &&
|
||||
(identical(other.id, id) || other.id == id) &&
|
||||
(identical(other.productId, productId) ||
|
||||
other.productId == productId) &&
|
||||
(identical(other.name, name) || other.name == name) &&
|
||||
(identical(other.priceModifier, priceModifier) ||
|
||||
other.priceModifier == priceModifier) &&
|
||||
(identical(other.cost, cost) || other.cost == cost) &&
|
||||
const DeepCollectionEquality().equals(other._metadata, _metadata) &&
|
||||
(identical(other.createdAt, createdAt) ||
|
||||
other.createdAt == createdAt) &&
|
||||
(identical(other.updatedAt, updatedAt) ||
|
||||
other.updatedAt == updatedAt));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
id,
|
||||
productId,
|
||||
name,
|
||||
priceModifier,
|
||||
cost,
|
||||
const DeepCollectionEquality().hash(_metadata),
|
||||
createdAt,
|
||||
updatedAt,
|
||||
);
|
||||
|
||||
/// Create a copy of ProductVariantDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ProductVariantDtoImplCopyWith<_$ProductVariantDtoImpl> get copyWith =>
|
||||
__$$ProductVariantDtoImplCopyWithImpl<_$ProductVariantDtoImpl>(
|
||||
this,
|
||||
_$identity,
|
||||
);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$ProductVariantDtoImplToJson(this);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _ProductVariantDto extends ProductVariantDto {
|
||||
const factory _ProductVariantDto({
|
||||
@JsonKey(name: 'id') final String? id,
|
||||
@JsonKey(name: 'product_id') final String? productId,
|
||||
@JsonKey(name: 'name') final String? name,
|
||||
@JsonKey(name: 'price_modifier') final int? priceModifier,
|
||||
@JsonKey(name: 'cost') final int? cost,
|
||||
@JsonKey(name: 'metadata') final Map<String, dynamic>? metadata,
|
||||
@JsonKey(name: 'created_at') final DateTime? createdAt,
|
||||
@JsonKey(name: 'updated_at') final DateTime? updatedAt,
|
||||
}) = _$ProductVariantDtoImpl;
|
||||
const _ProductVariantDto._() : super._();
|
||||
|
||||
factory _ProductVariantDto.fromJson(Map<String, dynamic> json) =
|
||||
_$ProductVariantDtoImpl.fromJson;
|
||||
|
||||
@override
|
||||
@JsonKey(name: 'id')
|
||||
String? get id;
|
||||
@override
|
||||
@JsonKey(name: 'product_id')
|
||||
String? get productId;
|
||||
@override
|
||||
@JsonKey(name: 'name')
|
||||
String? get name;
|
||||
@override
|
||||
@JsonKey(name: 'price_modifier')
|
||||
int? get priceModifier;
|
||||
@override
|
||||
@JsonKey(name: 'cost')
|
||||
int? get cost;
|
||||
@override
|
||||
@JsonKey(name: 'metadata')
|
||||
Map<String, dynamic>? get metadata;
|
||||
@override
|
||||
@JsonKey(name: 'created_at')
|
||||
DateTime? get createdAt;
|
||||
@override
|
||||
@JsonKey(name: 'updated_at')
|
||||
DateTime? get updatedAt;
|
||||
|
||||
/// Create a copy of ProductVariantDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$ProductVariantDtoImplCopyWith<_$ProductVariantDtoImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
83
lib/infrastructure/product/product_dtos.g.dart
Normal file
83
lib/infrastructure/product/product_dtos.g.dart
Normal file
@ -0,0 +1,83 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'product_dtos.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$ProductDtoImpl _$$ProductDtoImplFromJson(Map<String, dynamic> json) =>
|
||||
_$ProductDtoImpl(
|
||||
id: json['id'] as String?,
|
||||
organizationId: json['organization_id'] as String?,
|
||||
categoryId: json['category_id'] as String?,
|
||||
sku: json['sku'] as String?,
|
||||
name: json['name'] as String?,
|
||||
description: json['description'] as String?,
|
||||
price: (json['price'] as num?)?.toInt(),
|
||||
cost: (json['cost'] as num?)?.toInt(),
|
||||
businessType: json['business_type'] as String?,
|
||||
imageUrl: json['image_url'] as String?,
|
||||
printerType: json['printer_type'] as String?,
|
||||
metadata: json['metadata'] as Map<String, dynamic>?,
|
||||
isActive: json['is_active'] as bool?,
|
||||
createdAt: json['created_at'] == null
|
||||
? null
|
||||
: DateTime.parse(json['created_at'] as String),
|
||||
updatedAt: json['updated_at'] == null
|
||||
? null
|
||||
: DateTime.parse(json['updated_at'] as String),
|
||||
variants: (json['variants'] as List<dynamic>?)
|
||||
?.map((e) => ProductVariantDto.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ProductDtoImplToJson(_$ProductDtoImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'organization_id': instance.organizationId,
|
||||
'category_id': instance.categoryId,
|
||||
'sku': instance.sku,
|
||||
'name': instance.name,
|
||||
'description': instance.description,
|
||||
'price': instance.price,
|
||||
'cost': instance.cost,
|
||||
'business_type': instance.businessType,
|
||||
'image_url': instance.imageUrl,
|
||||
'printer_type': instance.printerType,
|
||||
'metadata': instance.metadata,
|
||||
'is_active': instance.isActive,
|
||||
'created_at': instance.createdAt?.toIso8601String(),
|
||||
'updated_at': instance.updatedAt?.toIso8601String(),
|
||||
'variants': instance.variants,
|
||||
};
|
||||
|
||||
_$ProductVariantDtoImpl _$$ProductVariantDtoImplFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _$ProductVariantDtoImpl(
|
||||
id: json['id'] as String?,
|
||||
productId: json['product_id'] as String?,
|
||||
name: json['name'] as String?,
|
||||
priceModifier: (json['price_modifier'] as num?)?.toInt(),
|
||||
cost: (json['cost'] as num?)?.toInt(),
|
||||
metadata: json['metadata'] as Map<String, dynamic>?,
|
||||
createdAt: json['created_at'] == null
|
||||
? null
|
||||
: DateTime.parse(json['created_at'] as String),
|
||||
updatedAt: json['updated_at'] == null
|
||||
? null
|
||||
: DateTime.parse(json['updated_at'] as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ProductVariantDtoImplToJson(
|
||||
_$ProductVariantDtoImpl instance,
|
||||
) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'product_id': instance.productId,
|
||||
'name': instance.name,
|
||||
'price_modifier': instance.priceModifier,
|
||||
'cost': instance.cost,
|
||||
'metadata': instance.metadata,
|
||||
'created_at': instance.createdAt?.toIso8601String(),
|
||||
'updated_at': instance.updatedAt?.toIso8601String(),
|
||||
};
|
||||
@ -0,0 +1,43 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
|
||||
import '../../../domain/product/product.dart';
|
||||
import '../datasources/remote_data_provider.dart';
|
||||
|
||||
@Injectable(as: IProductRepository)
|
||||
class ProductRepository implements IProductRepository {
|
||||
final ProductRemoteDataProvider _dataProvider;
|
||||
final String _logName = 'ProductRepository';
|
||||
|
||||
ProductRepository(this._dataProvider);
|
||||
|
||||
@override
|
||||
Future<Either<ProductFailure, List<Product>>> get({
|
||||
int page = 1,
|
||||
int limit = 10,
|
||||
String? categoryId,
|
||||
String? search,
|
||||
}) async {
|
||||
try {
|
||||
final result = await _dataProvider.fetch(
|
||||
page: page,
|
||||
limit: limit,
|
||||
categoryId: categoryId,
|
||||
search: search,
|
||||
);
|
||||
|
||||
if (result.hasError) {
|
||||
return left(result.error!);
|
||||
}
|
||||
|
||||
final auth = result.data!.map((e) => e.toDomain()).toList();
|
||||
|
||||
return right(auth);
|
||||
} catch (e, s) {
|
||||
log('getProductError', name: _logName, error: e, stackTrace: s);
|
||||
return left(const ProductFailure.unexpectedError());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,13 +9,23 @@
|
||||
// coverage:ignore-file
|
||||
|
||||
// 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'
|
||||
as _i11;
|
||||
import 'package:apskel_owner_flutter/application/analytic/sales_loader/sales_loader_bloc.dart'
|
||||
as _i889;
|
||||
import 'package:apskel_owner_flutter/application/auth/auth_bloc.dart' as _i945;
|
||||
import 'package:apskel_owner_flutter/application/auth/login_form/login_form_bloc.dart'
|
||||
as _i775;
|
||||
import 'package:apskel_owner_flutter/application/auth/logout_form/logout_form_bloc.dart'
|
||||
as _i574;
|
||||
import 'package:apskel_owner_flutter/application/category/category_loader/category_loader_bloc.dart'
|
||||
as _i183;
|
||||
import 'package:apskel_owner_flutter/application/language/language_bloc.dart'
|
||||
as _i455;
|
||||
import 'package:apskel_owner_flutter/application/product/product_loader/product_loader_bloc.dart'
|
||||
as _i458;
|
||||
import 'package:apskel_owner_flutter/common/api/api_client.dart' as _i115;
|
||||
import 'package:apskel_owner_flutter/common/di/di_auto_route.dart' as _i311;
|
||||
import 'package:apskel_owner_flutter/common/di/di_connectivity.dart' as _i586;
|
||||
@ -25,14 +35,30 @@ import 'package:apskel_owner_flutter/common/di/di_shared_preferences.dart'
|
||||
as _i402;
|
||||
import 'package:apskel_owner_flutter/common/network/network_client.dart'
|
||||
as _i543;
|
||||
import 'package:apskel_owner_flutter/domain/analytic/repositories/i_analytic_repository.dart'
|
||||
as _i477;
|
||||
import 'package:apskel_owner_flutter/domain/auth/auth.dart' as _i49;
|
||||
import 'package:apskel_owner_flutter/domain/category/category.dart' as _i1020;
|
||||
import 'package:apskel_owner_flutter/domain/product/product.dart' as _i419;
|
||||
import 'package:apskel_owner_flutter/env.dart' as _i6;
|
||||
import 'package:apskel_owner_flutter/infrastructure/analytic/datasource/remote_data_provider.dart'
|
||||
as _i866;
|
||||
import 'package:apskel_owner_flutter/infrastructure/analytic/repositories/analytic_repository.dart'
|
||||
as _i393;
|
||||
import 'package:apskel_owner_flutter/infrastructure/auth/datasources/local_data_provider.dart'
|
||||
as _i991;
|
||||
import 'package:apskel_owner_flutter/infrastructure/auth/datasources/remote_data_provider.dart'
|
||||
as _i17;
|
||||
import 'package:apskel_owner_flutter/infrastructure/auth/repositories/auth_repository.dart'
|
||||
as _i1035;
|
||||
import 'package:apskel_owner_flutter/infrastructure/category/datasource/remote_data_provider.dart'
|
||||
as _i333;
|
||||
import 'package:apskel_owner_flutter/infrastructure/category/repositories/category_repository.dart'
|
||||
as _i869;
|
||||
import 'package:apskel_owner_flutter/infrastructure/product/datasources/remote_data_provider.dart'
|
||||
as _i823;
|
||||
import 'package:apskel_owner_flutter/infrastructure/product/repositories/product_repository.dart'
|
||||
as _i121;
|
||||
import 'package:apskel_owner_flutter/presentation/router/app_router.dart'
|
||||
as _i258;
|
||||
import 'package:connectivity_plus/connectivity_plus.dart' as _i895;
|
||||
@ -78,23 +104,52 @@ extension GetItInjectableX on _i174.GetIt {
|
||||
gh.factory<_i991.AuthLocalDataProvider>(
|
||||
() => _i991.AuthLocalDataProvider(gh<_i460.SharedPreferences>()),
|
||||
);
|
||||
gh.factory<_i6.Env>(() => _i6.ProdEnv(), registerFor: {_prod});
|
||||
gh.lazySingleton<_i115.ApiClient>(
|
||||
() => _i115.ApiClient(
|
||||
gh<_i361.Dio>(),
|
||||
gh<_i6.Env>(),
|
||||
gh<_i460.SharedPreferences>(),
|
||||
),
|
||||
() => _i115.ApiClient(gh<_i361.Dio>(), gh<_i6.Env>()),
|
||||
);
|
||||
gh.factory<_i6.Env>(() => _i6.ProdEnv(), registerFor: {_prod});
|
||||
gh.factory<_i333.CategoryRemoteDataProvider>(
|
||||
() => _i333.CategoryRemoteDataProvider(gh<_i115.ApiClient>()),
|
||||
);
|
||||
gh.factory<_i17.AuthRemoteDataProvider>(
|
||||
() => _i17.AuthRemoteDataProvider(gh<_i115.ApiClient>()),
|
||||
);
|
||||
gh.factory<_i823.ProductRemoteDataProvider>(
|
||||
() => _i823.ProductRemoteDataProvider(gh<_i115.ApiClient>()),
|
||||
);
|
||||
gh.factory<_i866.AnalyticRemoteDataProvider>(
|
||||
() => _i866.AnalyticRemoteDataProvider(gh<_i115.ApiClient>()),
|
||||
);
|
||||
gh.factory<_i477.IAnalyticRepository>(
|
||||
() => _i393.AnalyticRepository(gh<_i866.AnalyticRemoteDataProvider>()),
|
||||
);
|
||||
gh.factory<_i49.IAuthRepository>(
|
||||
() => _i1035.AuthRepository(
|
||||
gh<_i991.AuthLocalDataProvider>(),
|
||||
gh<_i17.AuthRemoteDataProvider>(),
|
||||
),
|
||||
);
|
||||
gh.factory<_i419.IProductRepository>(
|
||||
() => _i121.ProductRepository(gh<_i823.ProductRemoteDataProvider>()),
|
||||
);
|
||||
gh.factory<_i1020.ICategoryRepository>(
|
||||
() => _i869.CategoryRepository(gh<_i333.CategoryRemoteDataProvider>()),
|
||||
);
|
||||
gh.factory<_i458.ProductLoaderBloc>(
|
||||
() => _i458.ProductLoaderBloc(gh<_i419.IProductRepository>()),
|
||||
);
|
||||
gh.factory<_i183.CategoryLoaderBloc>(
|
||||
() => _i183.CategoryLoaderBloc(gh<_i1020.ICategoryRepository>()),
|
||||
);
|
||||
gh.factory<_i889.SalesLoaderBloc>(
|
||||
() => _i889.SalesLoaderBloc(gh<_i477.IAnalyticRepository>()),
|
||||
);
|
||||
gh.factory<_i11.ProfitLossLoaderBloc>(
|
||||
() => _i11.ProfitLossLoaderBloc(gh<_i477.IAnalyticRepository>()),
|
||||
);
|
||||
gh.factory<_i1038.CategoryAnalyticLoaderBloc>(
|
||||
() => _i1038.CategoryAnalyticLoaderBloc(gh<_i477.IAnalyticRepository>()),
|
||||
);
|
||||
gh.factory<_i775.LoginFormBloc>(
|
||||
() => _i775.LoginFormBloc(gh<_i49.IAuthRepository>()),
|
||||
);
|
||||
|
||||
5
lib/presentation/components/image/image.dart
Normal file
5
lib/presentation/components/image/image.dart
Normal file
@ -0,0 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:shimmer/shimmer.dart';
|
||||
|
||||
part 'network_image.dart';
|
||||
68
lib/presentation/components/image/network_image.dart
Normal file
68
lib/presentation/components/image/network_image.dart
Normal file
@ -0,0 +1,68 @@
|
||||
part of 'image.dart';
|
||||
|
||||
class AppNetworkImage extends StatelessWidget {
|
||||
final String? url;
|
||||
final double? height;
|
||||
final double? width;
|
||||
final double? borderRadius;
|
||||
final BoxFit? fit;
|
||||
final bool? isCanZoom;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
const AppNetworkImage({
|
||||
super.key,
|
||||
this.url,
|
||||
this.height,
|
||||
this.width,
|
||||
this.borderRadius = 0,
|
||||
this.fit = BoxFit.cover,
|
||||
this.isCanZoom = false,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget customPhoto(
|
||||
double? heightx,
|
||||
double? widthx,
|
||||
BoxFit? fitx,
|
||||
double? radius,
|
||||
) {
|
||||
return CachedNetworkImage(
|
||||
imageUrl: url.toString(),
|
||||
placeholder: (context, url) => Shimmer.fromColors(
|
||||
baseColor: Colors.grey[300]!,
|
||||
highlightColor: Colors.grey[100]!,
|
||||
child: Container(
|
||||
height: height,
|
||||
width: width,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade300,
|
||||
borderRadius: BorderRadius.circular(radius ?? 0),
|
||||
),
|
||||
),
|
||||
),
|
||||
errorWidget: (context, url, error) => Container(
|
||||
height: height,
|
||||
width: width,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade200,
|
||||
borderRadius: BorderRadius.circular(radius ?? 0),
|
||||
),
|
||||
child: Icon(Icons.image_outlined, color: Colors.grey.shade400),
|
||||
),
|
||||
height: heightx,
|
||||
width: widthx,
|
||||
fit: fitx,
|
||||
);
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(borderRadius!),
|
||||
child: customPhoto(height, width, BoxFit.fill, borderRadius),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
27
lib/presentation/components/widgets/empty_search_widget.dart
Normal file
27
lib/presentation/components/widgets/empty_search_widget.dart
Normal file
@ -0,0 +1,27 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../common/theme/theme.dart';
|
||||
import 'empty_widget.dart';
|
||||
|
||||
class EmptySearchWidget extends StatelessWidget {
|
||||
final String? searchQuery;
|
||||
final VoidCallback? onClear;
|
||||
|
||||
const EmptySearchWidget({Key? key, this.searchQuery, this.onClear})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return EmptyWidget(
|
||||
title: 'Pencarian Tidak Ditemukan',
|
||||
message: searchQuery != null
|
||||
? 'Tidak ada hasil untuk "$searchQuery"\nCoba kata kunci lain'
|
||||
: 'Coba gunakan kata kunci yang berbeda',
|
||||
emptyIcon: Icons.search_off_rounded,
|
||||
iconColor: AppColor.warning,
|
||||
buttonText: 'Hapus Filter',
|
||||
onRefresh: onClear,
|
||||
showButton: onClear != null,
|
||||
);
|
||||
}
|
||||
}
|
||||
179
lib/presentation/components/widgets/empty_widget.dart
Normal file
179
lib/presentation/components/widgets/empty_widget.dart
Normal file
@ -0,0 +1,179 @@
|
||||
// ==================== EMPTY WIDGET ====================
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../common/theme/theme.dart';
|
||||
|
||||
class EmptyWidget extends StatefulWidget {
|
||||
final String? title;
|
||||
final String? message;
|
||||
final VoidCallback? onRefresh;
|
||||
final IconData? emptyIcon;
|
||||
final String? buttonText;
|
||||
final double? width;
|
||||
final double? height;
|
||||
final bool showButton;
|
||||
final EdgeInsets? padding;
|
||||
final Color? iconColor;
|
||||
|
||||
const EmptyWidget({
|
||||
super.key,
|
||||
this.title,
|
||||
this.message,
|
||||
this.onRefresh,
|
||||
this.emptyIcon,
|
||||
this.buttonText,
|
||||
this.width,
|
||||
this.height,
|
||||
this.showButton = true,
|
||||
this.padding,
|
||||
this.iconColor,
|
||||
});
|
||||
|
||||
@override
|
||||
State<EmptyWidget> createState() => _EmptyWidgetState();
|
||||
}
|
||||
|
||||
class _EmptyWidgetState extends State<EmptyWidget>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _animationController;
|
||||
late Animation<double> _fadeAnimation;
|
||||
late Animation<Offset> _slideAnimation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_animationController = AnimationController(
|
||||
duration: const Duration(milliseconds: 800),
|
||||
vsync: this,
|
||||
);
|
||||
|
||||
_fadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
|
||||
CurvedAnimation(parent: _animationController, curve: Curves.easeInOut),
|
||||
);
|
||||
|
||||
_slideAnimation =
|
||||
Tween<Offset>(begin: const Offset(0.0, 0.3), end: Offset.zero).animate(
|
||||
CurvedAnimation(
|
||||
parent: _animationController,
|
||||
curve: Curves.easeOutCubic,
|
||||
),
|
||||
);
|
||||
|
||||
_animationController.forward();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_animationController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: widget.width,
|
||||
height: widget.height,
|
||||
padding: widget.padding ?? const EdgeInsets.all(24),
|
||||
child: FadeTransition(
|
||||
opacity: _fadeAnimation,
|
||||
child: SlideTransition(
|
||||
position: _slideAnimation,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Empty Illustration
|
||||
Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
// Background Circle
|
||||
Container(
|
||||
width: 120,
|
||||
height: 120,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
AppColor.primary.withOpacity(0.05),
|
||||
AppColor.secondary.withOpacity(0.05),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Icon Container
|
||||
Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: AppColor.surface,
|
||||
border: Border.all(color: AppColor.border, width: 2),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColor.primary.withOpacity(0.08),
|
||||
blurRadius: 20,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Icon(
|
||||
widget.emptyIcon ?? Icons.inbox_outlined,
|
||||
size: 40,
|
||||
color: widget.iconColor ?? AppColor.textLight,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Title
|
||||
Text(
|
||||
widget.title ?? 'Tidak Ada Data',
|
||||
style: AppStyle.xl.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColor.textPrimary,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// Message
|
||||
Text(
|
||||
widget.message ?? 'Belum ada data untuk ditampilkan',
|
||||
style: AppStyle.md.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
height: 1.4,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
|
||||
// Action Button
|
||||
if (widget.showButton && widget.onRefresh != null) ...[
|
||||
const SizedBox(height: 24),
|
||||
OutlinedButton.icon(
|
||||
onPressed: widget.onRefresh,
|
||||
icon: const Icon(Icons.refresh_rounded, size: 18),
|
||||
label: Text(widget.buttonText ?? 'Muat Ulang'),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: AppColor.primary,
|
||||
side: BorderSide(color: AppColor.primary, width: 1.5),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 20,
|
||||
vertical: 12,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
176
lib/presentation/components/widgets/error_widget.dart
Normal file
176
lib/presentation/components/widgets/error_widget.dart
Normal file
@ -0,0 +1,176 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../common/theme/theme.dart';
|
||||
|
||||
class ErrorWidget extends StatefulWidget {
|
||||
final String? title;
|
||||
final String? message;
|
||||
final VoidCallback? onRetry;
|
||||
final String? errorCode;
|
||||
final IconData? errorIcon;
|
||||
final double? width;
|
||||
final double? height;
|
||||
final bool showRetryButton;
|
||||
final EdgeInsets? padding;
|
||||
|
||||
const ErrorWidget({
|
||||
super.key,
|
||||
this.title,
|
||||
this.message,
|
||||
this.onRetry,
|
||||
this.errorCode,
|
||||
this.errorIcon,
|
||||
this.width,
|
||||
this.height,
|
||||
this.showRetryButton = true,
|
||||
this.padding,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ErrorWidget> createState() => _ErrorWidgetState();
|
||||
}
|
||||
|
||||
class _ErrorWidgetState extends State<ErrorWidget>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _animationController;
|
||||
late Animation<double> _fadeAnimation;
|
||||
late Animation<double> _scaleAnimation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_animationController = AnimationController(
|
||||
duration: const Duration(milliseconds: 600),
|
||||
vsync: this,
|
||||
);
|
||||
|
||||
_fadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
|
||||
CurvedAnimation(parent: _animationController, curve: Curves.easeInOut),
|
||||
);
|
||||
|
||||
_scaleAnimation = Tween<double>(begin: 0.8, end: 1.0).animate(
|
||||
CurvedAnimation(parent: _animationController, curve: Curves.elasticOut),
|
||||
);
|
||||
|
||||
_animationController.forward();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_animationController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: widget.width,
|
||||
height: widget.height,
|
||||
padding: widget.padding ?? const EdgeInsets.all(24),
|
||||
child: FadeTransition(
|
||||
opacity: _fadeAnimation,
|
||||
child: ScaleTransition(
|
||||
scale: _scaleAnimation,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Error Icon
|
||||
Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
AppColor.error.withOpacity(0.1),
|
||||
AppColor.warning.withOpacity(0.1),
|
||||
],
|
||||
),
|
||||
border: Border.all(
|
||||
color: AppColor.error.withOpacity(0.2),
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: Icon(
|
||||
widget.errorIcon ?? Icons.error_outline_rounded,
|
||||
size: 40,
|
||||
color: AppColor.error,
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Title
|
||||
if (widget.title != null)
|
||||
Text(
|
||||
widget.title!,
|
||||
style: AppStyle.xl.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColor.textPrimary,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// Message
|
||||
Text(
|
||||
widget.message ?? 'Terjadi kesalahan tidak terduga',
|
||||
style: AppStyle.md.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
height: 1.4,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
|
||||
// Error Code
|
||||
if (widget.errorCode != null) ...[
|
||||
const SizedBox(height: 12),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 6,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.error.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: AppColor.error.withOpacity(0.2)),
|
||||
),
|
||||
child: Text(
|
||||
'Error: ${widget.errorCode}',
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: AppColor.error,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
// Retry Button
|
||||
if (widget.showRetryButton && widget.onRetry != null) ...[
|
||||
const SizedBox(height: 20),
|
||||
ElevatedButton.icon(
|
||||
onPressed: widget.onRetry,
|
||||
icon: const Icon(Icons.refresh_rounded, size: 18),
|
||||
label: const Text('Coba Lagi'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColor.primary,
|
||||
foregroundColor: AppColor.textWhite,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 20,
|
||||
vertical: 12,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
509
lib/presentation/pages/error/error_page.dart
Normal file
509
lib/presentation/pages/error/error_page.dart
Normal file
@ -0,0 +1,509 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../common/theme/theme.dart';
|
||||
|
||||
@RoutePage()
|
||||
class ErrorPage extends StatefulWidget {
|
||||
final String? title;
|
||||
final String? message;
|
||||
final VoidCallback? onRetry;
|
||||
final VoidCallback? onBack;
|
||||
final String? errorCode;
|
||||
final IconData? errorIcon;
|
||||
|
||||
const ErrorPage({
|
||||
Key? key,
|
||||
this.title,
|
||||
this.message,
|
||||
this.onRetry,
|
||||
this.onBack,
|
||||
this.errorCode,
|
||||
this.errorIcon,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<ErrorPage> createState() => _ErrorPageState();
|
||||
}
|
||||
|
||||
class _ErrorPageState extends State<ErrorPage> with TickerProviderStateMixin {
|
||||
late AnimationController _bounceController;
|
||||
late AnimationController _fadeController;
|
||||
late AnimationController _slideController;
|
||||
|
||||
late Animation<double> _bounceAnimation;
|
||||
late Animation<double> _fadeAnimation;
|
||||
late Animation<Offset> _slideAnimation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_bounceController = AnimationController(
|
||||
duration: const Duration(milliseconds: 1200),
|
||||
vsync: this,
|
||||
);
|
||||
|
||||
_fadeController = AnimationController(
|
||||
duration: const Duration(milliseconds: 800),
|
||||
vsync: this,
|
||||
);
|
||||
|
||||
_slideController = AnimationController(
|
||||
duration: const Duration(milliseconds: 1000),
|
||||
vsync: this,
|
||||
);
|
||||
|
||||
_bounceAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
|
||||
CurvedAnimation(parent: _bounceController, curve: Curves.elasticOut),
|
||||
);
|
||||
|
||||
_fadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
|
||||
CurvedAnimation(parent: _fadeController, curve: Curves.easeInOut),
|
||||
);
|
||||
|
||||
_slideAnimation =
|
||||
Tween<Offset>(begin: const Offset(0.0, 0.5), end: Offset.zero).animate(
|
||||
CurvedAnimation(parent: _slideController, curve: Curves.easeOutCubic),
|
||||
);
|
||||
|
||||
// Start animations
|
||||
_bounceController.forward();
|
||||
Future.delayed(const Duration(milliseconds: 300), () {
|
||||
_fadeController.forward();
|
||||
});
|
||||
Future.delayed(const Duration(milliseconds: 500), () {
|
||||
_slideController.forward();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_bounceController.dispose();
|
||||
_fadeController.dispose();
|
||||
_slideController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
AppColor.primary.withOpacity(0.05),
|
||||
AppColor.background,
|
||||
AppColor.primaryLight.withOpacity(0.03),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(24.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(height: 60),
|
||||
|
||||
// Animated Error Illustration
|
||||
AnimatedBuilder(
|
||||
animation: _bounceAnimation,
|
||||
builder: (context, child) {
|
||||
return Transform.scale(
|
||||
scale: _bounceAnimation.value,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
// Outer glow circle
|
||||
Container(
|
||||
width: 200,
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: RadialGradient(
|
||||
colors: [
|
||||
AppColor.error.withOpacity(0.1),
|
||||
Colors.transparent,
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Middle circle with gradient
|
||||
Container(
|
||||
width: 140,
|
||||
height: 140,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
AppColor.error.withOpacity(0.15),
|
||||
AppColor.warning.withOpacity(0.15),
|
||||
AppColor.primary.withOpacity(0.1),
|
||||
],
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColor.error.withOpacity(0.2),
|
||||
blurRadius: 30,
|
||||
offset: const Offset(0, 10),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Inner circle with icon
|
||||
Container(
|
||||
width: 100,
|
||||
height: 100,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: LinearGradient(
|
||||
colors: AppColor.primaryGradient,
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColor.primary.withOpacity(0.3),
|
||||
blurRadius: 20,
|
||||
offset: const Offset(0, 8),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Icon(
|
||||
widget.errorIcon ?? Icons.sentiment_dissatisfied,
|
||||
size: 50,
|
||||
color: AppColor.textWhite,
|
||||
),
|
||||
),
|
||||
|
||||
// Decorative floating dots
|
||||
...List.generate(6, (index) {
|
||||
final radius = 120.0;
|
||||
return Positioned(
|
||||
left:
|
||||
100 +
|
||||
radius *
|
||||
0.8 *
|
||||
(index.isEven ? 1 : -1) *
|
||||
(index / 6),
|
||||
top:
|
||||
100 +
|
||||
radius *
|
||||
0.6 *
|
||||
(index.isOdd ? 1 : -1) *
|
||||
(index / 6),
|
||||
child: Container(
|
||||
width: 8 + (index % 3) * 4,
|
||||
height: 8 + (index % 3) * 4,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: [
|
||||
AppColor.primary,
|
||||
AppColor.error,
|
||||
AppColor.warning,
|
||||
][index % 3].withOpacity(0.6),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
const SizedBox(height: 40),
|
||||
|
||||
// Animated Title
|
||||
FadeTransition(
|
||||
opacity: _fadeAnimation,
|
||||
child: Text(
|
||||
widget.title ?? 'Ups! Ada Yang Salah',
|
||||
style: AppStyle.h2.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColor.primary, // warna solid
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Animated Message
|
||||
SlideTransition(
|
||||
position: _slideAnimation,
|
||||
child: FadeTransition(
|
||||
opacity: _fadeAnimation,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.surface,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColor.primary.withOpacity(0.08),
|
||||
blurRadius: 20,
|
||||
offset: const Offset(0, 8),
|
||||
),
|
||||
],
|
||||
border: Border.all(
|
||||
color: AppColor.border.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
widget.message ??
|
||||
'Sepertinya ada masalah teknis yang tidak terduga. Jangan khawatir, tim kami sedang bekerja keras untuk memperbaikinya!',
|
||||
style: AppStyle.lg.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
height: 1.6,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
|
||||
if (widget.errorCode != null) ...[
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
AppColor.error.withOpacity(0.1),
|
||||
AppColor.warning.withOpacity(0.1),
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(
|
||||
color: AppColor.error.withOpacity(0.3),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.code,
|
||||
size: 16,
|
||||
color: AppColor.error,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'Kode Error: ${widget.errorCode}',
|
||||
style: AppStyle.sm.copyWith(
|
||||
color: AppColor.error,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 50),
|
||||
|
||||
// Animated Buttons
|
||||
SlideTransition(
|
||||
position: _slideAnimation,
|
||||
child: FadeTransition(
|
||||
opacity: _fadeAnimation,
|
||||
child: Column(
|
||||
children: [
|
||||
// Retry Button with gradient
|
||||
if (widget.onRetry != null)
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 60,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: AppColor.primaryGradient,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColor.primary.withOpacity(0.4),
|
||||
blurRadius: 15,
|
||||
offset: const Offset(0, 8),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: widget.onRetry,
|
||||
icon: const Icon(
|
||||
Icons.refresh_rounded,
|
||||
color: AppColor.textWhite,
|
||||
size: 24,
|
||||
),
|
||||
label: Text(
|
||||
'Coba Lagi',
|
||||
style: AppStyle.xl.copyWith(
|
||||
color: AppColor.textWhite,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Back Button with modern design
|
||||
if (widget.onBack != null)
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 60,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.surface,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
width: 2,
|
||||
color: AppColor.primary.withOpacity(0.3),
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColor.primary.withOpacity(0.1),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: widget.onBack,
|
||||
icon: Icon(
|
||||
Icons.arrow_back_ios_rounded,
|
||||
color: AppColor.primary,
|
||||
size: 20,
|
||||
),
|
||||
label: Text(
|
||||
'Kembali',
|
||||
style: AppStyle.xl.copyWith(
|
||||
color: AppColor.primary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
style: OutlinedButton.styleFrom(
|
||||
backgroundColor: Colors.transparent,
|
||||
side: BorderSide.none,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 40),
|
||||
|
||||
// Help text with icon
|
||||
FadeTransition(
|
||||
opacity: _fadeAnimation,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.info.withOpacity(0.05),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: AppColor.info.withOpacity(0.1)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.help_outline_rounded,
|
||||
size: 18,
|
||||
color: AppColor.info,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Flexible(
|
||||
child: Text(
|
||||
'Butuh bantuan? Hubungi tim support kami',
|
||||
style: AppStyle.sm.copyWith(
|
||||
color: AppColor.info,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage Example dengan berbagai variasi
|
||||
class ErrorPageExamples extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
// Network Error
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ErrorPage(
|
||||
title: 'Koneksi Terputus',
|
||||
message:
|
||||
'Sepertinya koneksi internet Anda bermasalah. Periksa jaringan dan coba lagi.',
|
||||
errorCode: 'NET_404',
|
||||
errorIcon: Icons.wifi_off_rounded,
|
||||
onRetry: () => Navigator.pop(context),
|
||||
onBack: () => Navigator.pop(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Text('Network Error'),
|
||||
),
|
||||
|
||||
// Server Error
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ErrorPage(
|
||||
title: 'Server Sibuk',
|
||||
message:
|
||||
'Server sedang mengalami gangguan. Tim kami sedang memperbaikinya.',
|
||||
errorCode: 'SRV_500',
|
||||
errorIcon: Icons.dns_rounded,
|
||||
onRetry: () => Navigator.pop(context),
|
||||
onBack: () => Navigator.pop(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Text('Server Error'),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,20 +1,43 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.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 '../../../common/extension/extension.dart';
|
||||
import '../../../common/theme/theme.dart';
|
||||
import '../../../domain/analytic/analytic.dart';
|
||||
import '../../../injection.dart';
|
||||
import '../../components/appbar/appbar.dart';
|
||||
import 'widgets/cash_flow.dart';
|
||||
import 'widgets/category.dart';
|
||||
import 'widgets/product.dart';
|
||||
import 'widgets/profit_loss.dart';
|
||||
import 'widgets/summary_card.dart';
|
||||
|
||||
@RoutePage()
|
||||
class FinancePage extends StatefulWidget {
|
||||
class FinancePage extends StatefulWidget implements AutoRouteWrapper {
|
||||
const FinancePage({super.key});
|
||||
|
||||
@override
|
||||
State<FinancePage> createState() => _FinancePageState();
|
||||
|
||||
@override
|
||||
Widget wrappedRoute(BuildContext context) => MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
create: (_) =>
|
||||
getIt<ProfitLossLoaderBloc>()..add(ProfitLossLoaderEvent.fetched()),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (context) =>
|
||||
getIt<CategoryAnalyticLoaderBloc>()
|
||||
..add(CategoryAnalyticLoaderEvent.fetched()),
|
||||
),
|
||||
],
|
||||
child: this,
|
||||
);
|
||||
}
|
||||
|
||||
class _FinancePageState extends State<FinancePage>
|
||||
@ -90,69 +113,83 @@ class _FinancePageState extends State<FinancePage>
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColor.background,
|
||||
body: CustomScrollView(
|
||||
slivers: [
|
||||
// SliverAppBar with animated background
|
||||
SliverAppBar(
|
||||
expandedHeight: 120,
|
||||
floating: false,
|
||||
pinned: true,
|
||||
backgroundColor: AppColor.primary,
|
||||
elevation: 0,
|
||||
flexibleSpace: CustomAppBar(title: 'Keuangan'),
|
||||
),
|
||||
body: BlocBuilder<ProfitLossLoaderBloc, ProfitLossLoaderState>(
|
||||
builder: (context, state) {
|
||||
return CustomScrollView(
|
||||
slivers: [
|
||||
// SliverAppBar with animated background
|
||||
SliverAppBar(
|
||||
expandedHeight: 120,
|
||||
floating: false,
|
||||
pinned: true,
|
||||
backgroundColor: AppColor.primary,
|
||||
elevation: 0,
|
||||
flexibleSpace: CustomAppBar(title: 'Keuangan'),
|
||||
),
|
||||
|
||||
// Header dengan filter periode
|
||||
SliverToBoxAdapter(
|
||||
child: FadeTransition(
|
||||
opacity: _fadeAnimation,
|
||||
child: _buildPeriodSelector(),
|
||||
),
|
||||
),
|
||||
// Header dengan filter periode
|
||||
SliverToBoxAdapter(
|
||||
child: FadeTransition(
|
||||
opacity: _fadeAnimation,
|
||||
child: _buildPeriodSelector(),
|
||||
),
|
||||
),
|
||||
|
||||
// Summary Cards
|
||||
SliverToBoxAdapter(
|
||||
child: SlideTransition(
|
||||
position: _slideAnimation,
|
||||
child: _buildSummaryCards(),
|
||||
),
|
||||
),
|
||||
// Summary Cards
|
||||
SliverToBoxAdapter(
|
||||
child: SlideTransition(
|
||||
position: _slideAnimation,
|
||||
child: _buildSummaryCards(state.profitLoss.summary),
|
||||
),
|
||||
),
|
||||
|
||||
// Cash Flow Analysis
|
||||
SliverToBoxAdapter(
|
||||
child: ScaleTransition(
|
||||
scale: _scaleAnimation,
|
||||
child: FinanceCashFlow(),
|
||||
),
|
||||
),
|
||||
// Cash Flow Analysis
|
||||
SliverToBoxAdapter(
|
||||
child: ScaleTransition(
|
||||
scale: _scaleAnimation,
|
||||
child: FinanceCashFlow(dailyData: state.profitLoss.data),
|
||||
),
|
||||
),
|
||||
|
||||
// Profit Loss Detail
|
||||
SliverToBoxAdapter(
|
||||
child: FadeTransition(
|
||||
opacity: _fadeAnimation,
|
||||
child: FinanceProfitLoss(),
|
||||
),
|
||||
),
|
||||
// Profit Loss Detail
|
||||
SliverToBoxAdapter(
|
||||
child: FadeTransition(
|
||||
opacity: _fadeAnimation,
|
||||
child: FinanceProfitLoss(data: state.profitLoss.summary),
|
||||
),
|
||||
),
|
||||
|
||||
// Transaction Categories
|
||||
SliverToBoxAdapter(
|
||||
child: SlideTransition(
|
||||
position: _slideAnimation,
|
||||
child: FinanceCategory(),
|
||||
),
|
||||
),
|
||||
BlocBuilder<
|
||||
CategoryAnalyticLoaderBloc,
|
||||
CategoryAnalyticLoaderState
|
||||
>(
|
||||
builder: (context, stateCategory) {
|
||||
return SliverToBoxAdapter(
|
||||
child: SlideTransition(
|
||||
position: _slideAnimation,
|
||||
child: FinanceCategory(
|
||||
categories: stateCategory.categoryAnalytic.data,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
// Monthly Comparison
|
||||
SliverToBoxAdapter(
|
||||
child: ScaleTransition(
|
||||
scale: _scaleAnimation,
|
||||
child: _buildMonthlyComparison(),
|
||||
),
|
||||
),
|
||||
// Product Analysis Section
|
||||
SliverToBoxAdapter(
|
||||
child: SlideTransition(
|
||||
position: _slideAnimation,
|
||||
child: _buildProductAnalysis(state.profitLoss.productData),
|
||||
),
|
||||
),
|
||||
|
||||
// Bottom spacing
|
||||
const SliverToBoxAdapter(child: SizedBox(height: 100)),
|
||||
],
|
||||
// Transaction Categories
|
||||
|
||||
// Bottom spacing
|
||||
const SliverToBoxAdapter(child: SizedBox(height: 100)),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -210,7 +247,7 @@ class _FinancePageState extends State<FinancePage>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSummaryCards() {
|
||||
Widget _buildSummaryCards(ProfitLossSummary summary) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Column(
|
||||
@ -220,10 +257,9 @@ class _FinancePageState extends State<FinancePage>
|
||||
Expanded(
|
||||
child: FinanceSummaryCard(
|
||||
title: 'Total Pendapatan',
|
||||
amount: 'Rp 25.840.000',
|
||||
amount: summary.totalRevenue.currencyFormatRp,
|
||||
icon: LineIcons.arrowUp,
|
||||
color: AppColor.success,
|
||||
change: '+12.5%',
|
||||
isPositive: true,
|
||||
),
|
||||
),
|
||||
@ -231,10 +267,9 @@ class _FinancePageState extends State<FinancePage>
|
||||
Expanded(
|
||||
child: FinanceSummaryCard(
|
||||
title: 'Total Pengeluaran',
|
||||
amount: 'Rp 18.320.000',
|
||||
amount: summary.totalCost.currencyFormatRp,
|
||||
icon: LineIcons.arrowDown,
|
||||
color: AppColor.error,
|
||||
change: '+8.2%',
|
||||
isPositive: false,
|
||||
),
|
||||
),
|
||||
@ -246,10 +281,9 @@ class _FinancePageState extends State<FinancePage>
|
||||
Expanded(
|
||||
child: FinanceSummaryCard(
|
||||
title: 'Keuntungan Bersih',
|
||||
amount: 'Rp 7.520.000',
|
||||
amount: summary.netProfit.currencyFormatRp,
|
||||
icon: LineIcons.lineChart,
|
||||
color: AppColor.info,
|
||||
change: '+15.3%',
|
||||
isPositive: true,
|
||||
),
|
||||
),
|
||||
@ -257,10 +291,9 @@ class _FinancePageState extends State<FinancePage>
|
||||
Expanded(
|
||||
child: FinanceSummaryCard(
|
||||
title: 'Margin Profit',
|
||||
amount: '29.1%',
|
||||
amount: '${summary.profitabilityRatio.round()}%',
|
||||
icon: LineIcons.percent,
|
||||
color: AppColor.warning,
|
||||
change: '+2.1%',
|
||||
isPositive: true,
|
||||
),
|
||||
),
|
||||
@ -271,7 +304,7 @@ class _FinancePageState extends State<FinancePage>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMonthlyComparison() {
|
||||
Widget _buildProductAnalysis(List<ProfitLossProductData> products) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.all(16),
|
||||
padding: const EdgeInsets.all(20),
|
||||
@ -295,138 +328,41 @@ class _FinancePageState extends State<FinancePage>
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.warning.withOpacity(0.1),
|
||||
color: AppColor.info.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: const Icon(
|
||||
LineIcons.calendarCheck,
|
||||
color: AppColor.warning,
|
||||
LineIcons.shoppingBag,
|
||||
color: AppColor.info,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
'Perbandingan Bulanan',
|
||||
'Analisis Produk',
|
||||
style: AppStyle.lg.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildComparisonCard(
|
||||
'Bulan Ini',
|
||||
'Rp 7.52M',
|
||||
'+15.3%',
|
||||
true,
|
||||
AppColor.primary,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: _buildComparisonCard(
|
||||
'Bulan Lalu',
|
||||
'Rp 6.53M',
|
||||
'-2.1%',
|
||||
false,
|
||||
AppColor.textSecondary,
|
||||
const Spacer(),
|
||||
TextButton(
|
||||
onPressed: () {},
|
||||
child: Text(
|
||||
'Lihat Semua',
|
||||
style: AppStyle.sm.copyWith(color: AppColor.primary),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.success.withOpacity(0.05),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: AppColor.success.withOpacity(0.2)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(
|
||||
LineIcons.thumbsUp,
|
||||
color: AppColor.success,
|
||||
size: 20,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Performa Bagus!',
|
||||
style: AppStyle.md.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColor.success,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Keuntungan meningkat 15.3% dari bulan lalu',
|
||||
style: AppStyle.sm.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildComparisonCard(
|
||||
String period,
|
||||
String amount,
|
||||
String change,
|
||||
bool isPositive,
|
||||
Color color,
|
||||
) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withOpacity(0.05),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: color.withOpacity(0.2)),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
period,
|
||||
style: AppStyle.sm.copyWith(color: AppColor.textSecondary),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
amount,
|
||||
style: AppStyle.lg.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
isPositive ? LineIcons.arrowUp : LineIcons.arrowDown,
|
||||
size: 14,
|
||||
color: isPositive ? AppColor.success : AppColor.error,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
change,
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: isPositive ? AppColor.success : AppColor.error,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
// Product list
|
||||
ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
padding: const EdgeInsets.only(top: 12),
|
||||
itemCount: products.length,
|
||||
separatorBuilder: (context, index) => const SizedBox(height: 12),
|
||||
itemBuilder: (context, index) {
|
||||
final product = products[index];
|
||||
return ProfitLossProduct(product: product);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@ -1,14 +1,23 @@
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:line_icons/line_icons.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../../domain/analytic/analytic.dart';
|
||||
|
||||
class FinanceCashFlow extends StatelessWidget {
|
||||
const FinanceCashFlow({super.key});
|
||||
final List<ProfitLossDailyData> dailyData;
|
||||
|
||||
const FinanceCashFlow({super.key, required this.dailyData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Calculate totals from daily data
|
||||
final totalCashIn = _calculateTotalCashIn();
|
||||
final totalCashOut = _calculateTotalCashOut();
|
||||
final netFlow = totalCashIn - totalCashOut;
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.all(16),
|
||||
padding: const EdgeInsets.all(20),
|
||||
@ -70,7 +79,7 @@ class FinanceCashFlow extends StatelessWidget {
|
||||
Expanded(
|
||||
child: _buildCashFlowIndicator(
|
||||
'Cash In',
|
||||
'Rp 28.5M',
|
||||
_formatCurrency(totalCashIn),
|
||||
LineIcons.arrowUp,
|
||||
AppColor.success,
|
||||
),
|
||||
@ -79,7 +88,7 @@ class FinanceCashFlow extends StatelessWidget {
|
||||
Expanded(
|
||||
child: _buildCashFlowIndicator(
|
||||
'Cash Out',
|
||||
'Rp 21.2M',
|
||||
_formatCurrency(totalCashOut),
|
||||
LineIcons.arrowDown,
|
||||
AppColor.error,
|
||||
),
|
||||
@ -88,7 +97,7 @@ class FinanceCashFlow extends StatelessWidget {
|
||||
Expanded(
|
||||
child: _buildCashFlowIndicator(
|
||||
'Net Flow',
|
||||
'Rp 7.3M',
|
||||
_formatCurrency(netFlow),
|
||||
LineIcons.equals,
|
||||
AppColor.info,
|
||||
),
|
||||
@ -110,7 +119,7 @@ class FinanceCashFlow extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Grafik Cash Flow 7 Hari Terakhir',
|
||||
'Grafik Cash Flow ${dailyData.length} Hari Terakhir',
|
||||
style: AppStyle.sm.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
fontWeight: FontWeight.w600,
|
||||
@ -118,207 +127,9 @@ class FinanceCashFlow extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Expanded(
|
||||
child: LineChart(
|
||||
LineChartData(
|
||||
gridData: FlGridData(
|
||||
show: true,
|
||||
drawVerticalLine: false,
|
||||
horizontalInterval: 5000000, // 5M interval
|
||||
getDrawingHorizontalLine: (value) {
|
||||
return FlLine(
|
||||
color: AppColor.borderLight,
|
||||
strokeWidth: 1,
|
||||
);
|
||||
},
|
||||
),
|
||||
titlesData: FlTitlesData(
|
||||
show: true,
|
||||
rightTitles: AxisTitles(
|
||||
sideTitles: SideTitles(showTitles: false),
|
||||
),
|
||||
topTitles: AxisTitles(
|
||||
sideTitles: SideTitles(showTitles: false),
|
||||
),
|
||||
bottomTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: true,
|
||||
reservedSize: 30,
|
||||
interval: 1,
|
||||
getTitlesWidget: (double value, TitleMeta meta) {
|
||||
const style = TextStyle(
|
||||
color: AppColor.textSecondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 10,
|
||||
);
|
||||
Widget text;
|
||||
switch (value.toInt()) {
|
||||
case 0:
|
||||
text = const Text('Sen', style: style);
|
||||
break;
|
||||
case 1:
|
||||
text = const Text('Sel', style: style);
|
||||
break;
|
||||
case 2:
|
||||
text = const Text('Rab', style: style);
|
||||
break;
|
||||
case 3:
|
||||
text = const Text('Kam', style: style);
|
||||
break;
|
||||
case 4:
|
||||
text = const Text('Jum', style: style);
|
||||
break;
|
||||
case 5:
|
||||
text = const Text('Sab', style: style);
|
||||
break;
|
||||
case 6:
|
||||
text = const Text('Min', style: style);
|
||||
break;
|
||||
default:
|
||||
text = const Text('', style: style);
|
||||
break;
|
||||
}
|
||||
return SideTitleWidget(meta: meta, child: text);
|
||||
},
|
||||
),
|
||||
),
|
||||
leftTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: true,
|
||||
interval: 10000000, // 10M interval
|
||||
reservedSize: 42,
|
||||
getTitlesWidget: (double value, TitleMeta meta) {
|
||||
return Text(
|
||||
'${(value / 1000000).toInt()}M',
|
||||
style: const TextStyle(
|
||||
color: AppColor.textSecondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 10,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
borderData: FlBorderData(
|
||||
show: true,
|
||||
border: Border.all(color: AppColor.borderLight),
|
||||
),
|
||||
minX: 0,
|
||||
maxX: 6,
|
||||
minY: -5000000,
|
||||
maxY: 30000000,
|
||||
lineBarsData: [
|
||||
// Cash In Line
|
||||
LineChartBarData(
|
||||
spots: const [
|
||||
FlSpot(0, 25000000), // Monday
|
||||
FlSpot(1, 22000000), // Tuesday
|
||||
FlSpot(2, 28000000), // Wednesday
|
||||
FlSpot(3, 24000000), // Thursday
|
||||
FlSpot(4, 30000000), // Friday
|
||||
FlSpot(5, 18000000), // Saturday
|
||||
FlSpot(6, 26000000), // Sunday
|
||||
],
|
||||
isCurved: true,
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
AppColor.success.withOpacity(0.8),
|
||||
AppColor.success,
|
||||
],
|
||||
),
|
||||
barWidth: 3,
|
||||
isStrokeCapRound: true,
|
||||
dotData: FlDotData(
|
||||
show: true,
|
||||
getDotPainter: (spot, percent, barData, index) {
|
||||
return FlDotCirclePainter(
|
||||
radius: 4,
|
||||
color: AppColor.success,
|
||||
strokeWidth: 2,
|
||||
strokeColor: AppColor.white,
|
||||
);
|
||||
},
|
||||
),
|
||||
belowBarData: BarAreaData(
|
||||
show: true,
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
AppColor.success.withOpacity(0.1),
|
||||
AppColor.success.withOpacity(0.0),
|
||||
],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
),
|
||||
),
|
||||
),
|
||||
// Cash Out Line
|
||||
LineChartBarData(
|
||||
spots: const [
|
||||
FlSpot(0, 20000000), // Monday
|
||||
FlSpot(1, 18000000), // Tuesday
|
||||
FlSpot(2, 23000000), // Wednesday
|
||||
FlSpot(3, 19000000), // Thursday
|
||||
FlSpot(4, 25000000), // Friday
|
||||
FlSpot(5, 15000000), // Saturday
|
||||
FlSpot(6, 21000000), // Sunday
|
||||
],
|
||||
isCurved: true,
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
AppColor.error.withOpacity(0.8),
|
||||
AppColor.error,
|
||||
],
|
||||
),
|
||||
barWidth: 3,
|
||||
isStrokeCapRound: true,
|
||||
dotData: FlDotData(
|
||||
show: true,
|
||||
getDotPainter: (spot, percent, barData, index) {
|
||||
return FlDotCirclePainter(
|
||||
radius: 4,
|
||||
color: AppColor.error,
|
||||
strokeWidth: 2,
|
||||
strokeColor: AppColor.white,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
// Net Flow Line
|
||||
LineChartBarData(
|
||||
spots: const [
|
||||
FlSpot(0, 5000000), // Monday
|
||||
FlSpot(1, 4000000), // Tuesday
|
||||
FlSpot(2, 5000000), // Wednesday
|
||||
FlSpot(3, 5000000), // Thursday
|
||||
FlSpot(4, 5000000), // Friday
|
||||
FlSpot(5, 3000000), // Saturday
|
||||
FlSpot(6, 5000000), // Sunday
|
||||
],
|
||||
isCurved: true,
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
AppColor.info.withOpacity(0.8),
|
||||
AppColor.info,
|
||||
],
|
||||
),
|
||||
barWidth: 3,
|
||||
isStrokeCapRound: true,
|
||||
dotData: FlDotData(
|
||||
show: true,
|
||||
getDotPainter: (spot, percent, barData, index) {
|
||||
return FlDotCirclePainter(
|
||||
radius: 4,
|
||||
color: AppColor.info,
|
||||
strokeWidth: 2,
|
||||
strokeColor: AppColor.white,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: dailyData.isEmpty
|
||||
? _buildEmptyChart()
|
||||
: LineChart(_buildLineChartData()),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
// Legend
|
||||
@ -340,6 +151,273 @@ class FinanceCashFlow extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
LineChartData _buildLineChartData() {
|
||||
final maxValue = _getMaxChartValue();
|
||||
final minValue = _getMinChartValue();
|
||||
|
||||
return LineChartData(
|
||||
gridData: FlGridData(
|
||||
show: true,
|
||||
drawVerticalLine: false,
|
||||
horizontalInterval: (maxValue / 5).roundToDouble(),
|
||||
getDrawingHorizontalLine: (value) {
|
||||
return FlLine(color: AppColor.borderLight, strokeWidth: 1);
|
||||
},
|
||||
),
|
||||
titlesData: FlTitlesData(
|
||||
show: true,
|
||||
rightTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
|
||||
topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
|
||||
bottomTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: true,
|
||||
reservedSize: 30,
|
||||
interval: 1,
|
||||
getTitlesWidget: (double value, TitleMeta meta) {
|
||||
final index = value.toInt();
|
||||
if (index >= 0 && index < dailyData.length) {
|
||||
final date = DateTime.parse(dailyData[index].date);
|
||||
final dayName = _getDayName(date.weekday);
|
||||
return SideTitleWidget(
|
||||
meta: meta,
|
||||
child: Text(
|
||||
dayName,
|
||||
style: const TextStyle(
|
||||
color: AppColor.textSecondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 10,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return SideTitleWidget(meta: meta, child: Text(''));
|
||||
},
|
||||
),
|
||||
),
|
||||
leftTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: true,
|
||||
interval: (maxValue / 3).roundToDouble(),
|
||||
reservedSize: 42,
|
||||
getTitlesWidget: (double value, TitleMeta meta) {
|
||||
return Text(
|
||||
_formatChartValue(value),
|
||||
style: const TextStyle(
|
||||
color: AppColor.textSecondary,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 10,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
borderData: FlBorderData(
|
||||
show: true,
|
||||
border: Border.all(color: AppColor.borderLight),
|
||||
),
|
||||
minX: 0,
|
||||
maxX: (dailyData.length - 1).toDouble(),
|
||||
minY: minValue,
|
||||
maxY: maxValue,
|
||||
lineBarsData: [
|
||||
// Cash In Line (Revenue)
|
||||
LineChartBarData(
|
||||
spots: _buildCashInSpots(),
|
||||
isCurved: true,
|
||||
gradient: LinearGradient(
|
||||
colors: [AppColor.success.withOpacity(0.8), AppColor.success],
|
||||
),
|
||||
barWidth: 3,
|
||||
isStrokeCapRound: true,
|
||||
dotData: FlDotData(
|
||||
show: true,
|
||||
getDotPainter: (spot, percent, barData, index) {
|
||||
return FlDotCirclePainter(
|
||||
radius: 4,
|
||||
color: AppColor.success,
|
||||
strokeWidth: 2,
|
||||
strokeColor: AppColor.white,
|
||||
);
|
||||
},
|
||||
),
|
||||
belowBarData: BarAreaData(
|
||||
show: true,
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
AppColor.success.withOpacity(0.1),
|
||||
AppColor.success.withOpacity(0.0),
|
||||
],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
),
|
||||
),
|
||||
),
|
||||
// Cash Out Line (Total Cost)
|
||||
LineChartBarData(
|
||||
spots: _buildCashOutSpots(),
|
||||
isCurved: true,
|
||||
gradient: LinearGradient(
|
||||
colors: [AppColor.error.withOpacity(0.8), AppColor.error],
|
||||
),
|
||||
barWidth: 3,
|
||||
isStrokeCapRound: true,
|
||||
dotData: FlDotData(
|
||||
show: true,
|
||||
getDotPainter: (spot, percent, barData, index) {
|
||||
return FlDotCirclePainter(
|
||||
radius: 4,
|
||||
color: AppColor.error,
|
||||
strokeWidth: 2,
|
||||
strokeColor: AppColor.white,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
// Net Flow Line (Net Profit)
|
||||
LineChartBarData(
|
||||
spots: _buildNetFlowSpots(),
|
||||
isCurved: true,
|
||||
gradient: LinearGradient(
|
||||
colors: [AppColor.info.withOpacity(0.8), AppColor.info],
|
||||
),
|
||||
barWidth: 3,
|
||||
isStrokeCapRound: true,
|
||||
dotData: FlDotData(
|
||||
show: true,
|
||||
getDotPainter: (spot, percent, barData, index) {
|
||||
return FlDotCirclePainter(
|
||||
radius: 4,
|
||||
color: AppColor.info,
|
||||
strokeWidth: 2,
|
||||
strokeColor: AppColor.white,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEmptyChart() {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
LineIcons.lineChart,
|
||||
size: 48,
|
||||
color: AppColor.textSecondary.withOpacity(0.3),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'Tidak ada data untuk ditampilkan',
|
||||
style: AppStyle.sm.copyWith(color: AppColor.textSecondary),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Helper methods for calculating data
|
||||
int _calculateTotalCashIn() {
|
||||
return dailyData.fold(0, (sum, data) => sum + data.revenue);
|
||||
}
|
||||
|
||||
int _calculateTotalCashOut() {
|
||||
return dailyData.fold(
|
||||
0,
|
||||
(sum, data) => sum + data.cost + data.tax + data.discount,
|
||||
);
|
||||
}
|
||||
|
||||
double _getMaxChartValue() {
|
||||
if (dailyData.isEmpty) return 30000000;
|
||||
|
||||
final maxRevenue = dailyData
|
||||
.map((e) => e.revenue)
|
||||
.reduce((a, b) => a > b ? a : b);
|
||||
final maxCost = dailyData
|
||||
.map((e) => e.cost + e.tax + e.discount)
|
||||
.reduce((a, b) => a > b ? a : b);
|
||||
final maxValue = maxRevenue > maxCost ? maxRevenue : maxCost;
|
||||
|
||||
return (maxValue * 1.2).toDouble(); // Add 20% padding
|
||||
}
|
||||
|
||||
double _getMinChartValue() {
|
||||
if (dailyData.isEmpty) return -5000000;
|
||||
|
||||
final minNetProfit = dailyData
|
||||
.map((e) => e.netProfit)
|
||||
.reduce((a, b) => a < b ? a : b);
|
||||
return minNetProfit < 0 ? (minNetProfit * 1.2).toDouble() : 0;
|
||||
}
|
||||
|
||||
List<FlSpot> _buildCashInSpots() {
|
||||
return dailyData.asMap().entries.map((entry) {
|
||||
return FlSpot(entry.key.toDouble(), entry.value.revenue.toDouble());
|
||||
}).toList();
|
||||
}
|
||||
|
||||
List<FlSpot> _buildCashOutSpots() {
|
||||
return dailyData.asMap().entries.map((entry) {
|
||||
final totalCost =
|
||||
entry.value.cost + entry.value.tax + entry.value.discount;
|
||||
return FlSpot(entry.key.toDouble(), totalCost.toDouble());
|
||||
}).toList();
|
||||
}
|
||||
|
||||
List<FlSpot> _buildNetFlowSpots() {
|
||||
return dailyData.asMap().entries.map((entry) {
|
||||
return FlSpot(entry.key.toDouble(), entry.value.netProfit.toDouble());
|
||||
}).toList();
|
||||
}
|
||||
|
||||
String _getDayName(int weekday) {
|
||||
switch (weekday) {
|
||||
case 1:
|
||||
return 'Sen';
|
||||
case 2:
|
||||
return 'Sel';
|
||||
case 3:
|
||||
return 'Rab';
|
||||
case 4:
|
||||
return 'Kam';
|
||||
case 5:
|
||||
return 'Jum';
|
||||
case 6:
|
||||
return 'Sab';
|
||||
case 7:
|
||||
return 'Min';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
String _formatChartValue(double value) {
|
||||
if (value.abs() >= 1000000) {
|
||||
return '${(value / 1000000).toStringAsFixed(0)}M';
|
||||
} else if (value.abs() >= 1000) {
|
||||
return '${(value / 1000).toStringAsFixed(0)}K';
|
||||
} else {
|
||||
return value.toStringAsFixed(0);
|
||||
}
|
||||
}
|
||||
|
||||
String _formatCurrency(int amount) {
|
||||
if (amount.abs() >= 1000000000) {
|
||||
return 'Rp ${(amount / 1000000000).toStringAsFixed(1)}B';
|
||||
} else if (amount.abs() >= 1000000) {
|
||||
return 'Rp ${(amount / 1000000).toStringAsFixed(1)}M';
|
||||
} else if (amount.abs() >= 1000) {
|
||||
return 'Rp ${(amount / 1000).toStringAsFixed(1)}K';
|
||||
} else {
|
||||
return 'Rp ${NumberFormat('#,###', 'id_ID').format(amount)}';
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildChartLegend(String label, Color color) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
||||
@ -1,33 +1,21 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:line_icons/line_icons.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../../../common/extension/extension.dart';
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../../domain/analytic/analytic.dart';
|
||||
import '../../../components/widgets/empty_widget.dart';
|
||||
|
||||
class FinanceCategory extends StatelessWidget {
|
||||
const FinanceCategory({super.key});
|
||||
final List<CategoryAnalyticItem> categories;
|
||||
|
||||
const FinanceCategory({super.key, required this.categories});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final categories = [
|
||||
{
|
||||
'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,
|
||||
},
|
||||
];
|
||||
final totalRevenue = _calculateTotalRevenue();
|
||||
final sortedCategories = _sortCategoriesByRevenue();
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.all(16),
|
||||
@ -70,25 +58,25 @@ class FinanceCategory extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
...categories
|
||||
.map(
|
||||
(category) => _buildCategoryItem(
|
||||
category['name'] as String,
|
||||
category['amount'] as String,
|
||||
category['percentage'] as int,
|
||||
category['color'] as Color,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
// Show empty state if no categories
|
||||
if (categories.isEmpty)
|
||||
_buildEmptyState()
|
||||
else
|
||||
...sortedCategories.asMap().entries.map(
|
||||
(entry) => _buildCategoryItem(
|
||||
entry.value,
|
||||
_calculatePercentage(entry.value.totalRevenue, totalRevenue),
|
||||
_getCategoryColor(entry.key),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCategoryItem(
|
||||
String name,
|
||||
String amount,
|
||||
int percentage,
|
||||
CategoryAnalyticItem category,
|
||||
double percentage,
|
||||
Color color,
|
||||
) {
|
||||
return Container(
|
||||
@ -98,30 +86,59 @@ class FinanceCategory extends StatelessWidget {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
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: [
|
||||
Container(
|
||||
width: 12,
|
||||
height: 12,
|
||||
decoration: BoxDecoration(
|
||||
Text(
|
||||
category.totalRevenue.currencyFormatRp,
|
||||
style: AppStyle.md.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: color,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
name,
|
||||
style: AppStyle.md.copyWith(fontWeight: FontWeight.w600),
|
||||
'${NumberFormat('#,###', 'id_ID').format(category.totalQuantity)} unit',
|
||||
style: AppStyle.xs.copyWith(color: AppColor.textSecondary),
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
amount,
|
||||
style: AppStyle.md.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@ -135,7 +152,7 @@ class FinanceCategory extends StatelessWidget {
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Text(
|
||||
'$percentage%',
|
||||
'${percentage.toStringAsFixed(1)}%',
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
148
lib/presentation/pages/finance/widgets/product.dart
Normal file
148
lib/presentation/pages/finance/widgets/product.dart
Normal file
@ -0,0 +1,148 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../common/extension/extension.dart';
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../../domain/analytic/analytic.dart';
|
||||
|
||||
class ProfitLossProduct extends StatelessWidget {
|
||||
final ProfitLossProductData product;
|
||||
const ProfitLossProduct({super.key, required this.product});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.background,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: AppColor.border.withOpacity(0.5)),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Product header
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
product.productName,
|
||||
style: AppStyle.md.copyWith(fontWeight: FontWeight.bold),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 2,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.primary.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Text(
|
||||
product.categoryName,
|
||||
style: AppStyle.xs.copyWith(color: AppColor.primary),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
'${product.quantitySold} terjual',
|
||||
style: AppStyle.sm.copyWith(color: AppColor.textSecondary),
|
||||
),
|
||||
Text(
|
||||
'${product.grossProfitMargin.toStringAsFixed(1)}%',
|
||||
style: AppStyle.md.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: product.grossProfitMargin > 25
|
||||
? AppColor.success
|
||||
: product.grossProfitMargin > 15
|
||||
? AppColor.warning
|
||||
: AppColor.error,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Financial metrics
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildMetricColumn(
|
||||
'Pendapatan',
|
||||
product.revenue.currencyFormatRp,
|
||||
AppColor.success,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: _buildMetricColumn(
|
||||
'Biaya',
|
||||
product.cost.currencyFormatRp,
|
||||
AppColor.error,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: _buildMetricColumn(
|
||||
'Laba Kotor',
|
||||
product.grossProfit.currencyFormatRp,
|
||||
AppColor.info,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// Average metrics
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildMetricColumn(
|
||||
'Harga Rata-rata',
|
||||
product.averagePrice.currencyFormatRp,
|
||||
AppColor.textSecondary,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: _buildMetricColumn(
|
||||
'Laba per Unit',
|
||||
product.profitPerUnit.currencyFormatRp,
|
||||
AppColor.primary,
|
||||
),
|
||||
),
|
||||
const Expanded(child: SizedBox()),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMetricColumn(String label, String value, Color color) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(label, style: AppStyle.xs.copyWith(color: AppColor.textSecondary)),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
value,
|
||||
style: AppStyle.sm.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:line_icons/line_icons.dart';
|
||||
|
||||
import '../../../../common/extension/extension.dart';
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../../domain/analytic/analytic.dart';
|
||||
|
||||
class FinanceProfitLoss extends StatelessWidget {
|
||||
const FinanceProfitLoss({super.key});
|
||||
final ProfitLossSummary data;
|
||||
|
||||
const FinanceProfitLoss({super.key, required this.data});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -49,52 +53,77 @@ class FinanceProfitLoss extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Total Revenue (Penjualan Kotor)
|
||||
_buildPLItem(
|
||||
'Penjualan Kotor',
|
||||
'Rp 25.840.000',
|
||||
data.totalRevenue.currencyFormatRp,
|
||||
AppColor.success,
|
||||
true,
|
||||
),
|
||||
_buildPLItem('Diskon & Retur', '- Rp 560.000', AppColor.error, false),
|
||||
|
||||
// Discount (Diskon & Retur)
|
||||
_buildPLItem(
|
||||
'Diskon & Retur',
|
||||
'- ${data.totalDiscount.currencyFormatRp}',
|
||||
AppColor.error,
|
||||
false,
|
||||
),
|
||||
|
||||
const Divider(height: 24),
|
||||
|
||||
// Net Sales (Penjualan Bersih = Total Revenue - Discount)
|
||||
_buildPLItem(
|
||||
'Penjualan Bersih',
|
||||
'Rp 25.280.000',
|
||||
(data.totalRevenue - data.totalDiscount).currencyFormatRp,
|
||||
AppColor.textPrimary,
|
||||
true,
|
||||
isHeader: true,
|
||||
),
|
||||
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// Cost of Goods Sold (HPP)
|
||||
_buildPLItem(
|
||||
'HPP (Harga Pokok Penjualan)',
|
||||
'- Rp 15.120.000',
|
||||
'- ${data.totalCost.currencyFormatRp}',
|
||||
AppColor.error,
|
||||
false,
|
||||
),
|
||||
|
||||
const Divider(height: 24),
|
||||
|
||||
// Gross Profit (Laba Kotor)
|
||||
_buildPLItem(
|
||||
'Laba Kotor',
|
||||
'Rp 10.160.000',
|
||||
data.grossProfit.currencyFormatRp,
|
||||
AppColor.success,
|
||||
true,
|
||||
isHeader: true,
|
||||
showPercentage: true,
|
||||
percentage: '${data.grossProfitMargin.toStringAsFixed(1)}%',
|
||||
),
|
||||
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// Operational Cost (Biaya Operasional) - calculated as difference
|
||||
_buildPLItem(
|
||||
'Biaya Operasional',
|
||||
'- Rp 2.640.000',
|
||||
'- ${_calculateOperationalCost().currencyFormatRp}',
|
||||
AppColor.error,
|
||||
false,
|
||||
),
|
||||
|
||||
const Divider(height: 24),
|
||||
|
||||
// Net Profit (Laba Bersih)
|
||||
_buildPLItem(
|
||||
'Laba Bersih',
|
||||
'Rp 7.520.000',
|
||||
data.netProfit.currencyFormatRp,
|
||||
AppColor.primary,
|
||||
true,
|
||||
isHeader: true,
|
||||
showPercentage: true,
|
||||
percentage: '29.8%',
|
||||
percentage: '${data.netProfitMargin.round()}%',
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -155,4 +184,9 @@ class FinanceProfitLoss extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Calculate operational cost as the difference between gross profit and net profit
|
||||
int _calculateOperationalCost() {
|
||||
return data.grossProfit - data.netProfit - data.totalTax;
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,6 @@ class FinanceSummaryCard extends StatelessWidget {
|
||||
required this.amount,
|
||||
required this.icon,
|
||||
required this.color,
|
||||
required this.change,
|
||||
required this.isPositive,
|
||||
});
|
||||
|
||||
@ -17,7 +16,6 @@ class FinanceSummaryCard extends StatelessWidget {
|
||||
final String amount;
|
||||
final IconData icon;
|
||||
final Color color;
|
||||
final String change;
|
||||
final bool isPositive;
|
||||
|
||||
@override
|
||||
@ -50,22 +48,6 @@ class FinanceSummaryCard extends StatelessWidget {
|
||||
),
|
||||
child: Icon(icon, color: color, size: 20),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: isPositive
|
||||
? AppColor.success.withOpacity(0.1)
|
||||
: AppColor.error.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Text(
|
||||
change,
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: isPositive ? AppColor.success : AppColor.error,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import '../../../../application/auth/auth_bloc.dart';
|
||||
import '../../../../common/constant/app_constant.dart';
|
||||
import '../../../../common/extension/extension.dart';
|
||||
import '../../../../common/painter/wave_painter.dart';
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../../domain/auth/auth.dart';
|
||||
import '../../../components/spacer/spacer.dart';
|
||||
|
||||
class HomeHeader extends StatefulWidget {
|
||||
@ -102,43 +106,47 @@ class _HomeHeaderState extends State<HomeHeader> with TickerProviderStateMixin {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedBuilder(
|
||||
animation: Listenable.merge([
|
||||
_particleController,
|
||||
_waveController,
|
||||
_breathController,
|
||||
]),
|
||||
builder: (context, child) {
|
||||
return Container(
|
||||
height: 280,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
AppColor.primary,
|
||||
AppColor.primaryLight,
|
||||
AppColor.primaryLight.withOpacity(0.8),
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
stops: const [0.0, 0.7, 1.0],
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColor.primary.withOpacity(0.3),
|
||||
blurRadius: 20,
|
||||
offset: const Offset(0, 10),
|
||||
return BlocBuilder<AuthBloc, AuthState>(
|
||||
builder: (context, state) {
|
||||
return AnimatedBuilder(
|
||||
animation: Listenable.merge([
|
||||
_particleController,
|
||||
_waveController,
|
||||
_breathController,
|
||||
]),
|
||||
builder: (context, child) {
|
||||
return Container(
|
||||
height: 280,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
AppColor.primary,
|
||||
AppColor.primaryLight,
|
||||
AppColor.primaryLight.withOpacity(0.8),
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
stops: const [0.0, 0.7, 1.0],
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColor.primary.withOpacity(0.3),
|
||||
blurRadius: 20,
|
||||
offset: const Offset(0, 10),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
// Enhanced animated background
|
||||
_buildAnimatedBackground(),
|
||||
child: Stack(
|
||||
children: [
|
||||
// Enhanced animated background
|
||||
_buildAnimatedBackground(),
|
||||
|
||||
// Main content
|
||||
SafeArea(child: _buildContent(context)),
|
||||
],
|
||||
),
|
||||
// Main content
|
||||
SafeArea(child: _buildContent(context, state.user)),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -255,7 +263,7 @@ class _HomeHeaderState extends State<HomeHeader> with TickerProviderStateMixin {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContent(BuildContext context) {
|
||||
Widget _buildContent(BuildContext context, User user) {
|
||||
String greeting(BuildContext context) {
|
||||
final hour = DateTime.now().hour;
|
||||
|
||||
@ -291,7 +299,7 @@ class _HomeHeaderState extends State<HomeHeader> with TickerProviderStateMixin {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'AppSkel POS Owner',
|
||||
AppConstant.appName,
|
||||
style: AppStyle.lg.copyWith(
|
||||
color: AppColor.white.withOpacity(0.9),
|
||||
fontWeight: FontWeight.w600,
|
||||
@ -307,7 +315,7 @@ class _HomeHeaderState extends State<HomeHeader> with TickerProviderStateMixin {
|
||||
),
|
||||
const SpaceHeight(2),
|
||||
Text(
|
||||
'Manager',
|
||||
user.role.toTitleCase,
|
||||
style: AppStyle.sm.copyWith(
|
||||
color: AppColor.white.withOpacity(0.7),
|
||||
fontSize: 11,
|
||||
@ -383,7 +391,7 @@ class _HomeHeaderState extends State<HomeHeader> with TickerProviderStateMixin {
|
||||
),
|
||||
const SpaceHeight(2),
|
||||
Text(
|
||||
'Vira Vania! đź‘‹',
|
||||
'${user.name}! đź‘‹',
|
||||
style: AppStyle.h4.copyWith(
|
||||
color: AppColor.white,
|
||||
fontWeight: FontWeight.w800,
|
||||
|
||||
@ -1,152 +1,96 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:line_icons/line_icons.dart';
|
||||
import 'package:shimmer/shimmer.dart';
|
||||
|
||||
import '../../../application/category/category_loader/category_loader_bloc.dart';
|
||||
import '../../../application/product/product_loader/product_loader_bloc.dart';
|
||||
import '../../../common/theme/theme.dart';
|
||||
import '../../../domain/category/category.dart';
|
||||
import '../../../domain/product/product.dart';
|
||||
import '../../../injection.dart';
|
||||
import '../../components/appbar/appbar.dart';
|
||||
import '../../components/button/button.dart';
|
||||
import '../../components/widgets/empty_widget.dart';
|
||||
import 'widgets/category_delegate.dart';
|
||||
import 'widgets/product_card.dart';
|
||||
import 'widgets/product_tile.dart';
|
||||
|
||||
@RoutePage()
|
||||
class ProductPage extends StatefulWidget {
|
||||
class ProductPage extends StatefulWidget implements AutoRouteWrapper {
|
||||
const ProductPage({super.key});
|
||||
|
||||
@override
|
||||
State<ProductPage> createState() => _ProductPageState();
|
||||
|
||||
@override
|
||||
Widget wrappedRoute(BuildContext context) => MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
create: (context) =>
|
||||
getIt<CategoryLoaderBloc>()..add(CategoryLoaderEvent.fetched()),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (context) =>
|
||||
getIt<ProductLoaderBloc>()
|
||||
..add(ProductLoaderEvent.fetched(isRefresh: true)),
|
||||
),
|
||||
],
|
||||
child: this,
|
||||
);
|
||||
}
|
||||
|
||||
enum ViewType { grid, list }
|
||||
|
||||
class _ProductPageState extends State<ProductPage>
|
||||
with TickerProviderStateMixin {
|
||||
String selectedCategory = 'Semua';
|
||||
List<String> categories = ['Semua', 'Makanan', 'Minuman', 'Snack', 'Dessert'];
|
||||
Category selectedCategory = Category.addAllData();
|
||||
ViewType currentViewType = ViewType.grid;
|
||||
|
||||
// Sample product data
|
||||
List<Product> products = [
|
||||
Product(
|
||||
id: '1',
|
||||
name: 'Nasi Goreng Special',
|
||||
price: 25000,
|
||||
category: 'Makanan',
|
||||
stock: 50,
|
||||
imageUrl: 'assets/images/nasi_goreng.jpg',
|
||||
isActive: true,
|
||||
),
|
||||
Product(
|
||||
id: '8',
|
||||
name: 'Nasi Goreng',
|
||||
price: 15000,
|
||||
category: 'Makanan',
|
||||
stock: 50,
|
||||
imageUrl: 'assets/images/nasi_goreng.jpg',
|
||||
isActive: true,
|
||||
),
|
||||
Product(
|
||||
id: '9',
|
||||
name: 'Nasi Goreng Telor',
|
||||
price: 18000,
|
||||
category: 'Makanan',
|
||||
stock: 50,
|
||||
imageUrl: 'assets/images/nasi_goreng.jpg',
|
||||
isActive: true,
|
||||
),
|
||||
Product(
|
||||
id: '10',
|
||||
name: 'Mie Goreng ',
|
||||
price: 18000,
|
||||
category: 'Makanan',
|
||||
stock: 50,
|
||||
imageUrl: 'assets/images/nasi_goreng.jpg',
|
||||
isActive: true,
|
||||
),
|
||||
Product(
|
||||
id: '2',
|
||||
name: 'Es Teh Manis',
|
||||
price: 8000,
|
||||
category: 'Minuman',
|
||||
stock: 100,
|
||||
imageUrl: 'assets/images/es_teh.jpg',
|
||||
isActive: true,
|
||||
),
|
||||
Product(
|
||||
id: '6',
|
||||
name: 'Es Jeruk',
|
||||
price: 10000,
|
||||
category: 'Minuman',
|
||||
stock: 100,
|
||||
imageUrl: 'assets/images/es_teh.jpg',
|
||||
isActive: true,
|
||||
),
|
||||
Product(
|
||||
id: '7',
|
||||
name: 'Es Kelapa',
|
||||
price: 12000,
|
||||
category: 'Minuman',
|
||||
stock: 100,
|
||||
imageUrl: 'assets/images/es_teh.jpg',
|
||||
isActive: true,
|
||||
),
|
||||
Product(
|
||||
id: '3',
|
||||
name: 'Keripik Singkong',
|
||||
price: 15000,
|
||||
category: 'Snack',
|
||||
stock: 25,
|
||||
imageUrl: 'assets/images/keripik.jpg',
|
||||
isActive: true,
|
||||
),
|
||||
Product(
|
||||
id: '4',
|
||||
name: 'Es Krim Vanilla',
|
||||
price: 12000,
|
||||
category: 'Dessert',
|
||||
stock: 30,
|
||||
imageUrl: 'assets/images/ice_cream.jpg',
|
||||
isActive: false,
|
||||
),
|
||||
Product(
|
||||
id: '5',
|
||||
name: 'Ayam Bakar',
|
||||
price: 35000,
|
||||
category: 'Makanan',
|
||||
stock: 20,
|
||||
imageUrl: 'assets/images/ayam_bakar.jpg',
|
||||
isActive: true,
|
||||
),
|
||||
];
|
||||
|
||||
List<Product> get filteredProducts {
|
||||
return products.where((product) {
|
||||
bool matchesCategory =
|
||||
selectedCategory == 'Semua' || product.category == selectedCategory;
|
||||
return matchesCategory;
|
||||
}).toList();
|
||||
}
|
||||
ScrollController scrollController = ScrollController();
|
||||
|
||||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColor.background,
|
||||
body: CustomScrollView(
|
||||
slivers: [
|
||||
_buildSliverAppBar(),
|
||||
_buildCategoryFilter(),
|
||||
_buildProductContent(),
|
||||
_buildEmptyState(),
|
||||
],
|
||||
return BlocListener<ProductLoaderBloc, ProductLoaderState>(
|
||||
listenWhen: (previous, current) =>
|
||||
previous.categoryId != current.categoryId,
|
||||
listener: (context, state) {
|
||||
context.read<ProductLoaderBloc>().add(
|
||||
ProductLoaderEvent.fetched(isRefresh: true),
|
||||
);
|
||||
},
|
||||
child: BlocBuilder<ProductLoaderBloc, ProductLoaderState>(
|
||||
builder: (context, state) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColor.background,
|
||||
body: NotificationListener<ScrollNotification>(
|
||||
onNotification: (notification) {
|
||||
if (notification is ScrollEndNotification &&
|
||||
scrollController.position.extentAfter == 0) {
|
||||
context.read<ProductLoaderBloc>().add(
|
||||
ProductLoaderEvent.fetched(),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
child: CustomScrollView(
|
||||
controller: scrollController,
|
||||
slivers: [
|
||||
_buildSliverAppBar(),
|
||||
_buildCategoryFilter(),
|
||||
_buildProductContent(state),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -160,44 +104,97 @@ class _ProductPageState extends State<ProductPage>
|
||||
flexibleSpace: CustomAppBar(title: 'Produk'),
|
||||
actions: [
|
||||
ActionIconButton(onTap: () {}, icon: LineIcons.search),
|
||||
ActionIconButton(onTap: _showAddProductDialog, icon: LineIcons.plus),
|
||||
ActionIconButton(
|
||||
onTap: _toggleViewType,
|
||||
icon: currentViewType == ViewType.grid
|
||||
? LineIcons.list
|
||||
: LineIcons.thLarge,
|
||||
),
|
||||
ActionIconButton(onTap: _showOptionsMenu, icon: LineIcons.filter),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCategoryFilter() {
|
||||
return SliverPersistentHeader(
|
||||
pinned: true,
|
||||
delegate: ProductCategoryHeaderDelegate(
|
||||
categories: categories,
|
||||
selectedCategory: selectedCategory,
|
||||
onCategoryChanged: (category) {
|
||||
setState(() {
|
||||
selectedCategory = category;
|
||||
});
|
||||
},
|
||||
return BlocBuilder<CategoryLoaderBloc, CategoryLoaderState>(
|
||||
builder: (context, state) {
|
||||
if (state.isFetching && state.categories.isEmpty) {
|
||||
return _buildCategoryShimmer();
|
||||
}
|
||||
|
||||
return SliverPersistentHeader(
|
||||
pinned: true,
|
||||
delegate: ProductCategoryHeaderDelegate(
|
||||
categories: state.categories,
|
||||
selectedCategory: selectedCategory,
|
||||
onCategoryChanged: (category) {
|
||||
setState(() {
|
||||
selectedCategory = category;
|
||||
});
|
||||
if (category.id == Category.addAllData().id) {
|
||||
context.read<ProductLoaderBloc>().add(
|
||||
ProductLoaderEvent.categoryIdChanged(''),
|
||||
);
|
||||
} else {
|
||||
context.read<ProductLoaderBloc>().add(
|
||||
ProductLoaderEvent.categoryIdChanged(category.id),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCategoryShimmer() {
|
||||
return SliverToBoxAdapter(
|
||||
child: Container(
|
||||
height: 60,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Shimmer.fromColors(
|
||||
baseColor: Colors.grey[300]!,
|
||||
highlightColor: Colors.grey[100]!,
|
||||
child: Row(
|
||||
children: List.generate(
|
||||
4,
|
||||
(index) => Container(
|
||||
margin: EdgeInsets.only(right: index < 3 ? 12 : 0),
|
||||
width: 80,
|
||||
height: 35,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildProductContent() {
|
||||
if (filteredProducts.isEmpty) {
|
||||
return const SliverToBoxAdapter(child: SizedBox.shrink());
|
||||
Widget _buildProductContent(ProductLoaderState state) {
|
||||
if (state.isFetching && state.products.isEmpty) {
|
||||
return currentViewType == ViewType.grid
|
||||
? _buildProductGridShimmer()
|
||||
: _buildProductListShimmer();
|
||||
}
|
||||
|
||||
if (state.products.isEmpty && !state.isFetching) {
|
||||
return SliverToBoxAdapter(
|
||||
child: EmptyWidget(
|
||||
title: 'Tidak ada produk ditemukan',
|
||||
message: 'Coba ubah filter atau tambah produk baru',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return currentViewType == ViewType.grid
|
||||
? _buildProductGrid()
|
||||
: _buildProductList();
|
||||
? _buildProductGrid(state.products)
|
||||
: _buildProductList(state.products);
|
||||
}
|
||||
|
||||
Widget _buildProductGrid() {
|
||||
Widget _buildProductGridShimmer() {
|
||||
return SliverPadding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
sliver: SliverGrid(
|
||||
@ -208,26 +205,74 @@ class _ProductPageState extends State<ProductPage>
|
||||
mainAxisSpacing: 16.0,
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
final product = filteredProducts[index];
|
||||
return ProductTile(product: product, onTap: () {});
|
||||
}, childCount: filteredProducts.length),
|
||||
return _buildProductTileShimmer();
|
||||
}, childCount: 6), // Show 6 shimmer items
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildProductList() {
|
||||
Widget _buildProductTileShimmer() {
|
||||
return Shimmer.fromColors(
|
||||
baseColor: Colors.grey[300]!,
|
||||
highlightColor: Colors.grey[100]!,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Image shimmer
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
),
|
||||
// Content shimmer
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 16,
|
||||
color: Colors.white,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Container(width: 100, height: 12, color: Colors.white),
|
||||
const Spacer(),
|
||||
Container(width: 80, height: 14, color: Colors.white),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildProductListShimmer() {
|
||||
return SliverPadding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
final product = filteredProducts[index];
|
||||
return _buildProductListItem(product);
|
||||
}, childCount: filteredProducts.length),
|
||||
return _buildProductListItemShimmer();
|
||||
}, childCount: 8), // Show 8 shimmer items
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildProductListItem(Product product) {
|
||||
Widget _buildProductListItemShimmer() {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 12.0),
|
||||
decoration: BoxDecoration(
|
||||
@ -242,104 +287,56 @@ class _ProductPageState extends State<ProductPage>
|
||||
),
|
||||
],
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: () {},
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Shimmer.fromColors(
|
||||
baseColor: Colors.grey[300]!,
|
||||
highlightColor: Colors.grey[100]!,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Row(
|
||||
children: [
|
||||
// Product Image
|
||||
// Image shimmer
|
||||
Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: AppColor.background,
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Image.asset(
|
||||
product.imageUrl,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return Container(
|
||||
color: AppColor.background,
|
||||
child: Icon(
|
||||
Icons.image_outlined,
|
||||
color: AppColor.textLight,
|
||||
size: 32,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
// Product Info
|
||||
// Content shimmer
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
product.name,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
product.category,
|
||||
style: TextStyle(fontSize: 12, color: AppColor.textLight),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 16,
|
||||
color: Colors.white,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Container(width: 120, height: 12, color: Colors.white),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Rp ${_formatPrice(product.price)}',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColor.primary,
|
||||
),
|
||||
),
|
||||
Container(width: 100, height: 16, color: Colors.white),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 4,
|
||||
),
|
||||
width: 60,
|
||||
height: 24,
|
||||
decoration: BoxDecoration(
|
||||
color: product.isActive
|
||||
? Colors.green.withOpacity(0.1)
|
||||
: Colors.red.withOpacity(0.1),
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Text(
|
||||
'Stock: ${product.stock}',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: product.isActive
|
||||
? Colors.green
|
||||
: Colors.red,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// Action Button
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.more_vert),
|
||||
color: AppColor.textLight,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
// Action button shimmer
|
||||
Container(width: 24, height: 24, color: Colors.white),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -347,10 +344,33 @@ class _ProductPageState extends State<ProductPage>
|
||||
);
|
||||
}
|
||||
|
||||
String _formatPrice(int price) {
|
||||
return price.toString().replaceAllMapped(
|
||||
RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
|
||||
(Match m) => '${m[1]}.',
|
||||
Widget _buildProductGrid(List<Product> products) {
|
||||
return SliverPadding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
childAspectRatio: 0.75,
|
||||
crossAxisSpacing: 16.0,
|
||||
mainAxisSpacing: 16.0,
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
final product = products[index];
|
||||
return ProductTile(product: product, onTap: () {});
|
||||
}, childCount: products.length),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildProductList(List<Product> products) {
|
||||
return SliverPadding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
final product = products[index];
|
||||
return ProductCard(product: product);
|
||||
}, childCount: products.length),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -361,128 +381,4 @@ class _ProductPageState extends State<ProductPage>
|
||||
: ViewType.grid;
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildEmptyState() {
|
||||
if (filteredProducts.isNotEmpty) {
|
||||
return const SliverToBoxAdapter(child: SizedBox.shrink());
|
||||
}
|
||||
|
||||
return SliverToBoxAdapter(
|
||||
child: Container(
|
||||
height: 300,
|
||||
margin: const EdgeInsets.all(32.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.inventory_2_outlined,
|
||||
size: 64,
|
||||
color: AppColor.textLight,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Tidak ada produk ditemukan',
|
||||
style: TextStyle(
|
||||
color: AppColor.textSecondary,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Coba ubah filter atau tambah produk baru',
|
||||
style: TextStyle(color: AppColor.textLight, fontSize: 14),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showAddProductDialog() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Tambah Produk'),
|
||||
content: const Text(
|
||||
'Dialog tambah produk akan diimplementasikan di sini',
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Batal'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Simpan'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showOptionsMenu() {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (context) => Container(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
leading: Icon(
|
||||
currentViewType == ViewType.grid ? Icons.list : Icons.grid_view,
|
||||
),
|
||||
title: Text(
|
||||
currentViewType == ViewType.grid
|
||||
? 'Tampilan List'
|
||||
: 'Tampilan Grid',
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
_toggleViewType();
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.sort),
|
||||
title: const Text('Urutkan'),
|
||||
onTap: () => Navigator.pop(context),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.filter_list),
|
||||
title: const Text('Filter Lanjutan'),
|
||||
onTap: () => Navigator.pop(context),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.download),
|
||||
title: const Text('Export Data'),
|
||||
onTap: () => Navigator.pop(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Product Model
|
||||
class Product {
|
||||
final String id;
|
||||
final String name;
|
||||
final int price;
|
||||
final String category;
|
||||
final int stock;
|
||||
final String imageUrl;
|
||||
bool isActive;
|
||||
|
||||
Product({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.price,
|
||||
required this.category,
|
||||
required this.stock,
|
||||
required this.imageUrl,
|
||||
required this.isActive,
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../../domain/category/category.dart';
|
||||
|
||||
class ProductCategoryHeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||
final List<String> categories;
|
||||
final String selectedCategory;
|
||||
final ValueChanged<String> onCategoryChanged;
|
||||
final List<Category> categories;
|
||||
final Category selectedCategory;
|
||||
final ValueChanged<Category> onCategoryChanged;
|
||||
|
||||
ProductCategoryHeaderDelegate({
|
||||
required this.categories,
|
||||
@ -35,7 +36,7 @@ class ProductCategoryHeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(right: 12.0),
|
||||
child: FilterChip(
|
||||
label: Text(category),
|
||||
label: Text(category.name),
|
||||
selected: isSelected,
|
||||
onSelected: (selected) => onCategoryChanged(category),
|
||||
backgroundColor: AppColor.surface,
|
||||
@ -64,6 +65,7 @@ class ProductCategoryHeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||
|
||||
@override
|
||||
bool shouldRebuild(ProductCategoryHeaderDelegate oldDelegate) {
|
||||
return oldDelegate.selectedCategory != selectedCategory;
|
||||
return oldDelegate.categories != categories ||
|
||||
oldDelegate.selectedCategory != selectedCategory;
|
||||
}
|
||||
}
|
||||
|
||||
117
lib/presentation/pages/product/widgets/product_card.dart
Normal file
117
lib/presentation/pages/product/widgets/product_card.dart
Normal file
@ -0,0 +1,117 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../common/extension/extension.dart';
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../../domain/product/product.dart';
|
||||
import '../../../components/image/image.dart';
|
||||
|
||||
class ProductCard extends StatelessWidget {
|
||||
const ProductCard({super.key, required this.product, this.onTap});
|
||||
|
||||
final Product product;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 12.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.1),
|
||||
spreadRadius: 1,
|
||||
blurRadius: 6,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Row(
|
||||
children: [
|
||||
// Product Image
|
||||
_buildProductImage(),
|
||||
const SizedBox(width: 12),
|
||||
// Product Info
|
||||
Expanded(child: _buildProductInfo()),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildProductImage() {
|
||||
return Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: AppColor.background,
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: AppNetworkImage(url: product.imageUrl, fit: BoxFit.cover),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildProductInfo() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
product.name,
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
product.description,
|
||||
style: TextStyle(fontSize: 12, color: AppColor.textLight),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
product.price.currencyFormatRp,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColor.primary,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: _getStatusColor().withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Text(
|
||||
product.isActive ? 'AKTIF' : 'NONAKTIF',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: _getStatusColor(),
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Color _getStatusColor() {
|
||||
return product.isActive ? Colors.green : Colors.red;
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../common/extension/extension.dart';
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../../domain/product/product.dart';
|
||||
import '../../../components/image/image.dart';
|
||||
import '../../../components/spacer/spacer.dart';
|
||||
import '../product_page.dart';
|
||||
|
||||
class ProductTile extends StatelessWidget {
|
||||
final Product product;
|
||||
@ -90,7 +92,7 @@ class ProductTile extends StatelessWidget {
|
||||
|
||||
Widget _buildProductImage() {
|
||||
return Expanded(
|
||||
flex: 2,
|
||||
flex: 3,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
decoration: const BoxDecoration(
|
||||
@ -100,12 +102,12 @@ class ProductTile extends StatelessWidget {
|
||||
topRight: Radius.circular(12.0),
|
||||
),
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
Center(
|
||||
child: Icon(Icons.image, size: 32, color: AppColor.textLight),
|
||||
),
|
||||
],
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(12.0),
|
||||
topRight: Radius.circular(12.0),
|
||||
),
|
||||
child: AppNetworkImage(url: product.imageUrl, fit: BoxFit.cover),
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -133,7 +135,7 @@ class ProductTile extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
_formatPrice(product.price),
|
||||
product.price.currencyFormatRp,
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: product.isActive
|
||||
? AppColor.primary
|
||||
@ -151,30 +153,20 @@ class ProductTile extends StatelessWidget {
|
||||
|
||||
Widget _buildBottomInfo() {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
'Stok: ${product.stock}',
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: AppColor.textSecondary,
|
||||
fontSize: 9,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.primaryLight.withOpacity(0.1),
|
||||
color: _getPrinterTypeColor().withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(3.0),
|
||||
),
|
||||
child: Text(
|
||||
product.category,
|
||||
product.printerType.toUpperCase(),
|
||||
style: AppStyle.xs.copyWith(
|
||||
color: product.isActive
|
||||
? AppColor.primary
|
||||
? _getPrinterTypeColor()
|
||||
: AppColor.textSecondary,
|
||||
fontSize: 8,
|
||||
fontWeight: FontWeight.w500,
|
||||
@ -185,7 +177,16 @@ class ProductTile extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
String _formatPrice(int price) {
|
||||
return 'Rp ${price.toString().replaceAllMapped(RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'), (Match m) => '${m[1]}.')}';
|
||||
Color _getPrinterTypeColor() {
|
||||
switch (product.printerType.toLowerCase()) {
|
||||
case 'kitchen':
|
||||
return Colors.orange;
|
||||
case 'bar':
|
||||
return Colors.blue;
|
||||
case 'receipt':
|
||||
return AppColor.primary;
|
||||
default:
|
||||
return AppColor.primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:line_icons/line_icons.dart';
|
||||
import 'package:loader_overlay/loader_overlay.dart';
|
||||
|
||||
import '../../../application/auth/auth_bloc.dart';
|
||||
import '../../../application/auth/logout_form/logout_form_bloc.dart';
|
||||
import '../../../common/theme/theme.dart';
|
||||
import '../../../injection.dart';
|
||||
@ -49,78 +50,85 @@ class ProfilePage extends StatelessWidget implements AutoRouteWrapper {
|
||||
},
|
||||
),
|
||||
],
|
||||
child: Scaffold(
|
||||
backgroundColor: AppColor.background,
|
||||
body: CustomScrollView(
|
||||
slivers: [
|
||||
SliverAppBar(
|
||||
backgroundColor: AppColor.primary,
|
||||
elevation: 0,
|
||||
pinned: true,
|
||||
expandedHeight: 264.0,
|
||||
flexibleSpace: LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints constraints) {
|
||||
// Calculate the collapse ratio
|
||||
final double top = constraints.biggest.height;
|
||||
final double collapsedHeight =
|
||||
MediaQuery.of(context).padding.top + kToolbarHeight;
|
||||
final double expandedHeight = 264.0;
|
||||
final double shrinkRatio =
|
||||
((expandedHeight - top) /
|
||||
(expandedHeight - collapsedHeight))
|
||||
.clamp(0.0, 1.0);
|
||||
child: BlocBuilder<AuthBloc, AuthState>(
|
||||
builder: (context, state) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColor.background,
|
||||
body: CustomScrollView(
|
||||
slivers: [
|
||||
SliverAppBar(
|
||||
backgroundColor: AppColor.primary,
|
||||
elevation: 0,
|
||||
pinned: true,
|
||||
expandedHeight: 264.0,
|
||||
flexibleSpace: LayoutBuilder(
|
||||
builder:
|
||||
(BuildContext context, BoxConstraints constraints) {
|
||||
// Calculate the collapse ratio
|
||||
final double top = constraints.biggest.height;
|
||||
final double collapsedHeight =
|
||||
MediaQuery.of(context).padding.top +
|
||||
kToolbarHeight;
|
||||
final double expandedHeight = 264.0;
|
||||
final double shrinkRatio =
|
||||
((expandedHeight - top) /
|
||||
(expandedHeight - collapsedHeight))
|
||||
.clamp(0.0, 1.0);
|
||||
|
||||
return FlexibleSpaceBar(
|
||||
background: ProfileHeader(),
|
||||
titlePadding: const EdgeInsets.only(
|
||||
left: 20,
|
||||
right: 12,
|
||||
bottom: 16,
|
||||
),
|
||||
title: Opacity(
|
||||
opacity: shrinkRatio,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Profile',
|
||||
style: AppStyle.xl.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 18,
|
||||
letterSpacing: -0.5,
|
||||
color: AppColor.white,
|
||||
return FlexibleSpaceBar(
|
||||
background: ProfileHeader(user: state.user),
|
||||
titlePadding: const EdgeInsets.only(
|
||||
left: 20,
|
||||
right: 12,
|
||||
bottom: 16,
|
||||
),
|
||||
),
|
||||
ActionIconButton(
|
||||
onTap: () {},
|
||||
icon: LineIcons.userEdit,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
title: Opacity(
|
||||
opacity: shrinkRatio,
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Profile',
|
||||
style: AppStyle.xl.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 18,
|
||||
letterSpacing: -0.5,
|
||||
color: AppColor.white,
|
||||
),
|
||||
),
|
||||
ActionIconButton(
|
||||
onTap: () {},
|
||||
icon: LineIcons.userEdit,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Column(
|
||||
children: [
|
||||
const SpaceHeight(20),
|
||||
ProfileAccountInfo(user: state.user),
|
||||
const SpaceHeight(12),
|
||||
ProfileBusinessSetting(),
|
||||
const SpaceHeight(12),
|
||||
ProfileAppSetting(),
|
||||
const SpaceHeight(12),
|
||||
ProfileSupport(),
|
||||
const SpaceHeight(12),
|
||||
ProfileDangerZone(),
|
||||
const SpaceHeight(30),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Column(
|
||||
children: [
|
||||
const SpaceHeight(20),
|
||||
ProfileAccountInfo(),
|
||||
const SpaceHeight(12),
|
||||
ProfileBusinessSetting(),
|
||||
const SpaceHeight(12),
|
||||
ProfileAppSetting(),
|
||||
const SpaceHeight(12),
|
||||
ProfileSupport(),
|
||||
const SpaceHeight(12),
|
||||
ProfileDangerZone(),
|
||||
const SpaceHeight(30),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:line_icons/line_icons.dart';
|
||||
|
||||
import '../../../../common/extension/extension.dart';
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../../domain/auth/auth.dart';
|
||||
import 'divider.dart';
|
||||
import 'profile_tile.dart';
|
||||
|
||||
class ProfileAccountInfo extends StatelessWidget {
|
||||
const ProfileAccountInfo({super.key});
|
||||
final User user;
|
||||
const ProfileAccountInfo({super.key, required this.user});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -38,9 +42,9 @@ class ProfileAccountInfo extends StatelessWidget {
|
||||
),
|
||||
|
||||
ProfileTile(
|
||||
icon: Icons.email_outlined,
|
||||
icon: LineIcons.envelope,
|
||||
title: 'Email',
|
||||
subtitle: 'john.doe@business.com',
|
||||
subtitle: user.email,
|
||||
onTap: () {
|
||||
// Edit email
|
||||
},
|
||||
@ -49,31 +53,9 @@ class ProfileAccountInfo extends StatelessWidget {
|
||||
ProfileDivider(),
|
||||
|
||||
ProfileTile(
|
||||
icon: Icons.phone_outlined,
|
||||
title: 'Phone Number',
|
||||
subtitle: '+62 812 3456 7890',
|
||||
onTap: () {
|
||||
// Edit phone
|
||||
},
|
||||
),
|
||||
|
||||
ProfileDivider(),
|
||||
|
||||
ProfileTile(
|
||||
icon: Icons.location_on_outlined,
|
||||
title: 'Address',
|
||||
subtitle: 'Jl. Merdeka No. 123, Jakarta',
|
||||
onTap: () {
|
||||
// Edit address
|
||||
},
|
||||
),
|
||||
|
||||
ProfileDivider(),
|
||||
|
||||
ProfileTile(
|
||||
icon: Icons.calendar_today_outlined,
|
||||
icon: LineIcons.calendarAlt,
|
||||
title: 'Member Since',
|
||||
subtitle: 'January 15, 2024',
|
||||
subtitle: user.createdAt.toDate,
|
||||
showArrow: false,
|
||||
),
|
||||
],
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import '../../../../common/extension/extension.dart';
|
||||
import '../../../../common/painter/wave_painter.dart';
|
||||
import '../../../../common/theme/theme.dart';
|
||||
import '../../../../domain/auth/auth.dart';
|
||||
import '../../../components/spacer/spacer.dart';
|
||||
|
||||
class ProfileHeader extends StatefulWidget {
|
||||
const ProfileHeader({super.key});
|
||||
final User user;
|
||||
const ProfileHeader({super.key, required this.user});
|
||||
|
||||
@override
|
||||
State<ProfileHeader> createState() => _ProfileHeaderState();
|
||||
@ -347,7 +350,7 @@ class _ProfileHeaderState extends State<ProfileHeader>
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
'John Doe',
|
||||
widget.user.name,
|
||||
style: AppStyle.h5.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColor.textWhite,
|
||||
@ -408,7 +411,7 @@ class _ProfileHeaderState extends State<ProfileHeader>
|
||||
),
|
||||
const SpaceHeight(6),
|
||||
Text(
|
||||
'Business Owner',
|
||||
widget.user.role.toTitleCase,
|
||||
style: AppStyle.md.copyWith(
|
||||
color: AppColor.textWhite,
|
||||
fontWeight: FontWeight.w600,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -48,5 +48,8 @@ class AppRouter extends RootStackRouter {
|
||||
|
||||
// Finance page
|
||||
AutoRoute(page: FinanceRoute.page),
|
||||
|
||||
// Error
|
||||
AutoRoute(page: ErrorRoute.page),
|
||||
];
|
||||
}
|
||||
|
||||
@ -10,48 +10,51 @@
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:apskel_owner_flutter/presentation/pages/auth/login/login_page.dart'
|
||||
as _i7;
|
||||
as _i8;
|
||||
import 'package:apskel_owner_flutter/presentation/pages/customer/customer_page.dart'
|
||||
as _i1;
|
||||
import 'package:apskel_owner_flutter/presentation/pages/finance/finance_page.dart'
|
||||
import 'package:apskel_owner_flutter/presentation/pages/error/error_page.dart'
|
||||
as _i3;
|
||||
import 'package:apskel_owner_flutter/presentation/pages/finance/finance_page.dart'
|
||||
as _i4;
|
||||
import 'package:apskel_owner_flutter/presentation/pages/form/daily_task_form_page.dart'
|
||||
as _i2;
|
||||
import 'package:apskel_owner_flutter/presentation/pages/home/home_page.dart'
|
||||
as _i4;
|
||||
import 'package:apskel_owner_flutter/presentation/pages/inventory/inventory_page.dart'
|
||||
as _i5;
|
||||
import 'package:apskel_owner_flutter/presentation/pages/language/language_page.dart'
|
||||
import 'package:apskel_owner_flutter/presentation/pages/inventory/inventory_page.dart'
|
||||
as _i6;
|
||||
import 'package:apskel_owner_flutter/presentation/pages/language/language_page.dart'
|
||||
as _i7;
|
||||
import 'package:apskel_owner_flutter/presentation/pages/main/main_page.dart'
|
||||
as _i8;
|
||||
import 'package:apskel_owner_flutter/presentation/pages/product/product_page.dart'
|
||||
as _i9;
|
||||
import 'package:apskel_owner_flutter/presentation/pages/profile/profile_page.dart'
|
||||
import 'package:apskel_owner_flutter/presentation/pages/product/product_page.dart'
|
||||
as _i10;
|
||||
import 'package:apskel_owner_flutter/presentation/pages/purchase/purchase_page.dart'
|
||||
import 'package:apskel_owner_flutter/presentation/pages/profile/profile_page.dart'
|
||||
as _i11;
|
||||
import 'package:apskel_owner_flutter/presentation/pages/report/report_page.dart'
|
||||
import 'package:apskel_owner_flutter/presentation/pages/purchase/purchase_page.dart'
|
||||
as _i12;
|
||||
import 'package:apskel_owner_flutter/presentation/pages/sales/sales_page.dart'
|
||||
import 'package:apskel_owner_flutter/presentation/pages/report/report_page.dart'
|
||||
as _i13;
|
||||
import 'package:apskel_owner_flutter/presentation/pages/schedule/schedule_page.dart'
|
||||
import 'package:apskel_owner_flutter/presentation/pages/sales/sales_page.dart'
|
||||
as _i14;
|
||||
import 'package:apskel_owner_flutter/presentation/pages/splash/splash_page.dart'
|
||||
import 'package:apskel_owner_flutter/presentation/pages/schedule/schedule_page.dart'
|
||||
as _i15;
|
||||
import 'package:apskel_owner_flutter/presentation/pages/transaction/transaction_page.dart'
|
||||
import 'package:apskel_owner_flutter/presentation/pages/splash/splash_page.dart'
|
||||
as _i16;
|
||||
import 'package:auto_route/auto_route.dart' as _i17;
|
||||
import 'package:apskel_owner_flutter/presentation/pages/transaction/transaction_page.dart'
|
||||
as _i17;
|
||||
import 'package:auto_route/auto_route.dart' as _i18;
|
||||
import 'package:flutter/material.dart' as _i19;
|
||||
|
||||
/// generated route for
|
||||
/// [_i1.CustomerPage]
|
||||
class CustomerRoute extends _i17.PageRouteInfo<void> {
|
||||
const CustomerRoute({List<_i17.PageRouteInfo>? children})
|
||||
class CustomerRoute extends _i18.PageRouteInfo<void> {
|
||||
const CustomerRoute({List<_i18.PageRouteInfo>? children})
|
||||
: super(CustomerRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'CustomerRoute';
|
||||
|
||||
static _i17.PageInfo page = _i17.PageInfo(
|
||||
static _i18.PageInfo page = _i18.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i1.CustomerPage();
|
||||
@ -61,13 +64,13 @@ class CustomerRoute extends _i17.PageRouteInfo<void> {
|
||||
|
||||
/// generated route for
|
||||
/// [_i2.DailyTasksFormPage]
|
||||
class DailyTasksFormRoute extends _i17.PageRouteInfo<void> {
|
||||
const DailyTasksFormRoute({List<_i17.PageRouteInfo>? children})
|
||||
class DailyTasksFormRoute extends _i18.PageRouteInfo<void> {
|
||||
const DailyTasksFormRoute({List<_i18.PageRouteInfo>? children})
|
||||
: super(DailyTasksFormRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'DailyTasksFormRoute';
|
||||
|
||||
static _i17.PageInfo page = _i17.PageInfo(
|
||||
static _i18.PageInfo page = _i18.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i2.DailyTasksFormPage();
|
||||
@ -76,225 +79,303 @@ class DailyTasksFormRoute extends _i17.PageRouteInfo<void> {
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i3.FinancePage]
|
||||
class FinanceRoute extends _i17.PageRouteInfo<void> {
|
||||
const FinanceRoute({List<_i17.PageRouteInfo>? children})
|
||||
/// [_i3.ErrorPage]
|
||||
class ErrorRoute extends _i18.PageRouteInfo<ErrorRouteArgs> {
|
||||
ErrorRoute({
|
||||
_i19.Key? key,
|
||||
String? title,
|
||||
String? message,
|
||||
_i19.VoidCallback? onRetry,
|
||||
_i19.VoidCallback? onBack,
|
||||
String? errorCode,
|
||||
_i19.IconData? errorIcon,
|
||||
List<_i18.PageRouteInfo>? children,
|
||||
}) : super(
|
||||
ErrorRoute.name,
|
||||
args: ErrorRouteArgs(
|
||||
key: key,
|
||||
title: title,
|
||||
message: message,
|
||||
onRetry: onRetry,
|
||||
onBack: onBack,
|
||||
errorCode: errorCode,
|
||||
errorIcon: errorIcon,
|
||||
),
|
||||
initialChildren: children,
|
||||
);
|
||||
|
||||
static const String name = 'ErrorRoute';
|
||||
|
||||
static _i18.PageInfo page = _i18.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
final args = data.argsAs<ErrorRouteArgs>(
|
||||
orElse: () => const ErrorRouteArgs(),
|
||||
);
|
||||
return _i3.ErrorPage(
|
||||
key: args.key,
|
||||
title: args.title,
|
||||
message: args.message,
|
||||
onRetry: args.onRetry,
|
||||
onBack: args.onBack,
|
||||
errorCode: args.errorCode,
|
||||
errorIcon: args.errorIcon,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
class ErrorRouteArgs {
|
||||
const ErrorRouteArgs({
|
||||
this.key,
|
||||
this.title,
|
||||
this.message,
|
||||
this.onRetry,
|
||||
this.onBack,
|
||||
this.errorCode,
|
||||
this.errorIcon,
|
||||
});
|
||||
|
||||
final _i19.Key? key;
|
||||
|
||||
final String? title;
|
||||
|
||||
final String? message;
|
||||
|
||||
final _i19.VoidCallback? onRetry;
|
||||
|
||||
final _i19.VoidCallback? onBack;
|
||||
|
||||
final String? errorCode;
|
||||
|
||||
final _i19.IconData? errorIcon;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ErrorRouteArgs{key: $key, title: $title, message: $message, onRetry: $onRetry, onBack: $onBack, errorCode: $errorCode, errorIcon: $errorIcon}';
|
||||
}
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i4.FinancePage]
|
||||
class FinanceRoute extends _i18.PageRouteInfo<void> {
|
||||
const FinanceRoute({List<_i18.PageRouteInfo>? children})
|
||||
: super(FinanceRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'FinanceRoute';
|
||||
|
||||
static _i17.PageInfo page = _i17.PageInfo(
|
||||
static _i18.PageInfo page = _i18.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i3.FinancePage();
|
||||
return _i18.WrappedRoute(child: const _i4.FinancePage());
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i4.HomePage]
|
||||
class HomeRoute extends _i17.PageRouteInfo<void> {
|
||||
const HomeRoute({List<_i17.PageRouteInfo>? children})
|
||||
/// [_i5.HomePage]
|
||||
class HomeRoute extends _i18.PageRouteInfo<void> {
|
||||
const HomeRoute({List<_i18.PageRouteInfo>? children})
|
||||
: super(HomeRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'HomeRoute';
|
||||
|
||||
static _i17.PageInfo page = _i17.PageInfo(
|
||||
static _i18.PageInfo page = _i18.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i4.HomePage();
|
||||
return const _i5.HomePage();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i5.InventoryPage]
|
||||
class InventoryRoute extends _i17.PageRouteInfo<void> {
|
||||
const InventoryRoute({List<_i17.PageRouteInfo>? children})
|
||||
/// [_i6.InventoryPage]
|
||||
class InventoryRoute extends _i18.PageRouteInfo<void> {
|
||||
const InventoryRoute({List<_i18.PageRouteInfo>? children})
|
||||
: super(InventoryRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'InventoryRoute';
|
||||
|
||||
static _i17.PageInfo page = _i17.PageInfo(
|
||||
static _i18.PageInfo page = _i18.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i5.InventoryPage();
|
||||
return const _i6.InventoryPage();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i6.LanguagePage]
|
||||
class LanguageRoute extends _i17.PageRouteInfo<void> {
|
||||
const LanguageRoute({List<_i17.PageRouteInfo>? children})
|
||||
/// [_i7.LanguagePage]
|
||||
class LanguageRoute extends _i18.PageRouteInfo<void> {
|
||||
const LanguageRoute({List<_i18.PageRouteInfo>? children})
|
||||
: super(LanguageRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'LanguageRoute';
|
||||
|
||||
static _i17.PageInfo page = _i17.PageInfo(
|
||||
static _i18.PageInfo page = _i18.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i6.LanguagePage();
|
||||
return const _i7.LanguagePage();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i7.LoginPage]
|
||||
class LoginRoute extends _i17.PageRouteInfo<void> {
|
||||
const LoginRoute({List<_i17.PageRouteInfo>? children})
|
||||
/// [_i8.LoginPage]
|
||||
class LoginRoute extends _i18.PageRouteInfo<void> {
|
||||
const LoginRoute({List<_i18.PageRouteInfo>? children})
|
||||
: super(LoginRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'LoginRoute';
|
||||
|
||||
static _i17.PageInfo page = _i17.PageInfo(
|
||||
static _i18.PageInfo page = _i18.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return _i17.WrappedRoute(child: const _i7.LoginPage());
|
||||
return _i18.WrappedRoute(child: const _i8.LoginPage());
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i8.MainPage]
|
||||
class MainRoute extends _i17.PageRouteInfo<void> {
|
||||
const MainRoute({List<_i17.PageRouteInfo>? children})
|
||||
/// [_i9.MainPage]
|
||||
class MainRoute extends _i18.PageRouteInfo<void> {
|
||||
const MainRoute({List<_i18.PageRouteInfo>? children})
|
||||
: super(MainRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'MainRoute';
|
||||
|
||||
static _i17.PageInfo page = _i17.PageInfo(
|
||||
static _i18.PageInfo page = _i18.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i8.MainPage();
|
||||
return const _i9.MainPage();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i9.ProductPage]
|
||||
class ProductRoute extends _i17.PageRouteInfo<void> {
|
||||
const ProductRoute({List<_i17.PageRouteInfo>? children})
|
||||
/// [_i10.ProductPage]
|
||||
class ProductRoute extends _i18.PageRouteInfo<void> {
|
||||
const ProductRoute({List<_i18.PageRouteInfo>? children})
|
||||
: super(ProductRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'ProductRoute';
|
||||
|
||||
static _i17.PageInfo page = _i17.PageInfo(
|
||||
static _i18.PageInfo page = _i18.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i9.ProductPage();
|
||||
return _i18.WrappedRoute(child: const _i10.ProductPage());
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i10.ProfilePage]
|
||||
class ProfileRoute extends _i17.PageRouteInfo<void> {
|
||||
const ProfileRoute({List<_i17.PageRouteInfo>? children})
|
||||
/// [_i11.ProfilePage]
|
||||
class ProfileRoute extends _i18.PageRouteInfo<void> {
|
||||
const ProfileRoute({List<_i18.PageRouteInfo>? children})
|
||||
: super(ProfileRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'ProfileRoute';
|
||||
|
||||
static _i17.PageInfo page = _i17.PageInfo(
|
||||
static _i18.PageInfo page = _i18.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i10.ProfilePage();
|
||||
return _i18.WrappedRoute(child: const _i11.ProfilePage());
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i11.PurchasePage]
|
||||
class PurchaseRoute extends _i17.PageRouteInfo<void> {
|
||||
const PurchaseRoute({List<_i17.PageRouteInfo>? children})
|
||||
/// [_i12.PurchasePage]
|
||||
class PurchaseRoute extends _i18.PageRouteInfo<void> {
|
||||
const PurchaseRoute({List<_i18.PageRouteInfo>? children})
|
||||
: super(PurchaseRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'PurchaseRoute';
|
||||
|
||||
static _i17.PageInfo page = _i17.PageInfo(
|
||||
static _i18.PageInfo page = _i18.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i11.PurchasePage();
|
||||
return const _i12.PurchasePage();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i12.ReportPage]
|
||||
class ReportRoute extends _i17.PageRouteInfo<void> {
|
||||
const ReportRoute({List<_i17.PageRouteInfo>? children})
|
||||
/// [_i13.ReportPage]
|
||||
class ReportRoute extends _i18.PageRouteInfo<void> {
|
||||
const ReportRoute({List<_i18.PageRouteInfo>? children})
|
||||
: super(ReportRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'ReportRoute';
|
||||
|
||||
static _i17.PageInfo page = _i17.PageInfo(
|
||||
static _i18.PageInfo page = _i18.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i12.ReportPage();
|
||||
return const _i13.ReportPage();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i13.SalesPage]
|
||||
class SalesRoute extends _i17.PageRouteInfo<void> {
|
||||
const SalesRoute({List<_i17.PageRouteInfo>? children})
|
||||
/// [_i14.SalesPage]
|
||||
class SalesRoute extends _i18.PageRouteInfo<void> {
|
||||
const SalesRoute({List<_i18.PageRouteInfo>? children})
|
||||
: super(SalesRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'SalesRoute';
|
||||
|
||||
static _i17.PageInfo page = _i17.PageInfo(
|
||||
static _i18.PageInfo page = _i18.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i13.SalesPage();
|
||||
return _i18.WrappedRoute(child: const _i14.SalesPage());
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i14.SchedulePage]
|
||||
class ScheduleRoute extends _i17.PageRouteInfo<void> {
|
||||
const ScheduleRoute({List<_i17.PageRouteInfo>? children})
|
||||
/// [_i15.SchedulePage]
|
||||
class ScheduleRoute extends _i18.PageRouteInfo<void> {
|
||||
const ScheduleRoute({List<_i18.PageRouteInfo>? children})
|
||||
: super(ScheduleRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'ScheduleRoute';
|
||||
|
||||
static _i17.PageInfo page = _i17.PageInfo(
|
||||
static _i18.PageInfo page = _i18.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i14.SchedulePage();
|
||||
return const _i15.SchedulePage();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i15.SplashPage]
|
||||
class SplashRoute extends _i17.PageRouteInfo<void> {
|
||||
const SplashRoute({List<_i17.PageRouteInfo>? children})
|
||||
/// [_i16.SplashPage]
|
||||
class SplashRoute extends _i18.PageRouteInfo<void> {
|
||||
const SplashRoute({List<_i18.PageRouteInfo>? children})
|
||||
: super(SplashRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'SplashRoute';
|
||||
|
||||
static _i17.PageInfo page = _i17.PageInfo(
|
||||
static _i18.PageInfo page = _i18.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i15.SplashPage();
|
||||
return const _i16.SplashPage();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
/// [_i16.TransactionPage]
|
||||
class TransactionRoute extends _i17.PageRouteInfo<void> {
|
||||
const TransactionRoute({List<_i17.PageRouteInfo>? children})
|
||||
/// [_i17.TransactionPage]
|
||||
class TransactionRoute extends _i18.PageRouteInfo<void> {
|
||||
const TransactionRoute({List<_i18.PageRouteInfo>? children})
|
||||
: super(TransactionRoute.name, initialChildren: children);
|
||||
|
||||
static const String name = 'TransactionRoute';
|
||||
|
||||
static _i17.PageInfo page = _i17.PageInfo(
|
||||
static _i18.PageInfo page = _i18.PageInfo(
|
||||
name,
|
||||
builder: (data) {
|
||||
return const _i16.TransactionPage();
|
||||
return const _i17.TransactionPage();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import file_selector_macos
|
||||
import package_info_plus
|
||||
import path_provider_foundation
|
||||
import shared_preferences_foundation
|
||||
import sqflite_darwin
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
||||
@ -17,4 +18,5 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||
}
|
||||
|
||||
120
pubspec.lock
120
pubspec.lock
@ -161,6 +161,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.11.1"
|
||||
cached_network_image:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: cached_network_image
|
||||
sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.1"
|
||||
cached_network_image_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cached_network_image_platform_interface
|
||||
sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.1"
|
||||
cached_network_image_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cached_network_image_web
|
||||
sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -422,6 +446,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.1.1"
|
||||
flutter_cache_manager:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_cache_manager
|
||||
sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.1"
|
||||
flutter_gen_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -813,6 +845,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
octo_image:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: octo_image
|
||||
sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -973,6 +1013,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
rxdart:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: rxdart
|
||||
sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.28.0"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -1045,6 +1093,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
shimmer:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shimmer
|
||||
sha256: "5f88c883a22e9f9f299e5ba0e4f7e6054857224976a5d9f839d4ebdc94a14ac9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
simple_gesture_detector:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1082,6 +1138,54 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.1"
|
||||
sprintf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sprintf
|
||||
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
sqflite:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite
|
||||
sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
sqflite_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_android
|
||||
sha256: "2b3070c5fa881839f8b402ee4a39c1b4d561704d4ebbbcfb808a119bc2a1701b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
sqflite_common:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_common
|
||||
sha256: "6ef422a4525ecc601db6c0a2233ff448c731307906e92cabc9ba292afaae16a6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.6"
|
||||
sqflite_darwin:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_darwin
|
||||
sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
sqflite_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_platform_interface
|
||||
sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1114,6 +1218,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
synchronized:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: synchronized
|
||||
sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.0"
|
||||
table_calendar:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -1162,6 +1274,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: uuid
|
||||
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.5.1"
|
||||
vector_graphics:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@ -40,6 +40,8 @@ dependencies:
|
||||
table_calendar: ^3.2.0
|
||||
package_info_plus: ^8.3.1
|
||||
loader_overlay: ^5.0.0
|
||||
shimmer: ^3.0.0
|
||||
cached_network_image: ^3.4.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user