Compare commits

...

2 Commits

Author SHA1 Message Date
efrilm
2a457ea5f6 feat: item sales report 2025-08-06 12:06:34 +07:00
efrilm
fd254c22fd feat: payment method report 2025-08-06 11:23:03 +07:00
15 changed files with 1416 additions and 366 deletions

View File

@ -0,0 +1,82 @@
import 'dart:developer';
import 'package:dartz/dartz.dart';
import 'package:dio/dio.dart';
import 'package:enaklo_pos/core/constants/variables.dart';
import 'package:enaklo_pos/core/network/dio_client.dart';
import 'package:enaklo_pos/data/datasources/auth_local_datasource.dart';
import 'package:enaklo_pos/data/models/response/payment_method_analytic_response_model.dart';
import 'package:enaklo_pos/data/models/response/sales_analytic_response_model.dart';
import 'package:intl/intl.dart';
class AnalyticRemoteDatasource {
final Dio dio = DioClient.instance;
Future<Either<String, PaymentMethodAnalyticResponseModel>> getPaymentMethod({
required DateTime dateFrom,
required DateTime dateTo,
}) async {
final authData = await AuthLocalDataSource().getAuthData();
final headers = {
'Authorization': 'Bearer ${authData.token}',
'Accept': 'application/json',
};
try {
final response = await dio.get(
'${Variables.baseUrl}/api/v1/analytics/payment-methods',
queryParameters: {
'date_from': DateFormat('dd-MM-yyyy').format(dateFrom),
'date_to': DateFormat('dd-MM-yyyy').format(dateTo),
},
options: Options(headers: headers),
);
if (response.statusCode == 200) {
return right(PaymentMethodAnalyticResponseModel.fromMap(response.data));
} else {
return left('Terjadi Kesalahan, Coba lagi nanti.');
}
} on DioException catch (e) {
log('Dio error: ${e.message}');
return left(e.response?.data.toString() ?? e.message ?? 'Unknown error');
} catch (e) {
log('Unexpected error: $e');
return left('Unexpected error occurred');
}
}
Future<Either<String, SalesAnalyticResponseModel>> getSales({
required DateTime dateFrom,
required DateTime dateTo,
}) async {
final authData = await AuthLocalDataSource().getAuthData();
final headers = {
'Authorization': 'Bearer ${authData.token}',
'Accept': 'application/json',
};
try {
final response = await dio.get(
'${Variables.baseUrl}/api/v1/analytics/sales',
queryParameters: {
'date_from': DateFormat('dd-MM-yyyy').format(dateFrom),
'date_to': DateFormat('dd-MM-yyyy').format(dateTo),
},
options: Options(headers: headers),
);
if (response.statusCode == 200) {
return right(SalesAnalyticResponseModel.fromMap(response.data));
} else {
return left('Terjadi Kesalahan, Coba lagi nanti.');
}
} on DioException catch (e) {
log('Dio error: ${e.message}');
return left(e.response?.data.toString() ?? e.message ?? 'Unknown error');
} catch (e) {
log('Unexpected error: $e');
return left('Unexpected error occurred');
}
}
}

View File

@ -0,0 +1,170 @@
class PaymentMethodAnalyticResponseModel {
final bool success;
final PaymentMethodAnalyticData data;
final dynamic errors;
PaymentMethodAnalyticResponseModel({
required this.success,
required this.data,
this.errors,
});
factory PaymentMethodAnalyticResponseModel.fromJson(
Map<String, dynamic> json) =>
PaymentMethodAnalyticResponseModel.fromMap(json);
Map<String, dynamic> toJson() => toMap();
factory PaymentMethodAnalyticResponseModel.fromMap(Map<String, dynamic> map) {
return PaymentMethodAnalyticResponseModel(
success: map['success'],
data: PaymentMethodAnalyticData.fromMap(map['data']),
errors: map['errors'],
);
}
Map<String, dynamic> toMap() {
return {
'success': success,
'data': data.toMap(),
'errors': errors,
};
}
}
class PaymentMethodAnalyticData {
final String organizationId;
final String outletId;
final DateTime dateFrom;
final DateTime dateTo;
final String groupBy;
final PaymentSummary summary;
final List<PaymentMethodAnalyticItem> data;
PaymentMethodAnalyticData({
required this.organizationId,
required this.outletId,
required this.dateFrom,
required this.dateTo,
required this.groupBy,
required this.summary,
required this.data,
});
factory PaymentMethodAnalyticData.fromJson(Map<String, dynamic> json) =>
PaymentMethodAnalyticData.fromMap(json);
Map<String, dynamic> toJson() => toMap();
factory PaymentMethodAnalyticData.fromMap(Map<String, dynamic> map) {
return PaymentMethodAnalyticData(
organizationId: map['organization_id'],
outletId: map['outlet_id'],
dateFrom: DateTime.parse(map['date_from']),
dateTo: DateTime.parse(map['date_to']),
groupBy: map['group_by'],
summary: PaymentSummary.fromMap(map['summary']),
data: List<PaymentMethodAnalyticItem>.from(
map['data']?.map((x) => PaymentMethodAnalyticItem.fromMap(x)) ?? [],
),
);
}
Map<String, dynamic> toMap() {
return {
'organization_id': organizationId,
'outlet_id': outletId,
'date_from': dateFrom.toIso8601String(),
'date_to': dateTo.toIso8601String(),
'group_by': groupBy,
'summary': summary.toMap(),
'data': data.map((x) => x.toMap()).toList(),
};
}
}
class PaymentSummary {
final int totalAmount;
final int totalOrders;
final int totalPayments;
final double averageOrderValue;
PaymentSummary({
required this.totalAmount,
required this.totalOrders,
required this.totalPayments,
required this.averageOrderValue,
});
factory PaymentSummary.fromJson(Map<String, dynamic> json) =>
PaymentSummary.fromMap(json);
Map<String, dynamic> toJson() => toMap();
factory PaymentSummary.fromMap(Map<String, dynamic> map) {
return PaymentSummary(
totalAmount: map['total_amount'],
totalOrders: map['total_orders'],
totalPayments: map['total_payments'],
averageOrderValue: (map['average_order_value'] as num).toDouble(),
);
}
Map<String, dynamic> toMap() {
return {
'total_amount': totalAmount,
'total_orders': totalOrders,
'total_payments': totalPayments,
'average_order_value': averageOrderValue,
};
}
}
class PaymentMethodAnalyticItem {
final String paymentMethodId;
final String paymentMethodName;
final String paymentMethodType;
final int totalAmount;
final int orderCount;
final int paymentCount;
final int percentage;
PaymentMethodAnalyticItem({
required this.paymentMethodId,
required this.paymentMethodName,
required this.paymentMethodType,
required this.totalAmount,
required this.orderCount,
required this.paymentCount,
required this.percentage,
});
factory PaymentMethodAnalyticItem.fromJson(Map<String, dynamic> json) =>
PaymentMethodAnalyticItem.fromMap(json);
Map<String, dynamic> toJson() => toMap();
factory PaymentMethodAnalyticItem.fromMap(Map<String, dynamic> map) {
return PaymentMethodAnalyticItem(
paymentMethodId: map['payment_method_id'],
paymentMethodName: map['payment_method_name'],
paymentMethodType: map['payment_method_type'],
totalAmount: map['total_amount'],
orderCount: map['order_count'],
paymentCount: map['payment_count'],
percentage: map['percentage'],
);
}
Map<String, dynamic> toMap() {
return {
'payment_method_id': paymentMethodId,
'payment_method_name': paymentMethodName,
'payment_method_type': paymentMethodType,
'total_amount': totalAmount,
'order_count': orderCount,
'payment_count': paymentCount,
'percentage': percentage,
};
}
}

View File

@ -0,0 +1,195 @@
class SalesAnalyticResponseModel {
final bool success;
final SalesAnalyticData data;
final dynamic errors;
SalesAnalyticResponseModel({
required this.success,
required this.data,
this.errors,
});
factory SalesAnalyticResponseModel.fromJson(Map<String, dynamic> json) =>
SalesAnalyticResponseModel.fromMap(json);
Map<String, dynamic> toJson() => toMap();
factory SalesAnalyticResponseModel.fromMap(Map<String, dynamic> map) {
return SalesAnalyticResponseModel(
success: map['success'],
data: SalesAnalyticData.fromMap(map['data']),
errors: map['errors'],
);
}
Map<String, dynamic> toMap() {
return {
'success': success,
'data': data.toMap(),
'errors': errors,
};
}
}
class SalesAnalyticData {
final String organizationId;
final String outletId;
final DateTime dateFrom;
final DateTime dateTo;
final String groupBy;
final SalesSummary summary;
final List<SalesAnalyticItem> data;
SalesAnalyticData({
required this.organizationId,
required this.outletId,
required this.dateFrom,
required this.dateTo,
required this.groupBy,
required this.summary,
required this.data,
});
factory SalesAnalyticData.fromJson(Map<String, dynamic> json) =>
SalesAnalyticData.fromMap(json);
Map<String, dynamic> toJson() => toMap();
factory SalesAnalyticData.fromMap(Map<String, dynamic> map) {
return SalesAnalyticData(
organizationId: map['organization_id'],
outletId: map['outlet_id'],
dateFrom: DateTime.parse(map['date_from']),
dateTo: DateTime.parse(map['date_to']),
groupBy: map['group_by'],
summary: SalesSummary.fromMap(map['summary']),
data: List<SalesAnalyticItem>.from(
map['data']?.map((x) => SalesAnalyticItem.fromMap(x)) ?? [],
),
);
}
Map<String, dynamic> toMap() {
return {
'organization_id': organizationId,
'outlet_id': outletId,
'date_from': dateFrom.toIso8601String(),
'date_to': dateTo.toIso8601String(),
'group_by': groupBy,
'summary': summary.toMap(),
'data': data.map((x) => x.toMap()).toList(),
};
}
}
class SalesSummary {
final int totalSales;
final int totalOrders;
final int totalItems;
final double averageOrderValue;
final int totalTax;
final int totalDiscount;
final int netSales;
SalesSummary({
required this.totalSales,
required this.totalOrders,
required this.totalItems,
required this.averageOrderValue,
required this.totalTax,
required this.totalDiscount,
required this.netSales,
});
factory SalesSummary.fromJson(Map<String, dynamic> json) =>
SalesSummary.fromMap(json);
Map<String, dynamic> toJson() => toMap();
factory SalesSummary.fromMap(Map<String, dynamic> map) {
return SalesSummary(
totalSales: map['total_sales'],
totalOrders: map['total_orders'],
totalItems: map['total_items'],
averageOrderValue: (map['average_order_value'] as num).toDouble(),
totalTax: map['total_tax'],
totalDiscount: map['total_discount'],
netSales: map['net_sales'],
);
}
Map<String, dynamic> toMap() {
return {
'total_sales': totalSales,
'total_orders': totalOrders,
'total_items': totalItems,
'average_order_value': averageOrderValue,
'total_tax': totalTax,
'total_discount': totalDiscount,
'net_sales': netSales,
};
}
}
class SalesAnalyticItem {
final DateTime date;
final int sales;
final int orders;
final int items;
final int tax;
final int discount;
final int netSales;
SalesAnalyticItem({
required this.date,
required this.sales,
required this.orders,
required this.items,
required this.tax,
required this.discount,
required this.netSales,
});
factory SalesAnalyticItem.fromJson(Map<String, dynamic> json) =>
SalesAnalyticItem.fromMap(json);
Map<String, dynamic> toJson() => toMap();
factory SalesAnalyticItem.fromMap(Map<String, dynamic> map) {
return SalesAnalyticItem(
date: DateTime.parse(map['date']),
sales: map['sales'],
orders: map['orders'],
items: map['items'],
tax: map['tax'],
discount: map['discount'],
netSales: map['net_sales'],
);
}
Map<String, dynamic> toMap() {
return {
'date': date.toIso8601String(),
'sales': sales,
'orders': orders,
'items': items,
'tax': tax,
'discount': discount,
'net_sales': netSales,
};
}
}
class SalesInsights {
final List<SalesAnalyticItem> originalData;
final List<SalesAnalyticItem> sortedDailyData;
final SalesAnalyticItem? highestRevenueDay;
final SalesSummary summary;
SalesInsights({
required this.originalData,
required this.sortedDailyData,
required this.highestRevenueDay,
required this.summary,
});
}

View File

@ -1,5 +1,6 @@
import 'dart:developer'; import 'dart:developer';
import 'package:enaklo_pos/core/constants/theme.dart'; import 'package:enaklo_pos/core/constants/theme.dart';
import 'package:enaklo_pos/data/datasources/analytic_remote_datasource.dart';
import 'package:enaklo_pos/data/datasources/customer_remote_datasource.dart'; import 'package:enaklo_pos/data/datasources/customer_remote_datasource.dart';
import 'package:enaklo_pos/data/datasources/file_remote_datasource.dart'; import 'package:enaklo_pos/data/datasources/file_remote_datasource.dart';
import 'package:enaklo_pos/data/datasources/outlet_remote_data_source.dart'; import 'package:enaklo_pos/data/datasources/outlet_remote_data_source.dart';
@ -195,10 +196,11 @@ class _MyAppState extends State<MyApp> {
create: (context) => ProductSalesBloc(OrderItemRemoteDatasource()), create: (context) => ProductSalesBloc(OrderItemRemoteDatasource()),
), ),
BlocProvider( BlocProvider(
create: (context) => ItemSalesReportBloc(OrderItemRemoteDatasource()), create: (context) => ItemSalesReportBloc(AnalyticRemoteDatasource()),
), ),
BlocProvider( BlocProvider(
create: (context) => PaymentMethodReportBloc(OrderRemoteDatasource()), create: (context) =>
PaymentMethodReportBloc(AnalyticRemoteDatasource()),
), ),
BlocProvider( BlocProvider(
create: (context) => DaySalesBloc(ProductLocalDatasource.instance), create: (context) => DaySalesBloc(ProductLocalDatasource.instance),

View File

@ -1,6 +1,6 @@
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:enaklo_pos/data/models/response/item_sales_response_model.dart'; import 'package:enaklo_pos/data/datasources/analytic_remote_datasource.dart';
import 'package:enaklo_pos/data/datasources/order_item_remote_datasource.dart'; import 'package:enaklo_pos/data/models/response/sales_analytic_response_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
part 'item_sales_report_event.dart'; part 'item_sales_report_event.dart';
@ -9,12 +9,14 @@ part 'item_sales_report_bloc.freezed.dart';
class ItemSalesReportBloc class ItemSalesReportBloc
extends Bloc<ItemSalesReportEvent, ItemSalesReportState> { extends Bloc<ItemSalesReportEvent, ItemSalesReportState> {
final OrderItemRemoteDatasource datasource; final AnalyticRemoteDatasource datasource;
ItemSalesReportBloc(this.datasource) : super(const _Initial()) { ItemSalesReportBloc(this.datasource) : super(const _Initial()) {
on<_GetItemSales>((event, emit) async { on<_GetItemSales>((event, emit) async {
emit(const _Loading()); emit(const _Loading());
final result = await datasource.getItemSalesByRangeDate( final result = await datasource.getSales(
event.startDate, event.endDate); dateFrom: event.startDate,
dateTo: event.endDate,
);
result.fold((l) => emit(_Error(l)), (r) => emit(_Loaded(r.data!))); result.fold((l) => emit(_Error(l)), (r) => emit(_Loaded(r.data!)));
}); });
} }

View File

@ -19,19 +19,20 @@ mixin _$ItemSalesReportEvent {
@optionalTypeArgs @optionalTypeArgs
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() started, required TResult Function() started,
required TResult Function(String startDate, String endDate) getItemSales, required TResult Function(DateTime startDate, DateTime endDate)
getItemSales,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@optionalTypeArgs @optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? started, TResult? Function()? started,
TResult? Function(String startDate, String endDate)? getItemSales, TResult? Function(DateTime startDate, DateTime endDate)? getItemSales,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@optionalTypeArgs @optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? started, TResult Function()? started,
TResult Function(String startDate, String endDate)? getItemSales, TResult Function(DateTime startDate, DateTime endDate)? getItemSales,
required TResult orElse(), required TResult orElse(),
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@ -120,7 +121,8 @@ class _$StartedImpl implements _Started {
@optionalTypeArgs @optionalTypeArgs
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() started, required TResult Function() started,
required TResult Function(String startDate, String endDate) getItemSales, required TResult Function(DateTime startDate, DateTime endDate)
getItemSales,
}) { }) {
return started(); return started();
} }
@ -129,7 +131,7 @@ class _$StartedImpl implements _Started {
@optionalTypeArgs @optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? started, TResult? Function()? started,
TResult? Function(String startDate, String endDate)? getItemSales, TResult? Function(DateTime startDate, DateTime endDate)? getItemSales,
}) { }) {
return started?.call(); return started?.call();
} }
@ -138,7 +140,7 @@ class _$StartedImpl implements _Started {
@optionalTypeArgs @optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? started, TResult Function()? started,
TResult Function(String startDate, String endDate)? getItemSales, TResult Function(DateTime startDate, DateTime endDate)? getItemSales,
required TResult orElse(), required TResult orElse(),
}) { }) {
if (started != null) { if (started != null) {
@ -189,7 +191,7 @@ abstract class _$$GetItemSalesImplCopyWith<$Res> {
_$GetItemSalesImpl value, $Res Function(_$GetItemSalesImpl) then) = _$GetItemSalesImpl value, $Res Function(_$GetItemSalesImpl) then) =
__$$GetItemSalesImplCopyWithImpl<$Res>; __$$GetItemSalesImplCopyWithImpl<$Res>;
@useResult @useResult
$Res call({String startDate, String endDate}); $Res call({DateTime startDate, DateTime endDate});
} }
/// @nodoc /// @nodoc
@ -212,11 +214,11 @@ class __$$GetItemSalesImplCopyWithImpl<$Res>
startDate: null == startDate startDate: null == startDate
? _value.startDate ? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable : startDate // ignore: cast_nullable_to_non_nullable
as String, as DateTime,
endDate: null == endDate endDate: null == endDate
? _value.endDate ? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable : endDate // ignore: cast_nullable_to_non_nullable
as String, as DateTime,
)); ));
} }
} }
@ -227,9 +229,9 @@ class _$GetItemSalesImpl implements _GetItemSales {
const _$GetItemSalesImpl({required this.startDate, required this.endDate}); const _$GetItemSalesImpl({required this.startDate, required this.endDate});
@override @override
final String startDate; final DateTime startDate;
@override @override
final String endDate; final DateTime endDate;
@override @override
String toString() { String toString() {
@ -261,7 +263,8 @@ class _$GetItemSalesImpl implements _GetItemSales {
@optionalTypeArgs @optionalTypeArgs
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() started, required TResult Function() started,
required TResult Function(String startDate, String endDate) getItemSales, required TResult Function(DateTime startDate, DateTime endDate)
getItemSales,
}) { }) {
return getItemSales(startDate, endDate); return getItemSales(startDate, endDate);
} }
@ -270,7 +273,7 @@ class _$GetItemSalesImpl implements _GetItemSales {
@optionalTypeArgs @optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? started, TResult? Function()? started,
TResult? Function(String startDate, String endDate)? getItemSales, TResult? Function(DateTime startDate, DateTime endDate)? getItemSales,
}) { }) {
return getItemSales?.call(startDate, endDate); return getItemSales?.call(startDate, endDate);
} }
@ -279,7 +282,7 @@ class _$GetItemSalesImpl implements _GetItemSales {
@optionalTypeArgs @optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? started, TResult Function()? started,
TResult Function(String startDate, String endDate)? getItemSales, TResult Function(DateTime startDate, DateTime endDate)? getItemSales,
required TResult orElse(), required TResult orElse(),
}) { }) {
if (getItemSales != null) { if (getItemSales != null) {
@ -322,11 +325,11 @@ class _$GetItemSalesImpl implements _GetItemSales {
abstract class _GetItemSales implements ItemSalesReportEvent { abstract class _GetItemSales implements ItemSalesReportEvent {
const factory _GetItemSales( const factory _GetItemSales(
{required final String startDate, {required final DateTime startDate,
required final String endDate}) = _$GetItemSalesImpl; required final DateTime endDate}) = _$GetItemSalesImpl;
String get startDate; DateTime get startDate;
String get endDate; DateTime get endDate;
/// Create a copy of ItemSalesReportEvent /// Create a copy of ItemSalesReportEvent
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@ -341,7 +344,7 @@ mixin _$ItemSalesReportState {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() initial, required TResult Function() initial,
required TResult Function() loading, required TResult Function() loading,
required TResult Function(List<ItemSales> itemSales) loaded, required TResult Function(SalesAnalyticData itemSales) loaded,
required TResult Function(String message) error, required TResult Function(String message) error,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@ -349,7 +352,7 @@ mixin _$ItemSalesReportState {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial, TResult? Function()? initial,
TResult? Function()? loading, TResult? Function()? loading,
TResult? Function(List<ItemSales> itemSales)? loaded, TResult? Function(SalesAnalyticData itemSales)? loaded,
TResult? Function(String message)? error, TResult? Function(String message)? error,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@ -357,7 +360,7 @@ mixin _$ItemSalesReportState {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial, TResult Function()? initial,
TResult Function()? loading, TResult Function()? loading,
TResult Function(List<ItemSales> itemSales)? loaded, TResult Function(SalesAnalyticData itemSales)? loaded,
TResult Function(String message)? error, TResult Function(String message)? error,
required TResult orElse(), required TResult orElse(),
}) => }) =>
@ -454,7 +457,7 @@ class _$InitialImpl implements _Initial {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() initial, required TResult Function() initial,
required TResult Function() loading, required TResult Function() loading,
required TResult Function(List<ItemSales> itemSales) loaded, required TResult Function(SalesAnalyticData itemSales) loaded,
required TResult Function(String message) error, required TResult Function(String message) error,
}) { }) {
return initial(); return initial();
@ -465,7 +468,7 @@ class _$InitialImpl implements _Initial {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial, TResult? Function()? initial,
TResult? Function()? loading, TResult? Function()? loading,
TResult? Function(List<ItemSales> itemSales)? loaded, TResult? Function(SalesAnalyticData itemSales)? loaded,
TResult? Function(String message)? error, TResult? Function(String message)? error,
}) { }) {
return initial?.call(); return initial?.call();
@ -476,7 +479,7 @@ class _$InitialImpl implements _Initial {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial, TResult Function()? initial,
TResult Function()? loading, TResult Function()? loading,
TResult Function(List<ItemSales> itemSales)? loaded, TResult Function(SalesAnalyticData itemSales)? loaded,
TResult Function(String message)? error, TResult Function(String message)? error,
required TResult orElse(), required TResult orElse(),
}) { }) {
@ -571,7 +574,7 @@ class _$LoadingImpl implements _Loading {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() initial, required TResult Function() initial,
required TResult Function() loading, required TResult Function() loading,
required TResult Function(List<ItemSales> itemSales) loaded, required TResult Function(SalesAnalyticData itemSales) loaded,
required TResult Function(String message) error, required TResult Function(String message) error,
}) { }) {
return loading(); return loading();
@ -582,7 +585,7 @@ class _$LoadingImpl implements _Loading {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial, TResult? Function()? initial,
TResult? Function()? loading, TResult? Function()? loading,
TResult? Function(List<ItemSales> itemSales)? loaded, TResult? Function(SalesAnalyticData itemSales)? loaded,
TResult? Function(String message)? error, TResult? Function(String message)? error,
}) { }) {
return loading?.call(); return loading?.call();
@ -593,7 +596,7 @@ class _$LoadingImpl implements _Loading {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial, TResult Function()? initial,
TResult Function()? loading, TResult Function()? loading,
TResult Function(List<ItemSales> itemSales)? loaded, TResult Function(SalesAnalyticData itemSales)? loaded,
TResult Function(String message)? error, TResult Function(String message)? error,
required TResult orElse(), required TResult orElse(),
}) { }) {
@ -651,7 +654,7 @@ abstract class _$$LoadedImplCopyWith<$Res> {
_$LoadedImpl value, $Res Function(_$LoadedImpl) then) = _$LoadedImpl value, $Res Function(_$LoadedImpl) then) =
__$$LoadedImplCopyWithImpl<$Res>; __$$LoadedImplCopyWithImpl<$Res>;
@useResult @useResult
$Res call({List<ItemSales> itemSales}); $Res call({SalesAnalyticData itemSales});
} }
/// @nodoc /// @nodoc
@ -671,9 +674,9 @@ class __$$LoadedImplCopyWithImpl<$Res>
}) { }) {
return _then(_$LoadedImpl( return _then(_$LoadedImpl(
null == itemSales null == itemSales
? _value._itemSales ? _value.itemSales
: itemSales // ignore: cast_nullable_to_non_nullable : itemSales // ignore: cast_nullable_to_non_nullable
as List<ItemSales>, as SalesAnalyticData,
)); ));
} }
} }
@ -681,15 +684,10 @@ class __$$LoadedImplCopyWithImpl<$Res>
/// @nodoc /// @nodoc
class _$LoadedImpl implements _Loaded { class _$LoadedImpl implements _Loaded {
const _$LoadedImpl(final List<ItemSales> itemSales) : _itemSales = itemSales; const _$LoadedImpl(this.itemSales);
final List<ItemSales> _itemSales;
@override @override
List<ItemSales> get itemSales { final SalesAnalyticData itemSales;
if (_itemSales is EqualUnmodifiableListView) return _itemSales;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_itemSales);
}
@override @override
String toString() { String toString() {
@ -701,13 +699,12 @@ class _$LoadedImpl implements _Loaded {
return identical(this, other) || return identical(this, other) ||
(other.runtimeType == runtimeType && (other.runtimeType == runtimeType &&
other is _$LoadedImpl && other is _$LoadedImpl &&
const DeepCollectionEquality() (identical(other.itemSales, itemSales) ||
.equals(other._itemSales, _itemSales)); other.itemSales == itemSales));
} }
@override @override
int get hashCode => int get hashCode => Object.hash(runtimeType, itemSales);
Object.hash(runtimeType, const DeepCollectionEquality().hash(_itemSales));
/// Create a copy of ItemSalesReportState /// Create a copy of ItemSalesReportState
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@ -722,7 +719,7 @@ class _$LoadedImpl implements _Loaded {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() initial, required TResult Function() initial,
required TResult Function() loading, required TResult Function() loading,
required TResult Function(List<ItemSales> itemSales) loaded, required TResult Function(SalesAnalyticData itemSales) loaded,
required TResult Function(String message) error, required TResult Function(String message) error,
}) { }) {
return loaded(itemSales); return loaded(itemSales);
@ -733,7 +730,7 @@ class _$LoadedImpl implements _Loaded {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial, TResult? Function()? initial,
TResult? Function()? loading, TResult? Function()? loading,
TResult? Function(List<ItemSales> itemSales)? loaded, TResult? Function(SalesAnalyticData itemSales)? loaded,
TResult? Function(String message)? error, TResult? Function(String message)? error,
}) { }) {
return loaded?.call(itemSales); return loaded?.call(itemSales);
@ -744,7 +741,7 @@ class _$LoadedImpl implements _Loaded {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial, TResult Function()? initial,
TResult Function()? loading, TResult Function()? loading,
TResult Function(List<ItemSales> itemSales)? loaded, TResult Function(SalesAnalyticData itemSales)? loaded,
TResult Function(String message)? error, TResult Function(String message)? error,
required TResult orElse(), required TResult orElse(),
}) { }) {
@ -793,9 +790,9 @@ class _$LoadedImpl implements _Loaded {
} }
abstract class _Loaded implements ItemSalesReportState { abstract class _Loaded implements ItemSalesReportState {
const factory _Loaded(final List<ItemSales> itemSales) = _$LoadedImpl; const factory _Loaded(final SalesAnalyticData itemSales) = _$LoadedImpl;
List<ItemSales> get itemSales; SalesAnalyticData get itemSales;
/// Create a copy of ItemSalesReportState /// Create a copy of ItemSalesReportState
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@ -874,7 +871,7 @@ class _$ErrorImpl implements _Error {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() initial, required TResult Function() initial,
required TResult Function() loading, required TResult Function() loading,
required TResult Function(List<ItemSales> itemSales) loaded, required TResult Function(SalesAnalyticData itemSales) loaded,
required TResult Function(String message) error, required TResult Function(String message) error,
}) { }) {
return error(message); return error(message);
@ -885,7 +882,7 @@ class _$ErrorImpl implements _Error {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial, TResult? Function()? initial,
TResult? Function()? loading, TResult? Function()? loading,
TResult? Function(List<ItemSales> itemSales)? loaded, TResult? Function(SalesAnalyticData itemSales)? loaded,
TResult? Function(String message)? error, TResult? Function(String message)? error,
}) { }) {
return error?.call(message); return error?.call(message);
@ -896,7 +893,7 @@ class _$ErrorImpl implements _Error {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial, TResult Function()? initial,
TResult Function()? loading, TResult Function()? loading,
TResult Function(List<ItemSales> itemSales)? loaded, TResult Function(SalesAnalyticData itemSales)? loaded,
TResult Function(String message)? error, TResult Function(String message)? error,
required TResult orElse(), required TResult orElse(),
}) { }) {

View File

@ -4,7 +4,7 @@ part of 'item_sales_report_bloc.dart';
class ItemSalesReportEvent with _$ItemSalesReportEvent { class ItemSalesReportEvent with _$ItemSalesReportEvent {
const factory ItemSalesReportEvent.started() = _Started; const factory ItemSalesReportEvent.started() = _Started;
const factory ItemSalesReportEvent.getItemSales({ const factory ItemSalesReportEvent.getItemSales({
required String startDate, required DateTime startDate,
required String endDate, required DateTime endDate,
}) = _GetItemSales; }) = _GetItemSales;
} }

View File

@ -4,7 +4,7 @@ part of 'item_sales_report_bloc.dart';
class ItemSalesReportState with _$ItemSalesReportState { class ItemSalesReportState with _$ItemSalesReportState {
const factory ItemSalesReportState.initial() = _Initial; const factory ItemSalesReportState.initial() = _Initial;
const factory ItemSalesReportState.loading() = _Loading; const factory ItemSalesReportState.loading() = _Loading;
const factory ItemSalesReportState.loaded(List<ItemSales> itemSales) = const factory ItemSalesReportState.loaded(SalesAnalyticData itemSales) =
_Loaded; _Loaded;
const factory ItemSalesReportState.error(String message) = _Error; const factory ItemSalesReportState.error(String message) = _Error;
} }

View File

@ -1,6 +1,6 @@
import 'package:enaklo_pos/data/datasources/analytic_remote_datasource.dart';
import 'package:enaklo_pos/data/models/response/payment_method_analytic_response_model.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:enaklo_pos/data/datasources/order_remote_datasource.dart';
import 'package:enaklo_pos/data/models/response/payment_method_response_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
part 'payment_method_report_event.dart'; part 'payment_method_report_event.dart';
@ -9,16 +9,23 @@ part 'payment_method_report_bloc.freezed.dart';
class PaymentMethodReportBloc class PaymentMethodReportBloc
extends Bloc<PaymentMethodReportEvent, PaymentMethodReportState> { extends Bloc<PaymentMethodReportEvent, PaymentMethodReportState> {
final OrderRemoteDatasource datasource; final AnalyticRemoteDatasource datasource;
PaymentMethodReportBloc(this.datasource) : super(const _Initial()) { PaymentMethodReportBloc(this.datasource) : super(const _Initial()) {
on<_GetPaymentMethodReport>((event, emit) async { on<_GetPaymentMethodReport>((event, emit) async {
emit(const _Loading()); emit(const _Loading());
final result = await datasource.getPaymentMethodByRangeDate( final result = await datasource.getPaymentMethod(
event.startDate, dateFrom: event.startDate,
event.endDate, dateTo: event.endDate,
); );
result.fold((l) => emit(_Error(l)), (r) => emit(_Loaded(r.data!))); result.fold(
(l) => emit(_Error(l)),
(r) => emit(
_Loaded(
r.data,
),
),
);
}); });
} }
} }

View File

@ -16,22 +16,24 @@ final _privateConstructorUsedError = UnsupportedError(
/// @nodoc /// @nodoc
mixin _$PaymentMethodReportEvent { mixin _$PaymentMethodReportEvent {
String get startDate => throw _privateConstructorUsedError; DateTime get startDate => throw _privateConstructorUsedError;
String get endDate => throw _privateConstructorUsedError; DateTime get endDate => throw _privateConstructorUsedError;
@optionalTypeArgs @optionalTypeArgs
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function(String startDate, String endDate) required TResult Function(DateTime startDate, DateTime endDate)
getPaymentMethodReport, getPaymentMethodReport,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@optionalTypeArgs @optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String startDate, String endDate)? getPaymentMethodReport, TResult? Function(DateTime startDate, DateTime endDate)?
getPaymentMethodReport,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@optionalTypeArgs @optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function(String startDate, String endDate)? getPaymentMethodReport, TResult Function(DateTime startDate, DateTime endDate)?
getPaymentMethodReport,
required TResult orElse(), required TResult orElse(),
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@ -66,7 +68,7 @@ abstract class $PaymentMethodReportEventCopyWith<$Res> {
$Res Function(PaymentMethodReportEvent) then) = $Res Function(PaymentMethodReportEvent) then) =
_$PaymentMethodReportEventCopyWithImpl<$Res, PaymentMethodReportEvent>; _$PaymentMethodReportEventCopyWithImpl<$Res, PaymentMethodReportEvent>;
@useResult @useResult
$Res call({String startDate, String endDate}); $Res call({DateTime startDate, DateTime endDate});
} }
/// @nodoc /// @nodoc
@ -92,11 +94,11 @@ class _$PaymentMethodReportEventCopyWithImpl<$Res,
startDate: null == startDate startDate: null == startDate
? _value.startDate ? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable : startDate // ignore: cast_nullable_to_non_nullable
as String, as DateTime,
endDate: null == endDate endDate: null == endDate
? _value.endDate ? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable : endDate // ignore: cast_nullable_to_non_nullable
as String, as DateTime,
) as $Val); ) as $Val);
} }
} }
@ -110,7 +112,7 @@ abstract class _$$GetPaymentMethodReportImplCopyWith<$Res>
__$$GetPaymentMethodReportImplCopyWithImpl<$Res>; __$$GetPaymentMethodReportImplCopyWithImpl<$Res>;
@override @override
@useResult @useResult
$Res call({String startDate, String endDate}); $Res call({DateTime startDate, DateTime endDate});
} }
/// @nodoc /// @nodoc
@ -135,11 +137,11 @@ class __$$GetPaymentMethodReportImplCopyWithImpl<$Res>
startDate: null == startDate startDate: null == startDate
? _value.startDate ? _value.startDate
: startDate // ignore: cast_nullable_to_non_nullable : startDate // ignore: cast_nullable_to_non_nullable
as String, as DateTime,
endDate: null == endDate endDate: null == endDate
? _value.endDate ? _value.endDate
: endDate // ignore: cast_nullable_to_non_nullable : endDate // ignore: cast_nullable_to_non_nullable
as String, as DateTime,
)); ));
} }
} }
@ -151,9 +153,9 @@ class _$GetPaymentMethodReportImpl implements _GetPaymentMethodReport {
{required this.startDate, required this.endDate}); {required this.startDate, required this.endDate});
@override @override
final String startDate; final DateTime startDate;
@override @override
final String endDate; final DateTime endDate;
@override @override
String toString() { String toString() {
@ -185,7 +187,7 @@ class _$GetPaymentMethodReportImpl implements _GetPaymentMethodReport {
@override @override
@optionalTypeArgs @optionalTypeArgs
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function(String startDate, String endDate) required TResult Function(DateTime startDate, DateTime endDate)
getPaymentMethodReport, getPaymentMethodReport,
}) { }) {
return getPaymentMethodReport(startDate, endDate); return getPaymentMethodReport(startDate, endDate);
@ -194,7 +196,8 @@ class _$GetPaymentMethodReportImpl implements _GetPaymentMethodReport {
@override @override
@optionalTypeArgs @optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String startDate, String endDate)? getPaymentMethodReport, TResult? Function(DateTime startDate, DateTime endDate)?
getPaymentMethodReport,
}) { }) {
return getPaymentMethodReport?.call(startDate, endDate); return getPaymentMethodReport?.call(startDate, endDate);
} }
@ -202,7 +205,8 @@ class _$GetPaymentMethodReportImpl implements _GetPaymentMethodReport {
@override @override
@optionalTypeArgs @optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function(String startDate, String endDate)? getPaymentMethodReport, TResult Function(DateTime startDate, DateTime endDate)?
getPaymentMethodReport,
required TResult orElse(), required TResult orElse(),
}) { }) {
if (getPaymentMethodReport != null) { if (getPaymentMethodReport != null) {
@ -243,13 +247,13 @@ class _$GetPaymentMethodReportImpl implements _GetPaymentMethodReport {
abstract class _GetPaymentMethodReport implements PaymentMethodReportEvent { abstract class _GetPaymentMethodReport implements PaymentMethodReportEvent {
const factory _GetPaymentMethodReport( const factory _GetPaymentMethodReport(
{required final String startDate, {required final DateTime startDate,
required final String endDate}) = _$GetPaymentMethodReportImpl; required final DateTime endDate}) = _$GetPaymentMethodReportImpl;
@override @override
String get startDate; DateTime get startDate;
@override @override
String get endDate; DateTime get endDate;
/// Create a copy of PaymentMethodReportEvent /// Create a copy of PaymentMethodReportEvent
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@ -265,7 +269,7 @@ mixin _$PaymentMethodReportState {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() initial, required TResult Function() initial,
required TResult Function() loading, required TResult Function() loading,
required TResult Function(PaymentMethodData data) loaded, required TResult Function(PaymentMethodAnalyticData data) loaded,
required TResult Function(String message) error, required TResult Function(String message) error,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@ -273,7 +277,7 @@ mixin _$PaymentMethodReportState {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial, TResult? Function()? initial,
TResult? Function()? loading, TResult? Function()? loading,
TResult? Function(PaymentMethodData data)? loaded, TResult? Function(PaymentMethodAnalyticData data)? loaded,
TResult? Function(String message)? error, TResult? Function(String message)? error,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@ -281,7 +285,7 @@ mixin _$PaymentMethodReportState {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial, TResult Function()? initial,
TResult Function()? loading, TResult Function()? loading,
TResult Function(PaymentMethodData data)? loaded, TResult Function(PaymentMethodAnalyticData data)? loaded,
TResult Function(String message)? error, TResult Function(String message)? error,
required TResult orElse(), required TResult orElse(),
}) => }) =>
@ -378,7 +382,7 @@ class _$InitialImpl implements _Initial {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() initial, required TResult Function() initial,
required TResult Function() loading, required TResult Function() loading,
required TResult Function(PaymentMethodData data) loaded, required TResult Function(PaymentMethodAnalyticData data) loaded,
required TResult Function(String message) error, required TResult Function(String message) error,
}) { }) {
return initial(); return initial();
@ -389,7 +393,7 @@ class _$InitialImpl implements _Initial {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial, TResult? Function()? initial,
TResult? Function()? loading, TResult? Function()? loading,
TResult? Function(PaymentMethodData data)? loaded, TResult? Function(PaymentMethodAnalyticData data)? loaded,
TResult? Function(String message)? error, TResult? Function(String message)? error,
}) { }) {
return initial?.call(); return initial?.call();
@ -400,7 +404,7 @@ class _$InitialImpl implements _Initial {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial, TResult Function()? initial,
TResult Function()? loading, TResult Function()? loading,
TResult Function(PaymentMethodData data)? loaded, TResult Function(PaymentMethodAnalyticData data)? loaded,
TResult Function(String message)? error, TResult Function(String message)? error,
required TResult orElse(), required TResult orElse(),
}) { }) {
@ -495,7 +499,7 @@ class _$LoadingImpl implements _Loading {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() initial, required TResult Function() initial,
required TResult Function() loading, required TResult Function() loading,
required TResult Function(PaymentMethodData data) loaded, required TResult Function(PaymentMethodAnalyticData data) loaded,
required TResult Function(String message) error, required TResult Function(String message) error,
}) { }) {
return loading(); return loading();
@ -506,7 +510,7 @@ class _$LoadingImpl implements _Loading {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial, TResult? Function()? initial,
TResult? Function()? loading, TResult? Function()? loading,
TResult? Function(PaymentMethodData data)? loaded, TResult? Function(PaymentMethodAnalyticData data)? loaded,
TResult? Function(String message)? error, TResult? Function(String message)? error,
}) { }) {
return loading?.call(); return loading?.call();
@ -517,7 +521,7 @@ class _$LoadingImpl implements _Loading {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial, TResult Function()? initial,
TResult Function()? loading, TResult Function()? loading,
TResult Function(PaymentMethodData data)? loaded, TResult Function(PaymentMethodAnalyticData data)? loaded,
TResult Function(String message)? error, TResult Function(String message)? error,
required TResult orElse(), required TResult orElse(),
}) { }) {
@ -575,7 +579,7 @@ abstract class _$$LoadedImplCopyWith<$Res> {
_$LoadedImpl value, $Res Function(_$LoadedImpl) then) = _$LoadedImpl value, $Res Function(_$LoadedImpl) then) =
__$$LoadedImplCopyWithImpl<$Res>; __$$LoadedImplCopyWithImpl<$Res>;
@useResult @useResult
$Res call({PaymentMethodData data}); $Res call({PaymentMethodAnalyticData data});
} }
/// @nodoc /// @nodoc
@ -597,7 +601,7 @@ class __$$LoadedImplCopyWithImpl<$Res>
null == data null == data
? _value.data ? _value.data
: data // ignore: cast_nullable_to_non_nullable : data // ignore: cast_nullable_to_non_nullable
as PaymentMethodData, as PaymentMethodAnalyticData,
)); ));
} }
} }
@ -608,7 +612,7 @@ class _$LoadedImpl implements _Loaded {
const _$LoadedImpl(this.data); const _$LoadedImpl(this.data);
@override @override
final PaymentMethodData data; final PaymentMethodAnalyticData data;
@override @override
String toString() { String toString() {
@ -639,7 +643,7 @@ class _$LoadedImpl implements _Loaded {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() initial, required TResult Function() initial,
required TResult Function() loading, required TResult Function() loading,
required TResult Function(PaymentMethodData data) loaded, required TResult Function(PaymentMethodAnalyticData data) loaded,
required TResult Function(String message) error, required TResult Function(String message) error,
}) { }) {
return loaded(data); return loaded(data);
@ -650,7 +654,7 @@ class _$LoadedImpl implements _Loaded {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial, TResult? Function()? initial,
TResult? Function()? loading, TResult? Function()? loading,
TResult? Function(PaymentMethodData data)? loaded, TResult? Function(PaymentMethodAnalyticData data)? loaded,
TResult? Function(String message)? error, TResult? Function(String message)? error,
}) { }) {
return loaded?.call(data); return loaded?.call(data);
@ -661,7 +665,7 @@ class _$LoadedImpl implements _Loaded {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial, TResult Function()? initial,
TResult Function()? loading, TResult Function()? loading,
TResult Function(PaymentMethodData data)? loaded, TResult Function(PaymentMethodAnalyticData data)? loaded,
TResult Function(String message)? error, TResult Function(String message)? error,
required TResult orElse(), required TResult orElse(),
}) { }) {
@ -710,9 +714,9 @@ class _$LoadedImpl implements _Loaded {
} }
abstract class _Loaded implements PaymentMethodReportState { abstract class _Loaded implements PaymentMethodReportState {
const factory _Loaded(final PaymentMethodData data) = _$LoadedImpl; const factory _Loaded(final PaymentMethodAnalyticData data) = _$LoadedImpl;
PaymentMethodData get data; PaymentMethodAnalyticData get data;
/// Create a copy of PaymentMethodReportState /// Create a copy of PaymentMethodReportState
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@ -791,7 +795,7 @@ class _$ErrorImpl implements _Error {
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() initial, required TResult Function() initial,
required TResult Function() loading, required TResult Function() loading,
required TResult Function(PaymentMethodData data) loaded, required TResult Function(PaymentMethodAnalyticData data) loaded,
required TResult Function(String message) error, required TResult Function(String message) error,
}) { }) {
return error(message); return error(message);
@ -802,7 +806,7 @@ class _$ErrorImpl implements _Error {
TResult? whenOrNull<TResult extends Object?>({ TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial, TResult? Function()? initial,
TResult? Function()? loading, TResult? Function()? loading,
TResult? Function(PaymentMethodData data)? loaded, TResult? Function(PaymentMethodAnalyticData data)? loaded,
TResult? Function(String message)? error, TResult? Function(String message)? error,
}) { }) {
return error?.call(message); return error?.call(message);
@ -813,7 +817,7 @@ class _$ErrorImpl implements _Error {
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial, TResult Function()? initial,
TResult Function()? loading, TResult Function()? loading,
TResult Function(PaymentMethodData data)? loaded, TResult Function(PaymentMethodAnalyticData data)? loaded,
TResult Function(String message)? error, TResult Function(String message)? error,
required TResult orElse(), required TResult orElse(),
}) { }) {

View File

@ -3,7 +3,7 @@ part of 'payment_method_report_bloc.dart';
@freezed @freezed
class PaymentMethodReportEvent with _$PaymentMethodReportEvent { class PaymentMethodReportEvent with _$PaymentMethodReportEvent {
const factory PaymentMethodReportEvent.getPaymentMethodReport({ const factory PaymentMethodReportEvent.getPaymentMethodReport({
required String startDate, required DateTime startDate,
required String endDate, required DateTime endDate,
}) = _GetPaymentMethodReport; }) = _GetPaymentMethodReport;
} }

View File

@ -4,6 +4,7 @@ part of 'payment_method_report_bloc.dart';
class PaymentMethodReportState with _$PaymentMethodReportState { class PaymentMethodReportState with _$PaymentMethodReportState {
const factory PaymentMethodReportState.initial() = _Initial; const factory PaymentMethodReportState.initial() = _Initial;
const factory PaymentMethodReportState.loading() = _Loading; const factory PaymentMethodReportState.loading() = _Loading;
const factory PaymentMethodReportState.loaded(PaymentMethodData data) = _Loaded; const factory PaymentMethodReportState.loaded(
PaymentMethodAnalyticData data) = _Loaded;
const factory PaymentMethodReportState.error(String message) = _Error; const factory PaymentMethodReportState.error(String message) = _Error;
} }

View File

@ -1,5 +1,7 @@
import 'dart:developer'; import 'dart:developer';
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
import 'package:enaklo_pos/presentation/sales/pages/sales_page.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:enaklo_pos/core/components/custom_date_picker.dart'; import 'package:enaklo_pos/core/components/custom_date_picker.dart';
import 'package:enaklo_pos/core/constants/colors.dart'; import 'package:enaklo_pos/core/constants/colors.dart';
@ -29,7 +31,7 @@ class ReportPage extends StatefulWidget {
} }
class _ReportPageState extends State<ReportPage> { class _ReportPageState extends State<ReportPage> {
int selectedMenu = 0; int selectedMenu = 1;
String title = 'Transaction Report'; String title = 'Transaction Report';
DateTime fromDate = DateTime.now().subtract(const Duration(days: 30)); DateTime fromDate = DateTime.now().subtract(const Duration(days: 30));
DateTime toDate = DateTime.now(); DateTime toDate = DateTime.now();
@ -37,10 +39,9 @@ class _ReportPageState extends State<ReportPage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
context.read<TransactionReportBloc>().add( context.read<ItemSalesReportBloc>().add(
TransactionReportEvent.getReport( ItemSalesReportEvent.getItemSales(
startDate: DateFormatter.formatDateTime(fromDate), startDate: fromDate, endDate: toDate),
endDate: DateFormatter.formatDateTime(toDate)),
); );
} }
@ -117,18 +118,7 @@ class _ReportPageState extends State<ReportPage> {
'Menampilkan riwayat lengkap semua transaksi yang telah dilakukan.', 'Menampilkan riwayat lengkap semua transaksi yang telah dilakukan.',
icon: Icons.receipt_long_outlined, icon: Icons.receipt_long_outlined,
onPressed: () { onPressed: () {
selectedMenu = 0; context.push(SalesPage(status: 'completed'));
title = 'Laporan Transaksi';
setState(() {});
//enddate is 1 month before the current date
context.read<TransactionReportBloc>().add(
TransactionReportEvent.getReport(
startDate:
DateFormatter.formatDateTime(
fromDate),
endDate: DateFormatter.formatDateTime(
toDate)),
);
}, },
isActive: selectedMenu == 0, isActive: selectedMenu == 0,
), ),
@ -143,11 +133,7 @@ class _ReportPageState extends State<ReportPage> {
setState(() {}); setState(() {});
context.read<ItemSalesReportBloc>().add( context.read<ItemSalesReportBloc>().add(
ItemSalesReportEvent.getItemSales( ItemSalesReportEvent.getItemSales(
startDate: startDate: fromDate, endDate: toDate),
DateFormatter.formatDateTime(
fromDate),
endDate: DateFormatter.formatDateTime(
toDate)),
); );
}, },
isActive: selectedMenu == 1, isActive: selectedMenu == 1,
@ -202,12 +188,9 @@ class _ReportPageState extends State<ReportPage> {
context.read<PaymentMethodReportBloc>().add( context.read<PaymentMethodReportBloc>().add(
PaymentMethodReportEvent PaymentMethodReportEvent
.getPaymentMethodReport( .getPaymentMethodReport(
startDate: startDate: fromDate,
DateFormatter.formatDateTime( endDate: toDate,
fromDate), ),
endDate:
DateFormatter.formatDateTime(
toDate)),
); );
}, },
isActive: selectedMenu == 4, isActive: selectedMenu == 4,
@ -257,7 +240,7 @@ class _ReportPageState extends State<ReportPage> {
}, },
loaded: (itemSales) { loaded: (itemSales) {
return ItemSalesReportWidget( return ItemSalesReportWidget(
itemSales: itemSales, sales: itemSales,
title: title, title: title,
searchDateFormatted: searchDateFormatted:
searchDateFormatted, searchDateFormatted,

View File

@ -1,24 +1,16 @@
import 'dart:developer';
import 'package:enaklo_pos/core/components/spaces.dart';
import 'package:enaklo_pos/core/constants/colors.dart';
import 'package:enaklo_pos/core/extensions/int_ext.dart'; import 'package:enaklo_pos/core/extensions/int_ext.dart';
import 'package:enaklo_pos/core/utils/helper_pdf_service.dart'; import 'package:enaklo_pos/data/models/response/sales_analytic_response_model.dart';
import 'package:enaklo_pos/presentation/report/widgets/report_page_title.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:enaklo_pos/core/utils/item_sales_invoice.dart'; import 'package:intl/intl.dart';
import 'package:enaklo_pos/core/utils/permession_handler.dart';
import 'package:enaklo_pos/data/models/response/item_sales_response_model.dart';
import 'package:horizontal_data_table/horizontal_data_table.dart';
class ItemSalesReportWidget extends StatelessWidget { class ItemSalesReportWidget extends StatelessWidget {
final String title; final String title;
final String searchDateFormatted; final String searchDateFormatted;
final List<ItemSales> itemSales; final SalesAnalyticData sales;
final List<Widget>? headerWidgets; final List<Widget>? headerWidgets;
const ItemSalesReportWidget({ const ItemSalesReportWidget({
super.key, super.key,
required this.itemSales, required this.sales,
required this.title, required this.title,
required this.searchDateFormatted, required this.searchDateFormatted,
required this.headerWidgets, required this.headerWidgets,
@ -26,123 +18,522 @@ class ItemSalesReportWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// Proses data untuk mendapatkan insights
final insights = _processSalesData(sales);
return Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.white,
border: Border(
left: BorderSide(
color: const Color(0xFFE5E7EB),
width: 1,
),
),
),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header
_buildHeader(),
const SizedBox(height: 24),
// Metrics menggunakan data dari summary
_buildMetrics(),
const SizedBox(height: 24),
// Daily Performance Section
Text(
'Daily Performance',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: const Color(0xFF111827),
),
),
const SizedBox(height: 16),
// Daily Performance List dengan data dinamis
ListView.builder(
itemCount: insights.sortedDailyData.length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
final dayData = insights.sortedDailyData[index];
return _buildDailyPerformanceItem(
date: dayData.formattedDate,
sales: dayData.formattedSales,
orders: dayData.orders,
items: dayData.items,
isHighest: dayData == insights.highestRevenueDay,
);
},
),
const SizedBox(height: 16),
// Summary Footer dengan data dinamis
_buildSummaryFooter(insights.highestRevenueDay),
],
),
),
);
}
// Method untuk memproses data dan mendapatkan insights
SalesInsights _processSalesData(SalesAnalyticData data) {
// Sort data by sales (descending) untuk ranking
List<SalesAnalyticItem> sortedData = List.from(data.data);
sortedData.sort((a, b) => b.sales.compareTo(a.sales));
// Find highest revenue day
SalesAnalyticItem? highestRevenueDay;
if (sortedData.isNotEmpty) {
highestRevenueDay = sortedData.first;
}
return SalesInsights(
originalData: data.data,
sortedDailyData: sortedData,
highestRevenueDay: highestRevenueDay,
summary: data.summary,
);
}
Widget _buildHeader() {
return Container(
padding: const EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: const Color(0xFFE5E7EB),
width: 1,
),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Sales Analytics',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w700,
color: const Color(0xFF111827),
),
),
const SizedBox(height: 4),
Text(
searchDateFormatted,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: const Color(0xFF6B7280),
),
),
],
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: const Color(0xFF10B981),
borderRadius: BorderRadius.circular(24),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.trending_up,
size: 16,
color: Colors.white,
),
const SizedBox(width: 6),
Text(
'Growing',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
],
),
),
],
),
);
}
Widget _buildMetrics() {
final summary = sales.summary;
return Column( return Column(
children: [ children: [
ReportPageTitle( Row(
title: title, children: [
searchDateFormatted: searchDateFormatted,
onExport: () async {
try {
final status = await PermessionHelper().checkPermission();
if (status) {
final pdfFile = await ItemSalesInvoice.generate(
itemSales, searchDateFormatted);
log("pdfFile: $pdfFile");
await HelperPdfService.openFile(pdfFile);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Storage permission is required to save PDF'),
backgroundColor: Colors.red,
),
);
}
} catch (e) {
log("Error generating PDF: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Failed to generate PDF: $e'),
backgroundColor: Colors.red,
),
);
}
},
),
const SpaceHeight(16.0),
Expanded( Expanded(
child: Padding( child: _buildMetricCard(
padding: const EdgeInsets.all(12), title: 'Total Sales',
child: ClipRRect( value: summary.totalSales.currencyFormatRpV2,
borderRadius: BorderRadius.circular(12), subtitle: 'Net Sales',
child: HorizontalDataTable( color: const Color(0xFF3B82F6),
leftHandSideColumnWidth: 80, backgroundColor: const Color(0xFFEFF6FF),
rightHandSideColumnWidth: 670, ),
isFixedHeader: true, ),
headerWidgets: headerWidgets, const SizedBox(width: 16),
Expanded(
// isFixedFooter: true, child: _buildMetricCard(
// footerWidgets: _getTitleWidget(), title: 'Total Orders',
leftSideItemBuilder: (context, index) { value: '${summary.totalOrders}',
return Container( subtitle: '${summary.totalItems} Items',
width: 80, color: const Color(0xFF8B5CF6),
height: 52, backgroundColor: const Color(0xFFF3E8FF),
alignment: Alignment.centerLeft, ),
child: Center(child: Text(itemSales[index].id.toString())), ),
],
),
const SizedBox(height: 16),
_buildFullWidthMetricCard(
title: 'Average Order Value',
value: summary.averageOrderValue.round().currencyFormatRpV2,
subtitle: 'Per transaction',
color: const Color(0xFFEF4444),
backgroundColor: const Color(0xFFFEF2F2),
),
],
); );
}, }
rightSideItemBuilder: (context, index) {
return Row( Widget _buildSummaryFooter(SalesAnalyticItem? highestDay) {
children: <Widget>[ if (highestDay == null) {
Container( return Container();
width: 100, }
height: 52,
alignment: Alignment.centerLeft, final dateFormat = DateFormat('dd MMM');
child: Center( final formattedDate = dateFormat.format(highestDay.date);
child: Text(itemSales[index].orderId.toString())),
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFFF9FAFB),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: const Color(0xFFE5E7EB),
width: 1,
), ),
Container(
width: 200,
height: 52,
alignment: Alignment.centerLeft,
child:
Center(child: Text(itemSales[index].productName!)),
), ),
child: Row(
children: [
Container(
width: 8,
height: 8,
decoration: BoxDecoration(
color: const Color(0xFF10B981),
shape: BoxShape.circle,
),
),
const SizedBox(width: 12),
Expanded(
child: Text(
'Peak performance on $formattedDate with ${_formatCurrency(highestDay.sales)} revenue (${highestDay.orders} orders)',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: const Color(0xFF374151),
),
),
),
],
),
);
}
// Helper method untuk format currency
String _formatCurrency(int amount) {
if (amount >= 1000000) {
return 'Rp ${(amount / 1000000).toStringAsFixed(1)}JT';
} else if (amount >= 1000) {
return 'Rp ${(amount / 1000).toStringAsFixed(0)}RB';
} else {
return 'Rp ${NumberFormat('#,###').format(amount)}';
}
}
Widget _buildMetricCard({
required String title,
required String value,
required String subtitle,
required Color color,
required Color backgroundColor,
}) {
return Container(
padding: const EdgeInsets.all(18),
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: color.withOpacity(0.2),
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: color,
),
),
const SizedBox(height: 8),
Text(
value,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
color: const Color(0xFF111827),
),
),
const SizedBox(height: 4),
Text(
subtitle,
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w400,
color: const Color(0xFF6B7280),
),
),
],
),
);
}
Widget _buildFullWidthMetricCard({
required String title,
required String value,
required String subtitle,
required Color color,
required Color backgroundColor,
}) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(18),
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: color.withOpacity(0.2),
width: 1,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: color,
),
),
const SizedBox(height: 4),
Text(
subtitle,
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w400,
color: const Color(0xFF6B7280),
),
),
],
),
Text(
value,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w700,
color: const Color(0xFF111827),
),
),
],
),
);
}
Widget _buildDailyPerformanceItem({
required String date,
required String sales,
required int orders,
required int items,
required bool isHighest,
}) {
return Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: isHighest ? const Color(0xFF10B981) : const Color(0xFFE5E7EB),
width: isHighest ? 2 : 1,
),
),
child: Row(
children: [
Container( Container(
width: 60, width: 60,
height: 52, child: Column(
alignment: Alignment.centerLeft, crossAxisAlignment: CrossAxisAlignment.start,
child: Center( children: [
child: Text(itemSales[index].quantity.toString())), Text(
date,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: const Color(0xFF111827),
), ),
),
if (isHighest)
Container( Container(
width: 150, margin: const EdgeInsets.only(top: 4),
height: 52, padding:
padding: const EdgeInsets.fromLTRB(5, 0, 0, 0), const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
alignment: Alignment.centerLeft, decoration: BoxDecoration(
child: Center( color: const Color(0xFF10B981),
borderRadius: BorderRadius.circular(4),
),
child: Text( child: Text(
itemSales[index].price!.currencyFormatRp, 'TOP',
)), style: TextStyle(
), fontSize: 8,
Container( fontWeight: FontWeight.w700,
width: 160, color: Colors.white,
height: 52,
padding: const EdgeInsets.fromLTRB(5, 0, 0, 0),
alignment: Alignment.centerLeft,
child: Center(
child: Text(
(itemSales[index].price! * itemSales[index].quantity!)
.currencyFormatRp,
)),
),
],
);
},
itemCount: itemSales.length,
rowSeparatorWidget: const Divider(
color: Colors.black38,
height: 1.0,
thickness: 0.0,
),
leftHandSideColBackgroundColor: AppColors.white,
rightHandSideColBackgroundColor: AppColors.white,
itemExtent: 55,
),
), ),
), ),
), ),
], ],
),
),
const SizedBox(width: 16),
Expanded(
flex: 2,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
sales,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: const Color(0xFF111827),
),
),
Text(
'Revenue',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w400,
color: const Color(0xFF6B7280),
),
),
],
),
),
Expanded(
flex: 1,
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'$orders orders',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: const Color(0xFF374151),
),
),
Text(
'$items items',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w400,
color: const Color(0xFF6B7280),
),
),
],
),
),
],
),
); );
} }
} }
extension SalesAnalyticItemExtension on SalesAnalyticItem {
String get formattedDate {
final dateFormat = DateFormat('dd MMM');
return dateFormat.format(date);
}
String get formattedSales {
if (sales >= 1000000) {
return 'Rp ${(sales / 1000000).toStringAsFixed(1)}JT';
} else if (sales >= 1000) {
return 'Rp ${(sales / 1000).toStringAsFixed(0)}RB';
} else {
return 'Rp ${NumberFormat('#,###').format(sales)}';
}
}
double get averageOrderValue {
return orders > 0 ? sales / orders : 0.0;
}
}
// ReportPageTitle(
// title: title,
// searchDateFormatted: searchDateFormatted,
// onExport: () async {
// try {
// final status = await PermessionHelper().checkPermission();
// if (status) {
// final pdfFile = await ItemSalesInvoice.generate(
// itemSales, searchDateFormatted);
// log("pdfFile: $pdfFile");
// await HelperPdfService.openFile(pdfFile);
// } else {
// ScaffoldMessenger.of(context).showSnackBar(
// const SnackBar(
// content:
// Text('Storage permission is required to save PDF'),
// backgroundColor: Colors.red,
// ),
// );
// }
// } catch (e) {
// log("Error generating PDF: $e");
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(
// content: Text('Failed to generate PDF: $e'),
// backgroundColor: Colors.red,
// ),
// );
// }
// },
// ),

View File

@ -1,12 +1,12 @@
import 'package:enaklo_pos/core/extensions/int_ext.dart';
import 'package:enaklo_pos/data/models/response/payment_method_analytic_response_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:enaklo_pos/core/constants/colors.dart'; import 'package:enaklo_pos/core/constants/colors.dart';
import 'package:enaklo_pos/core/extensions/string_ext.dart';
import 'package:enaklo_pos/data/models/response/payment_method_response_model.dart';
import '../../../core/components/spaces.dart'; import '../../../core/components/spaces.dart';
class PaymentMethodReportWidget extends StatelessWidget { class PaymentMethodReportWidget extends StatelessWidget {
final PaymentMethodData paymentMethodData; final PaymentMethodAnalyticData paymentMethodData;
final String title; final String title;
final String searchDateFormatted; final String searchDateFormatted;
final List<Widget> headerWidgets; final List<Widget> headerWidgets;
@ -23,127 +23,343 @@ class PaymentMethodReportWidget extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: AppColors.white, backgroundColor: AppColors.white,
body: Column( body: Container(
children: [ padding: const EdgeInsets.all(20),
// HEADER decoration: BoxDecoration(
Container( color: Colors.white,
padding: const EdgeInsets.all(24.0),
decoration: const BoxDecoration(
color: AppColors.white,
border: Border( border: Border(
bottom: BorderSide( left: BorderSide(
color: AppColors.stroke, color: Colors.grey.shade200,
width: 1, width: 1,
), ),
), ),
), ),
child: Row( child: SingleChildScrollView(
mainAxisAlignment: MainAxisAlignment.spaceBetween, child: Column(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header Section
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
title, title,
style: const TextStyle( style: TextStyle(
fontSize: 24, fontSize: 18,
fontWeight: FontWeight.w600, fontWeight: FontWeight.bold,
color: Colors.grey.shade800,
), ),
), ),
const SpaceHeight(8.0),
Text(
searchDateFormatted,
style: const TextStyle(
fontSize: 16,
color: AppColors.grey,
),
),
],
),
Row(
children: [
Container( Container(
padding: const EdgeInsets.symmetric( padding:
horizontal: 16.0, const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
vertical: 8.0,
),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.primary, color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(8.0), borderRadius: BorderRadius.circular(20),
), border: Border.all(
child: Text( color: Colors.blue.shade200,
'Total: ${paymentMethodData.total?.currencyFormatRpV2 ?? 'Rp 0'}',
style: const TextStyle(
color: AppColors.white,
fontWeight: FontWeight.w600,
),
),
),
],
),
],
),
),
// CONTENT
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
// TABLE HEADER
Row(
children: headerWidgets,
),
// TABLE BODY
...paymentMethodData.paymentMethods?.map((item) {
return Container(
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(
color: AppColors.stroke,
width: 1, width: 1,
), ),
), ),
child: Text(
searchDateFormatted,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.blue.shade700,
),
),
),
],
),
const SpaceHeight(24), // Summary Cards
Row(
children: [
Expanded(
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.green.shade50,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: Colors.green.shade200,
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Pendapatan Total',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.green.shade700,
),
),
const SizedBox(height: 8),
Text(
paymentMethodData
.summary.totalAmount.currencyFormatRpV2,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.green.shade800,
),
),
],
),
),
),
const SizedBox(width: 12),
Expanded(
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.orange.shade50,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: Colors.orange.shade200,
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Jumlah Pesanan',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.orange.shade700,
),
),
const SizedBox(height: 8),
Text(
paymentMethodData.summary.totalOrders.toString(),
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.orange.shade800,
),
),
],
),
),
),
],
),
const SpaceHeight(16),
// Average Order Value Card
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.purple.shade50,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: Colors.purple.shade200,
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Nilai Pesanan Rata-rata',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.purple.shade700,
),
),
const SizedBox(height: 8),
Text(
paymentMethodData.summary.averageOrderValue
.round()
.currencyFormatRpV2,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.purple.shade800,
),
),
],
),
),
const SpaceHeight(24),
// Payment Methods Section
Text(
'Rincian Metode Pembayaran',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.grey.shade800,
),
),
const SpaceHeight(16),
// Payment Method Item
...List.generate(paymentMethodData.data.length, (index) {
final item = paymentMethodData.data[index];
return Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Column(
children: [
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.grey.shade50,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: Colors.grey.shade200,
width: 1,
),
), ),
child: Row( child: Row(
children: [ children: [
_getBodyItemWidget( // Payment Method Icon
item.paymentMethod ?? '-', Container(
180, width: 40,
), height: 40,
_getBodyItemWidget( decoration: BoxDecoration(
item.totalAmount?.currencyFormatRpV2 ?? 'Rp 0', color: AppColors.primary,
180, borderRadius: BorderRadius.circular(8),
), border: Border.all(
_getBodyItemWidget( color: AppColors.primary,
item.transactionCount?.toString() ?? '0', width: 1,
180,
),
],
),
);
}).toList() ??
[],
],
), ),
), ),
child: Icon(
Icons.money,
color: AppColors.white,
size: 20,
), ),
],
), ),
);
}
Widget _getBodyItemWidget(String label, double width) { const SizedBox(width: 16),
return Container(
width: width, // Payment Method Details
height: 56, Expanded(
alignment: Alignment.centerLeft, child: Column(
padding: const EdgeInsets.symmetric(horizontal: 16.0), crossAxisAlignment: CrossAxisAlignment.start,
child: Text( children: [
label, Row(
style: const TextStyle( mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
item.paymentMethodName,
style: TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.grey.shade800,
),
),
Text(
"${item.percentage}%",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: AppColors.primary,
),
),
],
),
const SizedBox(height: 8),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
item.totalAmount.currencyFormatRpV2,
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: Colors.grey.shade600,
),
),
Text(
'${item.orderCount} Pesanan',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: Colors.grey.shade600,
),
),
],
),
const SizedBox(height: 8),
// Progress Bar
Container(
width: double.infinity,
height: 6,
decoration: BoxDecoration(
color: Colors.grey.shade200,
borderRadius: BorderRadius.circular(3),
),
child: FractionallySizedBox(
alignment: Alignment.centerLeft,
widthFactor:
(item.percentage / 100), // 100%
child: Container(
decoration: BoxDecoration(
color: AppColors.primary,
borderRadius:
BorderRadius.circular(3),
),
),
),
),
],
),
),
],
),
),
],
),
);
}),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: Colors.blue.shade200,
width: 1,
),
),
child: Row(
children: [
Icon(
Icons.info_outline,
color: Colors.blue.shade600,
size: 16,
),
const SizedBox(width: 8),
Expanded(
child: Text(
'All payments processed successfully with 100% cash transactions',
style: TextStyle(
fontSize: 12,
color: Colors.blue.shade700,
),
),
),
],
),
),
],
),
), ),
), ),
); );