dev #1

Merged
aefril merged 128 commits from dev into main 2025-08-13 17:19:48 +00:00
9 changed files with 608 additions and 164 deletions
Showing only changes of commit fd254c22fd - Show all commits

View File

@ -0,0 +1,47 @@
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: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(response.data.toString());
}
} 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

@ -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';
@ -198,7 +199,8 @@ class _MyAppState extends State<MyApp> {
create: (context) => ItemSalesReportBloc(OrderItemRemoteDatasource()), create: (context) => ItemSalesReportBloc(OrderItemRemoteDatasource()),
), ),
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: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

@ -202,12 +202,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,

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,
),
),
),
],
),
),
],
),
), ),
), ),
); );