feat: dashboard analytic range date

This commit is contained in:
efrilm 2025-08-18 17:35:03 +07:00
parent aac3eecf95
commit 06d777290d
5 changed files with 345 additions and 71 deletions

View File

@ -24,6 +24,9 @@ class DashboardAnalyticLoaderBloc
Emitter<DashboardAnalyticLoaderState> emit,
) {
return event.map(
rangeDateChanged: (e) async {
emit(state.copyWith(dateFrom: e.dateFrom, dateTo: e.dateTo));
},
fetched: (e) async {
emit(
state.copyWith(
@ -33,8 +36,8 @@ class DashboardAnalyticLoaderBloc
);
final result = await _repository.getDashboard(
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 _$DashboardAnalyticLoaderEvent {
@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;
@ -74,6 +81,165 @@ class _$DashboardAnalyticLoaderEventCopyWithImpl<
/// 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
_$DashboardAnalyticLoaderEventCopyWithImpl<$Res, _$RangeDateChangedImpl>
implements _$$RangeDateChangedImplCopyWith<$Res> {
__$$RangeDateChangedImplCopyWithImpl(
_$RangeDateChangedImpl _value,
$Res Function(_$RangeDateChangedImpl) _then,
) : super(_value, _then);
/// Create a copy of DashboardAnalyticLoaderEvent
/// 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 'DashboardAnalyticLoaderEvent.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 DashboardAnalyticLoaderEvent
/// 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 DashboardAnalyticLoaderEvent {
const factory _RangeDateChanged(
final DateTime dateFrom,
final DateTime dateTo,
) = _$RangeDateChangedImpl;
DateTime get dateFrom;
DateTime get dateTo;
/// Create a copy of DashboardAnalyticLoaderEvent
/// 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(
@ -116,19 +282,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(),
}) {
@ -141,6 +315,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);
@ -149,6 +324,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);
@ -157,6 +333,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(),
}) {
@ -177,6 +354,8 @@ mixin _$DashboardAnalyticLoaderState {
Option<AnalyticFailure> get failureOptionDashboardAnalytic =>
throw _privateConstructorUsedError;
bool get isFetching => throw _privateConstructorUsedError;
DateTime get dateFrom => throw _privateConstructorUsedError;
DateTime get dateTo => throw _privateConstructorUsedError;
/// Create a copy of DashboardAnalyticLoaderState
/// with the given fields replaced by the non-null parameter values.
@ -200,6 +379,8 @@ abstract class $DashboardAnalyticLoaderStateCopyWith<$Res> {
DashboardAnalytic dashboardAnalytic,
Option<AnalyticFailure> failureOptionDashboardAnalytic,
bool isFetching,
DateTime dateFrom,
DateTime dateTo,
});
$DashboardAnalyticCopyWith<$Res> get dashboardAnalytic;
@ -226,6 +407,8 @@ class _$DashboardAnalyticLoaderStateCopyWithImpl<
Object? dashboardAnalytic = null,
Object? failureOptionDashboardAnalytic = null,
Object? isFetching = null,
Object? dateFrom = null,
Object? dateTo = null,
}) {
return _then(
_value.copyWith(
@ -242,6 +425,14 @@ class _$DashboardAnalyticLoaderStateCopyWithImpl<
? _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,
);
@ -271,6 +462,8 @@ abstract class _$$DashboardAnalyticLoaderStateImplCopyWith<$Res>
DashboardAnalytic dashboardAnalytic,
Option<AnalyticFailure> failureOptionDashboardAnalytic,
bool isFetching,
DateTime dateFrom,
DateTime dateTo,
});
@override
@ -298,6 +491,8 @@ class __$$DashboardAnalyticLoaderStateImplCopyWithImpl<$Res>
Object? dashboardAnalytic = null,
Object? failureOptionDashboardAnalytic = null,
Object? isFetching = null,
Object? dateFrom = null,
Object? dateTo = null,
}) {
return _then(
_$DashboardAnalyticLoaderStateImpl(
@ -313,6 +508,14 @@ class __$$DashboardAnalyticLoaderStateImplCopyWithImpl<$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,
),
);
}
@ -326,6 +529,8 @@ class _$DashboardAnalyticLoaderStateImpl
required this.dashboardAnalytic,
required this.failureOptionDashboardAnalytic,
this.isFetching = false,
required this.dateFrom,
required this.dateTo,
});
@override
@ -335,10 +540,14 @@ class _$DashboardAnalyticLoaderStateImpl
@override
@JsonKey()
final bool isFetching;
@override
final DateTime dateFrom;
@override
final DateTime dateTo;
@override
String toString() {
return 'DashboardAnalyticLoaderState(dashboardAnalytic: $dashboardAnalytic, failureOptionDashboardAnalytic: $failureOptionDashboardAnalytic, isFetching: $isFetching)';
return 'DashboardAnalyticLoaderState(dashboardAnalytic: $dashboardAnalytic, failureOptionDashboardAnalytic: $failureOptionDashboardAnalytic, isFetching: $isFetching, dateFrom: $dateFrom, dateTo: $dateTo)';
}
@override
@ -355,7 +564,10 @@ class _$DashboardAnalyticLoaderStateImpl
other.failureOptionDashboardAnalytic ==
failureOptionDashboardAnalytic) &&
(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
@ -364,6 +576,8 @@ class _$DashboardAnalyticLoaderStateImpl
dashboardAnalytic,
failureOptionDashboardAnalytic,
isFetching,
dateFrom,
dateTo,
);
/// Create a copy of DashboardAnalyticLoaderState
@ -386,6 +600,8 @@ abstract class _DashboardAnalyticLoaderState
required final DashboardAnalytic dashboardAnalytic,
required final Option<AnalyticFailure> failureOptionDashboardAnalytic,
final bool isFetching,
required final DateTime dateFrom,
required final DateTime dateTo,
}) = _$DashboardAnalyticLoaderStateImpl;
@override
@ -394,6 +610,10 @@ abstract class _DashboardAnalyticLoaderState
Option<AnalyticFailure> get failureOptionDashboardAnalytic;
@override
bool get isFetching;
@override
DateTime get dateFrom;
@override
DateTime get dateTo;
/// Create a copy of DashboardAnalyticLoaderState
/// with the given fields replaced by the non-null parameter values.

View File

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

View File

@ -6,11 +6,15 @@ class DashboardAnalyticLoaderState with _$DashboardAnalyticLoaderState {
required DashboardAnalytic dashboardAnalytic,
required Option<AnalyticFailure> failureOptionDashboardAnalytic,
@Default(false) bool isFetching,
required DateTime dateFrom,
required DateTime dateTo,
}) = _DashboardAnalyticLoaderState;
factory DashboardAnalyticLoaderState.initial() =>
DashboardAnalyticLoaderState(
dashboardAnalytic: DashboardAnalytic.empty(),
failureOptionDashboardAnalytic: none(),
dateFrom: DateTime.now().subtract(const Duration(days: 30)),
dateTo: DateTime.now(),
);
}

View File

@ -9,6 +9,7 @@ import '../../../common/theme/theme.dart';
import '../../../injection.dart';
import '../../components/appbar/appbar.dart';
import '../../components/button/button.dart';
import '../../components/field/date_range_picker_field.dart';
import '../../components/spacer/spacer.dart';
import 'widgets/payment_method.dart';
import 'widgets/quick_stats.dart';
@ -87,78 +88,120 @@ class _ReportPageState extends State<ReportPage> with TickerProviderStateMixin {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColor.background,
body:
BlocBuilder<
DashboardAnalyticLoaderBloc,
DashboardAnalyticLoaderState
>(
builder: (context, state) {
return CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 120,
floating: false,
pinned: true,
backgroundColor: AppColor.primary,
centerTitle: false,
flexibleSpace: CustomAppBar(
title: 'Laporan',
isBack: false,
return BlocListener<
DashboardAnalyticLoaderBloc,
DashboardAnalyticLoaderState
>(
listenWhen: (previous, current) =>
previous.dateFrom != current.dateFrom ||
previous.dateTo != current.dateTo,
listener: (context, state) {
context.read<DashboardAnalyticLoaderBloc>().add(
DashboardAnalyticLoaderEvent.fetched(),
);
},
child: Scaffold(
backgroundColor: AppColor.background,
body:
BlocBuilder<
DashboardAnalyticLoaderBloc,
DashboardAnalyticLoaderState
>(
builder: (context, state) {
return CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 120,
floating: false,
pinned: true,
backgroundColor: AppColor.primary,
centerTitle: false,
flexibleSpace: CustomAppBar(
title: 'Laporan',
isBack: false,
),
actions: [
ActionIconButton(
onTap: () {},
icon: LineIcons.download,
),
ActionIconButton(onTap: () {}, icon: LineIcons.filter),
SpaceWidth(8),
],
),
actions: [
ActionIconButton(onTap: () {}, icon: LineIcons.download),
ActionIconButton(onTap: () {}, icon: LineIcons.filter),
SpaceWidth(8),
],
),
// Content
SliverPadding(
padding: EdgeInsets.all(AppValue.padding),
sliver: SliverList(
delegate: SliverChildListDelegate([
FadeTransition(
SliverToBoxAdapter(
child: SlideTransition(
position: _slideAnimation,
child: FadeTransition(
opacity: _fadeAnimation,
child: SlideTransition(
position: _slideAnimation,
child: Column(
children: [
ReportRevenueSummary(
overview: state.dashboardAnalytic.overview,
rotationAnimation: _rotationAnimation,
),
const SpaceHeight(24),
ReportQuickStats(
overview: state.dashboardAnalytic.overview,
),
const SpaceHeight(24),
ReportSales(
salesData:
state.dashboardAnalytic.recentSales,
),
const SpaceHeight(24),
ReportPaymentMethod(
paymentMethods:
state.dashboardAnalytic.paymentMethods,
),
const SpaceHeight(24),
ReportTopProduct(
products: state.dashboardAnalytic.topProducts,
),
const SpaceHeight(24),
],
child: Padding(
padding: const EdgeInsets.all(16.0),
child: DateRangePickerField(
maxDate: DateTime.now(),
startDate: state.dateFrom,
endDate: state.dateTo,
onChanged: (startDate, endDate) {
context.read<DashboardAnalyticLoaderBloc>().add(
DashboardAnalyticLoaderEvent.rangeDateChanged(
startDate!,
endDate!,
),
);
},
),
),
),
]),
),
),
),
],
);
},
),
// Content
SliverPadding(
padding: EdgeInsets.all(AppValue.padding),
sliver: SliverList(
delegate: SliverChildListDelegate([
FadeTransition(
opacity: _fadeAnimation,
child: SlideTransition(
position: _slideAnimation,
child: Column(
children: [
ReportRevenueSummary(
overview: state.dashboardAnalytic.overview,
rotationAnimation: _rotationAnimation,
),
const SpaceHeight(24),
ReportQuickStats(
overview: state.dashboardAnalytic.overview,
),
const SpaceHeight(24),
ReportSales(
salesData:
state.dashboardAnalytic.recentSales,
),
const SpaceHeight(24),
ReportPaymentMethod(
paymentMethods:
state.dashboardAnalytic.paymentMethods,
),
const SpaceHeight(24),
ReportTopProduct(
products:
state.dashboardAnalytic.topProducts,
),
const SpaceHeight(24),
],
),
),
),
]),
),
),
],
);
},
),
),
);
}
}