feat: category report

This commit is contained in:
efrilm 2025-08-15 17:07:29 +07:00
parent 79585b253d
commit 7007f39766
7 changed files with 1261 additions and 1 deletions

View File

@ -16,6 +16,7 @@ import 'package:enaklo_pos/presentation/home/bloc/outlet_loader/outlet_loader_bl
import 'package:enaklo_pos/presentation/home/bloc/product_loader/product_loader_bloc.dart'; import 'package:enaklo_pos/presentation/home/bloc/product_loader/product_loader_bloc.dart';
import 'package:enaklo_pos/presentation/home/bloc/user_update_outlet/user_update_outlet_bloc.dart'; import 'package:enaklo_pos/presentation/home/bloc/user_update_outlet/user_update_outlet_bloc.dart';
import 'package:enaklo_pos/presentation/refund/bloc/refund_bloc.dart'; import 'package:enaklo_pos/presentation/refund/bloc/refund_bloc.dart';
import 'package:enaklo_pos/presentation/report/blocs/category_report/category_report_bloc.dart';
import 'package:enaklo_pos/presentation/report/blocs/inventory_report/inventory_report_bloc.dart'; import 'package:enaklo_pos/presentation/report/blocs/inventory_report/inventory_report_bloc.dart';
import 'package:enaklo_pos/presentation/report/blocs/profit_loss/profit_loss_bloc.dart'; import 'package:enaklo_pos/presentation/report/blocs/profit_loss/profit_loss_bloc.dart';
import 'package:enaklo_pos/presentation/report/blocs/report/report_bloc.dart'; import 'package:enaklo_pos/presentation/report/blocs/report/report_bloc.dart';
@ -296,6 +297,9 @@ class _MyAppState extends State<MyApp> {
BlocProvider( BlocProvider(
create: (context) => InventoryReportBloc(AnalyticRemoteDatasource()), create: (context) => InventoryReportBloc(AnalyticRemoteDatasource()),
), ),
BlocProvider(
create: (context) => CategoryReportBloc(AnalyticRemoteDatasource()),
),
], ],
child: MaterialApp( child: MaterialApp(
navigatorKey: AuthInterceptor.navigatorKey, navigatorKey: AuthInterceptor.navigatorKey,

View File

@ -0,0 +1,28 @@
import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/datasources/analytic_remote_datasource.dart';
import 'package:enaklo_pos/data/models/response/category_analytic_response_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'category_report_event.dart';
part 'category_report_state.dart';
part 'category_report_bloc.freezed.dart';
class CategoryReportBloc
extends Bloc<CategoryReportEvent, CategoryReportState> {
final AnalyticRemoteDatasource _datasource;
CategoryReportBloc(this._datasource) : super(CategoryReportState.initial()) {
on<_Get>((event, emit) async {
emit(_Loading());
final result = await _datasource.getCategory(
dateFrom: event.startDate,
dateTo: event.endDate,
);
result.fold(
(l) => emit(_Error(l)),
(r) => emit(_Loaded(r.data!)),
);
});
}
}

View File

@ -0,0 +1,861 @@
// 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_report_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 _$CategoryReportEvent {
DateTime get startDate => throw _privateConstructorUsedError;
DateTime get endDate => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(DateTime startDate, DateTime endDate) get,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(DateTime startDate, DateTime endDate)? get,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(DateTime startDate, DateTime endDate)? get,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Get value) get,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Get value)? get,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Get value)? get,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
/// Create a copy of CategoryReportEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$CategoryReportEventCopyWith<CategoryReportEvent> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $CategoryReportEventCopyWith<$Res> {
factory $CategoryReportEventCopyWith(
CategoryReportEvent value, $Res Function(CategoryReportEvent) then) =
_$CategoryReportEventCopyWithImpl<$Res, CategoryReportEvent>;
@useResult
$Res call({DateTime startDate, DateTime endDate});
}
/// @nodoc
class _$CategoryReportEventCopyWithImpl<$Res, $Val extends CategoryReportEvent>
implements $CategoryReportEventCopyWith<$Res> {
_$CategoryReportEventCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of CategoryReportEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? startDate = null,
Object? endDate = null,
}) {
return _then(_value.copyWith(
startDate: null == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime,
endDate: null == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime,
) as $Val);
}
}
/// @nodoc
abstract class _$$GetImplCopyWith<$Res>
implements $CategoryReportEventCopyWith<$Res> {
factory _$$GetImplCopyWith(_$GetImpl value, $Res Function(_$GetImpl) then) =
__$$GetImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({DateTime startDate, DateTime endDate});
}
/// @nodoc
class __$$GetImplCopyWithImpl<$Res>
extends _$CategoryReportEventCopyWithImpl<$Res, _$GetImpl>
implements _$$GetImplCopyWith<$Res> {
__$$GetImplCopyWithImpl(_$GetImpl _value, $Res Function(_$GetImpl) _then)
: super(_value, _then);
/// Create a copy of CategoryReportEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? startDate = null,
Object? endDate = null,
}) {
return _then(_$GetImpl(
startDate: null == startDate
? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable
as DateTime,
endDate: null == endDate
? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable
as DateTime,
));
}
}
/// @nodoc
class _$GetImpl implements _Get {
const _$GetImpl({required this.startDate, required this.endDate});
@override
final DateTime startDate;
@override
final DateTime endDate;
@override
String toString() {
return 'CategoryReportEvent.get(startDate: $startDate, endDate: $endDate)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$GetImpl &&
(identical(other.startDate, startDate) ||
other.startDate == startDate) &&
(identical(other.endDate, endDate) || other.endDate == endDate));
}
@override
int get hashCode => Object.hash(runtimeType, startDate, endDate);
/// Create a copy of CategoryReportEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$GetImplCopyWith<_$GetImpl> get copyWith =>
__$$GetImplCopyWithImpl<_$GetImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(DateTime startDate, DateTime endDate) get,
}) {
return get(startDate, endDate);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(DateTime startDate, DateTime endDate)? get,
}) {
return get?.call(startDate, endDate);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(DateTime startDate, DateTime endDate)? get,
required TResult orElse(),
}) {
if (get != null) {
return get(startDate, endDate);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Get value) get,
}) {
return get(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Get value)? get,
}) {
return get?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Get value)? get,
required TResult orElse(),
}) {
if (get != null) {
return get(this);
}
return orElse();
}
}
abstract class _Get implements CategoryReportEvent {
const factory _Get(
{required final DateTime startDate,
required final DateTime endDate}) = _$GetImpl;
@override
DateTime get startDate;
@override
DateTime get endDate;
/// Create a copy of CategoryReportEvent
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$GetImplCopyWith<_$GetImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$CategoryReportState {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(CategoryAnalyticData data) loaded,
required TResult Function(String message) error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(CategoryAnalyticData data)? loaded,
TResult? Function(String message)? error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(CategoryAnalyticData data)? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Loaded value) loaded,
required TResult Function(_Error value) error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Loaded value)? loaded,
TResult? Function(_Error value)? error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Loaded value)? loaded,
TResult Function(_Error value)? error,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $CategoryReportStateCopyWith<$Res> {
factory $CategoryReportStateCopyWith(
CategoryReportState value, $Res Function(CategoryReportState) then) =
_$CategoryReportStateCopyWithImpl<$Res, CategoryReportState>;
}
/// @nodoc
class _$CategoryReportStateCopyWithImpl<$Res, $Val extends CategoryReportState>
implements $CategoryReportStateCopyWith<$Res> {
_$CategoryReportStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of CategoryReportState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
abstract class _$$InitialImplCopyWith<$Res> {
factory _$$InitialImplCopyWith(
_$InitialImpl value, $Res Function(_$InitialImpl) then) =
__$$InitialImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$InitialImplCopyWithImpl<$Res>
extends _$CategoryReportStateCopyWithImpl<$Res, _$InitialImpl>
implements _$$InitialImplCopyWith<$Res> {
__$$InitialImplCopyWithImpl(
_$InitialImpl _value, $Res Function(_$InitialImpl) _then)
: super(_value, _then);
/// Create a copy of CategoryReportState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$InitialImpl implements _Initial {
const _$InitialImpl();
@override
String toString() {
return 'CategoryReportState.initial()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$InitialImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(CategoryAnalyticData data) loaded,
required TResult Function(String message) error,
}) {
return initial();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(CategoryAnalyticData data)? loaded,
TResult? Function(String message)? error,
}) {
return initial?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(CategoryAnalyticData data)? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) {
if (initial != null) {
return initial();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Loaded value) loaded,
required TResult Function(_Error value) error,
}) {
return initial(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Loaded value)? loaded,
TResult? Function(_Error value)? error,
}) {
return initial?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Loaded value)? loaded,
TResult Function(_Error value)? error,
required TResult orElse(),
}) {
if (initial != null) {
return initial(this);
}
return orElse();
}
}
abstract class _Initial implements CategoryReportState {
const factory _Initial() = _$InitialImpl;
}
/// @nodoc
abstract class _$$LoadingImplCopyWith<$Res> {
factory _$$LoadingImplCopyWith(
_$LoadingImpl value, $Res Function(_$LoadingImpl) then) =
__$$LoadingImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$LoadingImplCopyWithImpl<$Res>
extends _$CategoryReportStateCopyWithImpl<$Res, _$LoadingImpl>
implements _$$LoadingImplCopyWith<$Res> {
__$$LoadingImplCopyWithImpl(
_$LoadingImpl _value, $Res Function(_$LoadingImpl) _then)
: super(_value, _then);
/// Create a copy of CategoryReportState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$LoadingImpl implements _Loading {
const _$LoadingImpl();
@override
String toString() {
return 'CategoryReportState.loading()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$LoadingImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(CategoryAnalyticData data) loaded,
required TResult Function(String message) error,
}) {
return loading();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(CategoryAnalyticData data)? loaded,
TResult? Function(String message)? error,
}) {
return loading?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(CategoryAnalyticData data)? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) {
if (loading != null) {
return loading();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Loaded value) loaded,
required TResult Function(_Error value) error,
}) {
return loading(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Loaded value)? loaded,
TResult? Function(_Error value)? error,
}) {
return loading?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Loaded value)? loaded,
TResult Function(_Error value)? error,
required TResult orElse(),
}) {
if (loading != null) {
return loading(this);
}
return orElse();
}
}
abstract class _Loading implements CategoryReportState {
const factory _Loading() = _$LoadingImpl;
}
/// @nodoc
abstract class _$$LoadedImplCopyWith<$Res> {
factory _$$LoadedImplCopyWith(
_$LoadedImpl value, $Res Function(_$LoadedImpl) then) =
__$$LoadedImplCopyWithImpl<$Res>;
@useResult
$Res call({CategoryAnalyticData data});
}
/// @nodoc
class __$$LoadedImplCopyWithImpl<$Res>
extends _$CategoryReportStateCopyWithImpl<$Res, _$LoadedImpl>
implements _$$LoadedImplCopyWith<$Res> {
__$$LoadedImplCopyWithImpl(
_$LoadedImpl _value, $Res Function(_$LoadedImpl) _then)
: super(_value, _then);
/// Create a copy of CategoryReportState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? data = null,
}) {
return _then(_$LoadedImpl(
null == data
? _value.data
: data // ignore: cast_nullable_to_non_nullable
as CategoryAnalyticData,
));
}
}
/// @nodoc
class _$LoadedImpl implements _Loaded {
const _$LoadedImpl(this.data);
@override
final CategoryAnalyticData data;
@override
String toString() {
return 'CategoryReportState.loaded(data: $data)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$LoadedImpl &&
(identical(other.data, data) || other.data == data));
}
@override
int get hashCode => Object.hash(runtimeType, data);
/// Create a copy of CategoryReportState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$LoadedImplCopyWith<_$LoadedImpl> get copyWith =>
__$$LoadedImplCopyWithImpl<_$LoadedImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(CategoryAnalyticData data) loaded,
required TResult Function(String message) error,
}) {
return loaded(data);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(CategoryAnalyticData data)? loaded,
TResult? Function(String message)? error,
}) {
return loaded?.call(data);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(CategoryAnalyticData data)? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) {
if (loaded != null) {
return loaded(data);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Loaded value) loaded,
required TResult Function(_Error value) error,
}) {
return loaded(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Loaded value)? loaded,
TResult? Function(_Error value)? error,
}) {
return loaded?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Loaded value)? loaded,
TResult Function(_Error value)? error,
required TResult orElse(),
}) {
if (loaded != null) {
return loaded(this);
}
return orElse();
}
}
abstract class _Loaded implements CategoryReportState {
const factory _Loaded(final CategoryAnalyticData data) = _$LoadedImpl;
CategoryAnalyticData get data;
/// Create a copy of CategoryReportState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$LoadedImplCopyWith<_$LoadedImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$ErrorImplCopyWith<$Res> {
factory _$$ErrorImplCopyWith(
_$ErrorImpl value, $Res Function(_$ErrorImpl) then) =
__$$ErrorImplCopyWithImpl<$Res>;
@useResult
$Res call({String message});
}
/// @nodoc
class __$$ErrorImplCopyWithImpl<$Res>
extends _$CategoryReportStateCopyWithImpl<$Res, _$ErrorImpl>
implements _$$ErrorImplCopyWith<$Res> {
__$$ErrorImplCopyWithImpl(
_$ErrorImpl _value, $Res Function(_$ErrorImpl) _then)
: super(_value, _then);
/// Create a copy of CategoryReportState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? message = null,
}) {
return _then(_$ErrorImpl(
null == message
? _value.message
: message // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class _$ErrorImpl implements _Error {
const _$ErrorImpl(this.message);
@override
final String message;
@override
String toString() {
return 'CategoryReportState.error(message: $message)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ErrorImpl &&
(identical(other.message, message) || other.message == message));
}
@override
int get hashCode => Object.hash(runtimeType, message);
/// Create a copy of CategoryReportState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$ErrorImplCopyWith<_$ErrorImpl> get copyWith =>
__$$ErrorImplCopyWithImpl<_$ErrorImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(CategoryAnalyticData data) loaded,
required TResult Function(String message) error,
}) {
return error(message);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(CategoryAnalyticData data)? loaded,
TResult? Function(String message)? error,
}) {
return error?.call(message);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(CategoryAnalyticData data)? loaded,
TResult Function(String message)? error,
required TResult orElse(),
}) {
if (error != null) {
return error(message);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Initial value) initial,
required TResult Function(_Loading value) loading,
required TResult Function(_Loaded value) loaded,
required TResult Function(_Error value) error,
}) {
return error(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Initial value)? initial,
TResult? Function(_Loading value)? loading,
TResult? Function(_Loaded value)? loaded,
TResult? Function(_Error value)? error,
}) {
return error?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Initial value)? initial,
TResult Function(_Loading value)? loading,
TResult Function(_Loaded value)? loaded,
TResult Function(_Error value)? error,
required TResult orElse(),
}) {
if (error != null) {
return error(this);
}
return orElse();
}
}
abstract class _Error implements CategoryReportState {
const factory _Error(final String message) = _$ErrorImpl;
String get message;
/// Create a copy of CategoryReportState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$ErrorImplCopyWith<_$ErrorImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,9 @@
part of 'category_report_bloc.dart';
@freezed
class CategoryReportEvent with _$CategoryReportEvent {
const factory CategoryReportEvent.get({
required DateTime startDate,
required DateTime endDate,
}) = _Get;
}

View File

@ -0,0 +1,9 @@
part of 'category_report_bloc.dart';
@freezed
class CategoryReportState with _$CategoryReportState {
const factory CategoryReportState.initial() = _Initial;
const factory CategoryReportState.loading() = _Loading;
const factory CategoryReportState.loaded(CategoryAnalyticData data) = _Loaded;
const factory CategoryReportState.error(String message) = _Error;
}

View File

@ -5,9 +5,11 @@ import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
import 'package:enaklo_pos/core/utils/helper_pdf_service.dart'; import 'package:enaklo_pos/core/utils/helper_pdf_service.dart';
import 'package:enaklo_pos/core/utils/permession_handler.dart'; import 'package:enaklo_pos/core/utils/permession_handler.dart';
import 'package:enaklo_pos/core/utils/transaction_report.dart'; import 'package:enaklo_pos/core/utils/transaction_report.dart';
import 'package:enaklo_pos/presentation/report/blocs/category_report/category_report_bloc.dart';
import 'package:enaklo_pos/presentation/report/blocs/inventory_report/inventory_report_bloc.dart'; import 'package:enaklo_pos/presentation/report/blocs/inventory_report/inventory_report_bloc.dart';
import 'package:enaklo_pos/presentation/report/blocs/profit_loss/profit_loss_bloc.dart'; import 'package:enaklo_pos/presentation/report/blocs/profit_loss/profit_loss_bloc.dart';
import 'package:enaklo_pos/presentation/report/blocs/report/report_bloc.dart'; import 'package:enaklo_pos/presentation/report/blocs/report/report_bloc.dart';
import 'package:enaklo_pos/presentation/report/widgets/category_report_widget.dart';
import 'package:enaklo_pos/presentation/report/widgets/dashboard_analytic_widget.dart'; import 'package:enaklo_pos/presentation/report/widgets/dashboard_analytic_widget.dart';
import 'package:enaklo_pos/presentation/report/widgets/inventory_report_widget.dart'; import 'package:enaklo_pos/presentation/report/widgets/inventory_report_widget.dart';
import 'package:enaklo_pos/presentation/report/widgets/profit_loss_widget.dart'; import 'package:enaklo_pos/presentation/report/widgets/profit_loss_widget.dart';
@ -111,6 +113,15 @@ class _ReportPageState extends State<ReportPage> {
), ),
); );
} }
if (selectedMenu == 7) {
context.read<CategoryReportBloc>().add(
CategoryReportEvent.get(
startDate: fromDate,
endDate: toDate,
),
);
}
} }
@override @override
@ -346,6 +357,23 @@ class _ReportPageState extends State<ReportPage> {
}, },
isActive: selectedMenu == 6, isActive: selectedMenu == 6,
), ),
ReportMenu(
label: 'Laporan Kategori',
subtitle: 'Laporan kategori produk',
icon: Icons.archive_outlined,
onPressed: () {
selectedMenu = 7;
title = 'Laporan Kategori';
setState(() {});
context.read<CategoryReportBloc>().add(
CategoryReportEvent.get(
startDate: fromDate,
endDate: toDate,
),
);
},
isActive: selectedMenu == 7,
),
], ],
), ),
), ),
@ -521,6 +549,35 @@ class _ReportPageState extends State<ReportPage> {
); );
}, },
) )
: selectedMenu == 7
? BlocBuilder<
CategoryReportBloc,
CategoryReportState>(
builder:
(context, state) {
return state
.maybeWhen(
orElse: () =>
const Center(
child:
CircularProgressIndicator(),
),
error: (message) {
return Text(
message);
},
loaded: (data) {
return CategoryReportWidget(
title: title,
searchDateFormatted:
searchDateFormatted,
categoryAnalyticData:
data,
);
},
);
},
)
: const SizedBox.shrink()), : const SizedBox.shrink()),
], ],
), ),

View File

@ -0,0 +1,292 @@
import 'package:enaklo_pos/core/constants/colors.dart';
import 'package:enaklo_pos/data/models/response/category_analytic_response_model.dart';
import 'package:flutter/material.dart';
class CategoryReportWidget extends StatelessWidget {
final String title;
final String searchDateFormatted;
final CategoryAnalyticData categoryAnalyticData;
const CategoryReportWidget({
super.key,
required this.title,
required this.searchDateFormatted,
required this.categoryAnalyticData,
});
String formatCurrency(int amount) {
return 'Rp ${amount.toString().replaceAllMapped(
RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
(Match m) => '${m[1]}.',
)}';
}
int getTotalRevenue() {
return categoryAnalyticData.data
.fold(0, (sum, item) => sum + item.totalRevenue);
}
int getTotalQuantity() {
return categoryAnalyticData.data
.fold(0, (sum, item) => sum + item.totalQuantity);
}
int getTotalOrders() {
return categoryAnalyticData.data
.fold(0, (sum, item) => sum + item.orderCount);
}
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
color: AppColors.background,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header Section
Container(
width: double.infinity,
color: AppColors.primary,
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: AppColors.whiteText,
),
),
const SizedBox(height: 8),
Text(
'Periode: $searchDateFormatted',
style: TextStyle(
fontSize: 14,
color: AppColors.whiteText.withOpacity(0.8),
),
),
],
),
),
// Summary Cards Section
Container(
color: AppColors.white,
padding: const EdgeInsets.all(20),
child: Row(
children: [
Expanded(
child: _buildSummaryCard(
'Total Pendapatan',
formatCurrency(getTotalRevenue()),
AppColors.green,
),
),
const SizedBox(width: 16),
Expanded(
child: _buildSummaryCard(
'Total Item Terjual',
'${getTotalQuantity()} pcs',
AppColors.subtitle,
),
),
const SizedBox(width: 16),
Expanded(
child: _buildSummaryCard(
'Total Pesanan',
'${getTotalOrders()}',
AppColors.primary,
),
),
],
),
),
// Categories List Header
Container(
color: AppColors.stroke,
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Row(
children: [
Expanded(
flex: 3,
child: Text(
'Kategori',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.black,
),
),
),
Expanded(
flex: 2,
child: Text(
'Pendapatan',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.black,
),
),
),
Expanded(
flex: 1,
child: Text(
'Qty',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.black,
),
),
),
Expanded(
flex: 1,
child: Text(
'Order',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.black,
),
),
),
],
),
),
// Categories List
Expanded(
child: Container(
color: AppColors.white,
child: ListView.builder(
padding: EdgeInsets.zero,
itemCount: categoryAnalyticData.data.length,
itemBuilder: (context, index) {
return _buildCategoryItem(
categoryAnalyticData.data[index], index);
},
),
),
),
],
),
);
}
Widget _buildSummaryCard(String title, String value, Color color) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColors.light,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppColors.stroke, width: 1),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 12,
color: AppColors.subtitle,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 8),
Text(
value,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: color,
),
),
],
),
);
}
Widget _buildCategoryItem(CategoryAnalyticItem category, int index) {
final isEven = index % 2 == 0;
return Container(
color: isEven ? AppColors.white : AppColors.light.withOpacity(0.3),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Row(
children: [
Expanded(
flex: 3,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
category.categoryName,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.black,
),
),
const SizedBox(height: 4),
Text(
'${category.productCount} produk',
style: TextStyle(
fontSize: 12,
color: AppColors.subtitle,
),
),
],
),
),
Expanded(
flex: 2,
child: Column(
children: [
Text(
formatCurrency(category.totalRevenue),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: AppColors.green,
),
),
],
),
),
Expanded(
flex: 1,
child: Text(
'${category.totalQuantity}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.black,
),
),
),
Expanded(
flex: 1,
child: Text(
'${category.orderCount}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.black,
),
),
),
],
),
);
}
}