feat: profit loss range date

This commit is contained in:
efrilm 2025-08-18 16:48:35 +07:00
parent 1f6e5e9a2b
commit ac8c2c0f54
5 changed files with 338 additions and 139 deletions

View File

@ -24,12 +24,15 @@ class ProfitLossLoaderBloc
Emitter<ProfitLossLoaderState> emit,
) {
return event.map(
rangeDateChanged: (e) async {
emit(state.copyWith(dateFrom: e.dateFrom, dateTo: e.dateTo));
},
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(),
dateFrom: state.dateFrom,
dateTo: state.dateTo,
);
var data = result.fold(

View File

@ -19,27 +19,34 @@ final _privateConstructorUsedError = UnsupportedError(
mixin _$ProfitLossLoaderEvent {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(DateTime dateFrom, DateTime dateTo)
rangeDateChanged,
required TResult Function() fetched,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(DateTime dateFrom, DateTime dateTo)? rangeDateChanged,
TResult? Function()? fetched,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(DateTime dateFrom, DateTime dateTo)? rangeDateChanged,
TResult Function()? fetched,
required TResult orElse(),
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_RangeDateChanged value) rangeDateChanged,
required TResult Function(_Fetched value) fetched,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_RangeDateChanged value)? rangeDateChanged,
TResult? Function(_Fetched value)? fetched,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_RangeDateChanged value)? rangeDateChanged,
TResult Function(_Fetched value)? fetched,
required TResult orElse(),
}) => throw _privateConstructorUsedError;
@ -70,6 +77,164 @@ class _$ProfitLossLoaderEventCopyWithImpl<
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
abstract class _$$RangeDateChangedImplCopyWith<$Res> {
factory _$$RangeDateChangedImplCopyWith(
_$RangeDateChangedImpl value,
$Res Function(_$RangeDateChangedImpl) then,
) = __$$RangeDateChangedImplCopyWithImpl<$Res>;
@useResult
$Res call({DateTime dateFrom, DateTime dateTo});
}
/// @nodoc
class __$$RangeDateChangedImplCopyWithImpl<$Res>
extends _$ProfitLossLoaderEventCopyWithImpl<$Res, _$RangeDateChangedImpl>
implements _$$RangeDateChangedImplCopyWith<$Res> {
__$$RangeDateChangedImplCopyWithImpl(
_$RangeDateChangedImpl _value,
$Res Function(_$RangeDateChangedImpl) _then,
) : super(_value, _then);
/// Create a copy of ProfitLossLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? dateFrom = null, Object? dateTo = null}) {
return _then(
_$RangeDateChangedImpl(
null == dateFrom
? _value.dateFrom
: dateFrom // ignore: cast_nullable_to_non_nullable
as DateTime,
null == dateTo
? _value.dateTo
: dateTo // ignore: cast_nullable_to_non_nullable
as DateTime,
),
);
}
}
/// @nodoc
class _$RangeDateChangedImpl implements _RangeDateChanged {
const _$RangeDateChangedImpl(this.dateFrom, this.dateTo);
@override
final DateTime dateFrom;
@override
final DateTime dateTo;
@override
String toString() {
return 'ProfitLossLoaderEvent.rangeDateChanged(dateFrom: $dateFrom, dateTo: $dateTo)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$RangeDateChangedImpl &&
(identical(other.dateFrom, dateFrom) ||
other.dateFrom == dateFrom) &&
(identical(other.dateTo, dateTo) || other.dateTo == dateTo));
}
@override
int get hashCode => Object.hash(runtimeType, dateFrom, dateTo);
/// Create a copy of ProfitLossLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$RangeDateChangedImplCopyWith<_$RangeDateChangedImpl> get copyWith =>
__$$RangeDateChangedImplCopyWithImpl<_$RangeDateChangedImpl>(
this,
_$identity,
);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(DateTime dateFrom, DateTime dateTo)
rangeDateChanged,
required TResult Function() fetched,
}) {
return rangeDateChanged(dateFrom, dateTo);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(DateTime dateFrom, DateTime dateTo)? rangeDateChanged,
TResult? Function()? fetched,
}) {
return rangeDateChanged?.call(dateFrom, dateTo);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(DateTime dateFrom, DateTime dateTo)? rangeDateChanged,
TResult Function()? fetched,
required TResult orElse(),
}) {
if (rangeDateChanged != null) {
return rangeDateChanged(dateFrom, dateTo);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_RangeDateChanged value) rangeDateChanged,
required TResult Function(_Fetched value) fetched,
}) {
return rangeDateChanged(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_RangeDateChanged value)? rangeDateChanged,
TResult? Function(_Fetched value)? fetched,
}) {
return rangeDateChanged?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_RangeDateChanged value)? rangeDateChanged,
TResult Function(_Fetched value)? fetched,
required TResult orElse(),
}) {
if (rangeDateChanged != null) {
return rangeDateChanged(this);
}
return orElse();
}
}
abstract class _RangeDateChanged implements ProfitLossLoaderEvent {
const factory _RangeDateChanged(
final DateTime dateFrom,
final DateTime dateTo,
) = _$RangeDateChangedImpl;
DateTime get dateFrom;
DateTime get dateTo;
/// Create a copy of ProfitLossLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$RangeDateChangedImplCopyWith<_$RangeDateChangedImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$FetchedImplCopyWith<$Res> {
factory _$$FetchedImplCopyWith(
@ -112,19 +277,27 @@ class _$FetchedImpl implements _Fetched {
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({required TResult Function() fetched}) {
TResult when<TResult extends Object?>({
required TResult Function(DateTime dateFrom, DateTime dateTo)
rangeDateChanged,
required TResult Function() fetched,
}) {
return fetched();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({TResult? Function()? fetched}) {
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(DateTime dateFrom, DateTime dateTo)? rangeDateChanged,
TResult? Function()? fetched,
}) {
return fetched?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(DateTime dateFrom, DateTime dateTo)? rangeDateChanged,
TResult Function()? fetched,
required TResult orElse(),
}) {
@ -137,6 +310,7 @@ class _$FetchedImpl implements _Fetched {
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_RangeDateChanged value) rangeDateChanged,
required TResult Function(_Fetched value) fetched,
}) {
return fetched(this);
@ -145,6 +319,7 @@ class _$FetchedImpl implements _Fetched {
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_RangeDateChanged value)? rangeDateChanged,
TResult? Function(_Fetched value)? fetched,
}) {
return fetched?.call(this);
@ -153,6 +328,7 @@ class _$FetchedImpl implements _Fetched {
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_RangeDateChanged value)? rangeDateChanged,
TResult Function(_Fetched value)? fetched,
required TResult orElse(),
}) {
@ -173,6 +349,8 @@ mixin _$ProfitLossLoaderState {
Option<AnalyticFailure> get failureOptionProfitLoss =>
throw _privateConstructorUsedError;
bool get isFetching => throw _privateConstructorUsedError;
DateTime get dateFrom => throw _privateConstructorUsedError;
DateTime get dateTo => throw _privateConstructorUsedError;
/// Create a copy of ProfitLossLoaderState
/// with the given fields replaced by the non-null parameter values.
@ -192,6 +370,8 @@ abstract class $ProfitLossLoaderStateCopyWith<$Res> {
ProfitLossAnalytic profitLoss,
Option<AnalyticFailure> failureOptionProfitLoss,
bool isFetching,
DateTime dateFrom,
DateTime dateTo,
});
$ProfitLossAnalyticCopyWith<$Res> get profitLoss;
@ -218,6 +398,8 @@ class _$ProfitLossLoaderStateCopyWithImpl<
Object? profitLoss = null,
Object? failureOptionProfitLoss = null,
Object? isFetching = null,
Object? dateFrom = null,
Object? dateTo = null,
}) {
return _then(
_value.copyWith(
@ -233,6 +415,14 @@ class _$ProfitLossLoaderStateCopyWithImpl<
? _value.isFetching
: isFetching // ignore: cast_nullable_to_non_nullable
as bool,
dateFrom: null == dateFrom
? _value.dateFrom
: dateFrom // ignore: cast_nullable_to_non_nullable
as DateTime,
dateTo: null == dateTo
? _value.dateTo
: dateTo // ignore: cast_nullable_to_non_nullable
as DateTime,
)
as $Val,
);
@ -262,6 +452,8 @@ abstract class _$$ProfitLossLoaderStateImplCopyWith<$Res>
ProfitLossAnalytic profitLoss,
Option<AnalyticFailure> failureOptionProfitLoss,
bool isFetching,
DateTime dateFrom,
DateTime dateTo,
});
@override
@ -286,6 +478,8 @@ class __$$ProfitLossLoaderStateImplCopyWithImpl<$Res>
Object? profitLoss = null,
Object? failureOptionProfitLoss = null,
Object? isFetching = null,
Object? dateFrom = null,
Object? dateTo = null,
}) {
return _then(
_$ProfitLossLoaderStateImpl(
@ -301,6 +495,14 @@ class __$$ProfitLossLoaderStateImplCopyWithImpl<$Res>
? _value.isFetching
: isFetching // ignore: cast_nullable_to_non_nullable
as bool,
dateFrom: null == dateFrom
? _value.dateFrom
: dateFrom // ignore: cast_nullable_to_non_nullable
as DateTime,
dateTo: null == dateTo
? _value.dateTo
: dateTo // ignore: cast_nullable_to_non_nullable
as DateTime,
),
);
}
@ -313,6 +515,8 @@ class _$ProfitLossLoaderStateImpl implements _ProfitLossLoaderState {
required this.profitLoss,
required this.failureOptionProfitLoss,
this.isFetching = false,
required this.dateFrom,
required this.dateTo,
});
@override
@ -322,10 +526,14 @@ class _$ProfitLossLoaderStateImpl implements _ProfitLossLoaderState {
@override
@JsonKey()
final bool isFetching;
@override
final DateTime dateFrom;
@override
final DateTime dateTo;
@override
String toString() {
return 'ProfitLossLoaderState(profitLoss: $profitLoss, failureOptionProfitLoss: $failureOptionProfitLoss, isFetching: $isFetching)';
return 'ProfitLossLoaderState(profitLoss: $profitLoss, failureOptionProfitLoss: $failureOptionProfitLoss, isFetching: $isFetching, dateFrom: $dateFrom, dateTo: $dateTo)';
}
@override
@ -341,12 +549,21 @@ class _$ProfitLossLoaderStateImpl implements _ProfitLossLoaderState {
) ||
other.failureOptionProfitLoss == failureOptionProfitLoss) &&
(identical(other.isFetching, isFetching) ||
other.isFetching == isFetching));
other.isFetching == isFetching) &&
(identical(other.dateFrom, dateFrom) ||
other.dateFrom == dateFrom) &&
(identical(other.dateTo, dateTo) || other.dateTo == dateTo));
}
@override
int get hashCode =>
Object.hash(runtimeType, profitLoss, failureOptionProfitLoss, isFetching);
int get hashCode => Object.hash(
runtimeType,
profitLoss,
failureOptionProfitLoss,
isFetching,
dateFrom,
dateTo,
);
/// Create a copy of ProfitLossLoaderState
/// with the given fields replaced by the non-null parameter values.
@ -366,6 +583,8 @@ abstract class _ProfitLossLoaderState implements ProfitLossLoaderState {
required final ProfitLossAnalytic profitLoss,
required final Option<AnalyticFailure> failureOptionProfitLoss,
final bool isFetching,
required final DateTime dateFrom,
required final DateTime dateTo,
}) = _$ProfitLossLoaderStateImpl;
@override
@ -374,6 +593,10 @@ abstract class _ProfitLossLoaderState implements ProfitLossLoaderState {
Option<AnalyticFailure> get failureOptionProfitLoss;
@override
bool get isFetching;
@override
DateTime get dateFrom;
@override
DateTime get dateTo;
/// Create a copy of ProfitLossLoaderState
/// with the given fields replaced by the non-null parameter values.

View File

@ -2,5 +2,9 @@ part of 'profit_loss_loader_bloc.dart';
@freezed
class ProfitLossLoaderEvent with _$ProfitLossLoaderEvent {
const factory ProfitLossLoaderEvent.rangeDateChanged(
DateTime dateFrom,
DateTime dateTo,
) = _RangeDateChanged;
const factory ProfitLossLoaderEvent.fetched() = _Fetched;
}

View File

@ -6,10 +6,14 @@ class ProfitLossLoaderState with _$ProfitLossLoaderState {
required ProfitLossAnalytic profitLoss,
required Option<AnalyticFailure> failureOptionProfitLoss,
@Default(false) bool isFetching,
required DateTime dateFrom,
required DateTime dateTo,
}) = _ProfitLossLoaderState;
factory ProfitLossLoaderState.initial() => ProfitLossLoaderState(
profitLoss: ProfitLossAnalytic.empty(),
failureOptionProfitLoss: none(),
dateFrom: DateTime.now().subtract(const Duration(days: 30)),
dateTo: DateTime.now(),
);
}

View File

@ -10,6 +10,7 @@ import '../../../common/theme/theme.dart';
import '../../../domain/analytic/analytic.dart';
import '../../../injection.dart';
import '../../components/appbar/appbar.dart';
import '../../components/field/date_range_picker_field.dart';
import 'widgets/cash_flow.dart';
import 'widgets/category.dart';
import 'widgets/product.dart';
@ -50,14 +51,6 @@ class _FinancePageState extends State<FinancePage>
late Animation<double> _fadeAnimation;
late Animation<double> _scaleAnimation;
String selectedPeriod = 'Hari ini';
final List<String> periods = [
'Hari ini',
'Minggu ini',
'Bulan ini',
'Tahun ini',
];
@override
void initState() {
super.initState();
@ -113,7 +106,16 @@ class _FinancePageState extends State<FinancePage>
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColor.background,
body: BlocBuilder<ProfitLossLoaderBloc, ProfitLossLoaderState>(
body: BlocListener<ProfitLossLoaderBloc, ProfitLossLoaderState>(
listenWhen: (previous, current) =>
previous.dateFrom != current.dateFrom &&
previous.dateTo != current.dateTo,
listener: (context, state) {
context.read<ProfitLossLoaderBloc>().add(
ProfitLossLoaderEvent.fetched(),
);
},
child: BlocBuilder<ProfitLossLoaderBloc, ProfitLossLoaderState>(
builder: (context, state) {
return CustomScrollView(
slivers: [
@ -131,7 +133,22 @@ class _FinancePageState extends State<FinancePage>
SliverToBoxAdapter(
child: FadeTransition(
opacity: _fadeAnimation,
child: _buildPeriodSelector(),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: DateRangePickerField(
maxDate: DateTime.now(),
startDate: state.dateFrom,
endDate: state.dateTo,
onChanged: (startDate, endDate) {
context.read<ProfitLossLoaderBloc>().add(
ProfitLossLoaderEvent.rangeDateChanged(
startDate!,
endDate!,
),
);
},
),
),
),
),
@ -191,58 +208,6 @@ class _FinancePageState extends State<FinancePage>
);
},
),
);
}
Widget _buildPeriodSelector() {
return Container(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: AppColor.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppColor.border),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: selectedPeriod,
isExpanded: true,
icon: const Icon(
LineIcons.angleDown,
color: AppColor.primary,
),
style: AppStyle.md,
items: periods.map((String period) {
return DropdownMenuItem<String>(
value: period,
child: Text(period),
);
}).toList(),
onChanged: (String? newValue) {
setState(() {
selectedPeriod = newValue!;
});
},
),
),
),
),
const SizedBox(width: 12),
Container(
decoration: BoxDecoration(
color: AppColor.primary,
borderRadius: BorderRadius.circular(12),
),
child: IconButton(
onPressed: () {},
icon: const Icon(LineIcons.calendar, color: AppColor.white),
),
),
],
),
);
}