feat: payment method report

This commit is contained in:
efrilm
2025-08-06 11:23:03 +07:00
parent ae4f7b06ce
commit fd254c22fd
9 changed files with 608 additions and 164 deletions
@@ -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: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';
class PaymentMethodReportWidget extends StatelessWidget {
final PaymentMethodData paymentMethodData;
final PaymentMethodAnalyticData paymentMethodData;
final String title;
final String searchDateFormatted;
final List<Widget> headerWidgets;
@@ -23,127 +23,343 @@ class PaymentMethodReportWidget extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.white,
body: Column(
children: [
// HEADER
Container(
padding: const EdgeInsets.all(24.0),
decoration: const BoxDecoration(
color: AppColors.white,
border: Border(
bottom: BorderSide(
color: AppColors.stroke,
width: 1,
),
),
body: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
border: Border(
left: BorderSide(
color: Colors.grey.shade200,
width: 1,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
),
),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header Section
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.grey.shade800,
),
),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: Colors.blue.shade200,
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(
title,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.w600,
'Nilai Pesanan Rata-rata',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.purple.shade700,
),
),
const SpaceHeight(8.0),
const SizedBox(height: 8),
Text(
searchDateFormatted,
style: const TextStyle(
fontSize: 16,
color: AppColors.grey,
paymentMethodData.summary.averageOrderValue
.round()
.currencyFormatRpV2,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.purple.shade800,
),
),
],
),
Row(
),
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(
children: [
// Payment Method Icon
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: AppColors.primary,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: AppColors.primary,
width: 1,
),
),
child: Icon(
Icons.money,
color: AppColors.white,
size: 20,
),
),
const SizedBox(width: 16),
// Payment Method Details
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
item.paymentMethodName,
style: TextStyle(
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: [
Container(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 8.0,
),
decoration: BoxDecoration(
color: AppColors.primary,
borderRadius: BorderRadius.circular(8.0),
),
Icon(
Icons.info_outline,
color: Colors.blue.shade600,
size: 16,
),
const SizedBox(width: 8),
Expanded(
child: Text(
'Total: ${paymentMethodData.total?.currencyFormatRpV2 ?? 'Rp 0'}',
style: const TextStyle(
color: AppColors.white,
fontWeight: FontWeight.w600,
'All payments processed successfully with 100% cash transactions',
style: TextStyle(
fontSize: 12,
color: Colors.blue.shade700,
),
),
),
],
),
],
),
),
// 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,
),
),
),
child: Row(
children: [
_getBodyItemWidget(
item.paymentMethod ?? '-',
180,
),
_getBodyItemWidget(
item.totalAmount?.currencyFormatRpV2 ?? 'Rp 0',
180,
),
_getBodyItemWidget(
item.transactionCount?.toString() ?? '0',
180,
),
],
),
);
}).toList() ??
[],
],
),
),
],
),
],
),
);
}
Widget _getBodyItemWidget(String label, double width) {
return Container(
width: width,
height: 56,
alignment: Alignment.centerLeft,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Text(
label,
style: const TextStyle(
fontSize: 14,
),
),
);