feat: report page

This commit is contained in:
efrilm 2025-08-01 18:27:40 +07:00
parent 8e4a289625
commit e825e5daed
21 changed files with 851 additions and 776 deletions

View File

@ -1,4 +1,4 @@
# EnakloPOS
# ApskelPOS
A new Flutter project.

View File

@ -11,7 +11,7 @@
<!-- Izin khusus untuk akses foto (media images) di Android 33 ke atas -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<application
android:label="EnakloPOS"
android:label="ApskelPOS"
android:name="${applicationName}"
android:icon="@mipmap/launcher_icon">
<activity

View File

@ -5,7 +5,7 @@ import 'lib/core/utils/app_icon_generator.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
print('Generating EnakloPOS app icon...');
print('Generating ApskelPOS app icon...');
try {
final iconData = await AppIconGenerator.generateAppIcon();
@ -20,14 +20,14 @@ void main() async {
final iconFile = File('assets/logo/logo_app_icon.png');
await iconFile.writeAsBytes(iconData);
print('✅ App icon generated successfully at: assets/logo/logo_app_icon.png');
print(
'✅ App icon generated successfully at: assets/logo/logo_app_icon.png');
print('📱 The icon features:');
print(' - White background for visibility');
print(' - Blue circular background');
print(' - Gift box with "e" inside');
print(' - "ENAKLO" and "POS" text');
print(' - 1024x1024 resolution for high quality');
} catch (e) {
print('❌ Error generating app icon: $e');
}

View File

@ -5,7 +5,7 @@
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>EnakloPOS</string>
<string>ApskelPOS</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>

View File

@ -38,7 +38,7 @@ class ItemSalesInvoice {
return HelperPdfService.saveDocument(
name:
'Enaklo POS | Item Sales Report | ${DateTime.now().millisecondsSinceEpoch}.pdf',
'Apskel POS | Item Sales Report | ${DateTime.now().millisecondsSinceEpoch}.pdf',
pdf: pdf);
}
@ -48,7 +48,7 @@ class ItemSalesInvoice {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 1 * PdfPageFormat.cm),
Text('Enaklo POS | Item Sales Report',
Text('Apskel POS | Item Sales Report',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,

View File

@ -28,7 +28,8 @@ class RevenueInvoice {
// Load logo image
log("Loading logo image...");
final ByteData dataImage = await rootBundle.load('assets/images/logo.png');
final ByteData dataImage =
await rootBundle.load('assets/images/logo.png');
final Uint8List bytes = dataImage.buffer.asUint8List();
final image = pw.MemoryImage(bytes);
log("Logo image loaded successfully, size: ${bytes.length} bytes");
@ -49,7 +50,7 @@ class RevenueInvoice {
log("Saving PDF document...");
return HelperPdfService.saveDocument(
name:
'Enaklo POS | Summary Sales Report | ${DateTime.now().millisecondsSinceEpoch}.pdf',
'Apskel POS | Summary Sales Report | ${DateTime.now().millisecondsSinceEpoch}.pdf',
pdf: pdf,
);
} catch (e) {
@ -69,7 +70,7 @@ class RevenueInvoice {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 1 * PdfPageFormat.cm),
Text('Enaklo POS | Summary Sales Report',
Text('Apskel POS | Summary Sales Report',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
@ -125,7 +126,8 @@ class RevenueInvoice {
buildText(
title: 'Discount',
titleStyle: TextStyle(fontWeight: FontWeight.normal),
value: "- ${safeParseInt(summaryModel.totalDiscount).currencyFormatRp}",
value:
"- ${safeParseInt(summaryModel.totalDiscount).currencyFormatRp}",
unite: true,
textStyle: TextStyle(
color: PdfColor.fromHex('#FF0000'),
@ -147,7 +149,8 @@ class RevenueInvoice {
titleStyle: TextStyle(
fontWeight: FontWeight.normal,
),
value: safeParseInt(summaryModel.totalServiceCharge).currencyFormatRp,
value:
safeParseInt(summaryModel.totalServiceCharge).currencyFormatRp,
unite: true,
),
Divider(),

View File

@ -38,7 +38,7 @@ class TransactionSalesInvoice {
return HelperPdfService.saveDocument(
name:
'Enaklo POS | Transaction Sales Report | ${DateTime.now().millisecondsSinceEpoch}.pdf',
'Apskel POS | Transaction Sales Report | ${DateTime.now().millisecondsSinceEpoch}.pdf',
pdf: pdf);
}
@ -48,7 +48,7 @@ class TransactionSalesInvoice {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 1 * PdfPageFormat.cm),
Text('Enaklo POS | Transaction Sales Report',
Text('Apskel POS | Transaction Sales Report',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,

View File

@ -35,7 +35,7 @@ class PrintDataoutputs {
final total = totalPrice + pajak;
bytes += generator.reset();
bytes += generator.text('Enaklo POS',
bytes += generator.text('Apskel POS',
styles: const PosStyles(
bold: true,
align: PosAlign.center,
@ -202,7 +202,7 @@ class PrintDataoutputs {
// bytes += generator.feed(3);
// }
bytes += generator.text('Enaklo POS',
bytes += generator.text('Apskel POS',
styles: const PosStyles(
bold: true,
align: PosAlign.center,
@ -487,8 +487,8 @@ class PrintDataoutputs {
String namaKasir,
String customerName,
int paper,
{int taxPercentage = 11, int serviceChargePercentage = 5}
) async {
{int taxPercentage = 11,
int serviceChargePercentage = 5}) async {
List<int> bytes = [];
final profile = await CapabilityProfile.load();
@ -778,8 +778,13 @@ class PrintDataoutputs {
return bytes;
}
Future<List<int>> printChecker(List<ProductQuantity> products,
String tableName, String draftName, String cashierName, int paper, String orderType) async {
Future<List<int>> printChecker(
List<ProductQuantity> products,
String tableName,
String draftName,
String cashierName,
int paper,
String orderType) async {
List<int> bytes = [];
final profile = await CapabilityProfile.load();
@ -908,8 +913,13 @@ class PrintDataoutputs {
return bytes;
}
Future<List<int>> printKitchen(List<ProductQuantity> products,
String tableNumber, String draftName, String cashierName, int paper, String orderType) async {
Future<List<int>> printKitchen(
List<ProductQuantity> products,
String tableNumber,
String draftName,
String cashierName,
int paper,
String orderType) async {
List<int> bytes = [];
final profile = await CapabilityProfile.load();

View File

@ -49,7 +49,7 @@ class _LoginPageState extends State<LoginPage> {
const SpaceHeight(24.0),
const Center(
child: Text(
'Enaklo POS',
'Apskel POS',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,

View File

@ -34,28 +34,29 @@ class _ReportPageState extends State<ReportPage> {
DateTime fromDate = DateTime.now().subtract(const Duration(days: 30));
DateTime toDate = DateTime.now();
@override
void initState() {
super.initState();
context.read<TransactionReportBloc>().add(
TransactionReportEvent.getReport(
startDate: DateFormatter.formatDateTime(fromDate),
endDate: DateFormatter.formatDateTime(toDate)),
);
}
@override
Widget build(BuildContext context) {
String searchDateFormatted =
'${fromDate.toFormattedDate2()} to ${toDate.toFormattedDate2()}';
return Scaffold(
body: Row(
children: [
// LEFT CONTENT
Expanded(
flex: 2,
child: Align(
alignment: Alignment.topLeft,
child: SingleChildScrollView(
padding: const EdgeInsets.all(24.0),
child: Column(
backgroundColor: AppColors.background,
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const ReportTitle(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
ReportTitle(
actionWidget: [
SizedBox(
width: 300,
child: CustomDatePicker(
prefix: const Text('From: '),
initialDate: fromDate,
@ -67,7 +68,8 @@ class _ReportPageState extends State<ReportPage> {
),
),
const SpaceWidth(24.0),
Flexible(
SizedBox(
width: 300,
child: CustomDatePicker(
prefix: const Text('To: '),
initialDate: toDate,
@ -95,20 +97,34 @@ class _ReportPageState extends State<ReportPage> {
),
],
),
Padding(
padding: const EdgeInsets.all(15.0),
child: Wrap(
Expanded(
child: Row(
children: [
// LEFT CONTENT
Expanded(
flex: 2,
child: Material(
color: AppColors.white,
child: Align(
alignment: Alignment.topLeft,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ReportMenu(
label: 'Transaction Report',
label: 'Laporan Transaksi',
subtitle:
'Menampilkan riwayat lengkap semua transaksi yang telah dilakukan.',
icon: Icons.receipt_long_outlined,
onPressed: () {
selectedMenu = 0;
title = 'Transaction Report';
title = 'Laporan Transaksi';
setState(() {});
//enddate is 1 month before the current date
context.read<TransactionReportBloc>().add(
TransactionReportEvent.getReport(
startDate: DateFormatter.formatDateTime(
startDate:
DateFormatter.formatDateTime(
fromDate),
endDate: DateFormatter.formatDateTime(
toDate)),
@ -117,14 +133,18 @@ class _ReportPageState extends State<ReportPage> {
isActive: selectedMenu == 0,
),
ReportMenu(
label: 'Item Sales Report',
label: 'Laporan Penjualan Item',
subtitle:
'Laporan penjualan berdasarkan masing-masing item atau produk.',
icon: Icons.inventory_2_outlined,
onPressed: () {
selectedMenu = 1;
title = 'Item Sales Report';
title = 'Laporan Penjualan Item';
setState(() {});
context.read<ItemSalesReportBloc>().add(
ItemSalesReportEvent.getItemSales(
startDate: DateFormatter.formatDateTime(
startDate:
DateFormatter.formatDateTime(
fromDate),
endDate: DateFormatter.formatDateTime(
toDate)),
@ -133,28 +153,36 @@ class _ReportPageState extends State<ReportPage> {
isActive: selectedMenu == 1,
),
ReportMenu(
label: 'Product Sales Chart',
label: 'Chart Penjualan Produk',
subtitle:
'Grafik visual penjualan produk untuk analisa performa penjualan.',
icon: Icons.bar_chart_outlined,
onPressed: () {
selectedMenu = 2;
title = 'Product Sales Chart';
title = 'Chart Penjualan Produk';
setState(() {});
context.read<ProductSalesBloc>().add(
ProductSalesEvent.getProductSales(
DateFormatter.formatDateTime(fromDate),
DateFormatter.formatDateTime(
fromDate),
DateFormatter.formatDateTime(toDate)),
);
},
isActive: selectedMenu == 2,
),
ReportMenu(
label: 'Summary Sales Report',
label: 'Ringkasan Laporan Penjualan',
subtitle:
'Ringkasan total penjualan dalam periode tertentu.',
icon: Icons.insert_drive_file_outlined,
onPressed: () {
selectedMenu = 3;
title = 'Summary Sales Report';
title = 'Ringkasan Laporan Penjualan';
setState(() {});
context.read<SummaryBloc>().add(
SummaryEvent.getSummary(
DateFormatter.formatDateTime(fromDate),
DateFormatter.formatDateTime(
fromDate),
DateFormatter.formatDateTime(toDate)),
);
@ -163,15 +191,23 @@ class _ReportPageState extends State<ReportPage> {
isActive: selectedMenu == 3,
),
ReportMenu(
label: 'Payment Method Report',
label: 'Laporan Metode Pembayaran',
subtitle:
'Laporan metode pembayaran yang digunakan.',
icon: Icons.payment_outlined,
onPressed: () {
selectedMenu = 4;
title = 'Payment Method Report';
title = 'Laporan Metode Pembayaran';
setState(() {});
context.read<PaymentMethodReportBloc>().add(
PaymentMethodReportEvent.getPaymentMethodReport(
startDate: DateFormatter.formatDateTime(fromDate),
endDate: DateFormatter.formatDateTime(toDate)),
PaymentMethodReportEvent
.getPaymentMethodReport(
startDate:
DateFormatter.formatDateTime(
fromDate),
endDate:
DateFormatter.formatDateTime(
toDate)),
);
},
isActive: selectedMenu == 4,
@ -179,17 +215,16 @@ class _ReportPageState extends State<ReportPage> {
],
),
),
],
),
),
),
),
// RIGHT CONTENT
Expanded(
flex: 2,
flex: 4,
child: selectedMenu == 0
? BlocBuilder<TransactionReportBloc, TransactionReportState>(
? BlocBuilder<TransactionReportBloc,
TransactionReportState>(
builder: (context, state) {
return state.maybeWhen(
orElse: () => const Center(
@ -210,7 +245,8 @@ class _ReportPageState extends State<ReportPage> {
},
)
: selectedMenu == 1
? BlocBuilder<ItemSalesReportBloc, ItemSalesReportState>(
? BlocBuilder<ItemSalesReportBloc,
ItemSalesReportState>(
builder: (context, state) {
return state.maybeWhen(
orElse: () => const Center(
@ -223,15 +259,18 @@ class _ReportPageState extends State<ReportPage> {
return ItemSalesReportWidget(
itemSales: itemSales,
title: title,
searchDateFormatted: searchDateFormatted,
headerWidgets: _getItemSalesPageWidget(),
searchDateFormatted:
searchDateFormatted,
headerWidgets:
_getItemSalesPageWidget(),
);
},
);
},
)
: selectedMenu == 2
? BlocBuilder<ProductSalesBloc, ProductSalesState>(
? BlocBuilder<ProductSalesBloc,
ProductSalesState>(
builder: (context, state) {
return state.maybeWhen(
orElse: () => const Center(
@ -243,7 +282,8 @@ class _ReportPageState extends State<ReportPage> {
success: (productSales) {
return ProductSalesChartWidgets(
title: title,
searchDateFormatted: searchDateFormatted,
searchDateFormatted:
searchDateFormatted,
productSales: productSales,
);
},
@ -255,7 +295,8 @@ class _ReportPageState extends State<ReportPage> {
builder: (context, state) {
return state.maybeWhen(
orElse: () => const Center(
child: CircularProgressIndicator(),
child:
CircularProgressIndicator(),
),
error: (message) {
return Text(message);
@ -264,28 +305,34 @@ class _ReportPageState extends State<ReportPage> {
return SummaryReportWidget(
summary: summary,
title: title,
searchDateFormatted: searchDateFormatted,
searchDateFormatted:
searchDateFormatted,
);
},
);
},
)
: selectedMenu == 4
? BlocBuilder<PaymentMethodReportBloc, PaymentMethodReportState>(
? BlocBuilder<PaymentMethodReportBloc,
PaymentMethodReportState>(
builder: (context, state) {
return state.maybeWhen(
orElse: () => const Center(
child: CircularProgressIndicator(),
child:
CircularProgressIndicator(),
),
error: (message) {
return Text(message);
},
loaded: (paymentMethodData) {
return PaymentMethodReportWidget(
paymentMethodData: paymentMethodData,
paymentMethodData:
paymentMethodData,
title: title,
searchDateFormatted: searchDateFormatted,
headerWidgets: _getPaymentMethodPageWidget(),
searchDateFormatted:
searchDateFormatted,
headerWidgets:
_getPaymentMethodPageWidget(),
);
},
);
@ -294,6 +341,9 @@ class _ReportPageState extends State<ReportPage> {
: const SizedBox.shrink()),
],
),
),
],
),
);
}
@ -314,11 +364,11 @@ class _ReportPageState extends State<ReportPage> {
List<Widget> _getItemSalesPageWidget() {
return [
_getTitleItemWidget('ID', 80),
_getTitleItemWidget('Order', 60),
_getTitleItemWidget('Product', 160),
_getTitleItemWidget('Order', 100),
_getTitleItemWidget('Product', 200),
_getTitleItemWidget('Qty', 60),
_getTitleItemWidget('Price', 140),
_getTitleItemWidget('Total Price', 140),
_getTitleItemWidget('Price', 150),
_getTitleItemWidget('Total Price', 160),
];
}

View File

@ -4,12 +4,12 @@ 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/utils/helper_pdf_service.dart';
import 'package:enaklo_pos/presentation/report/widgets/report_page_title.dart';
import 'package:flutter/material.dart';
import 'package:enaklo_pos/core/utils/item_sales_invoice.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';
import 'package:permission_handler/permission_handler.dart';
class ItemSalesReportWidget extends StatelessWidget {
final String title;
@ -26,32 +26,12 @@ class ItemSalesReportWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Card(
color: const Color.fromARGB(255, 255, 255, 255),
child: Column(
return Column(
children: [
const SpaceHeight(24.0),
Center(
child: Text(
title,
style:
const TextStyle(fontWeight: FontWeight.w800, fontSize: 16.0),
),
),
const SizedBox(
height: 8.0,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
searchDateFormatted,
style: const TextStyle(fontSize: 16.0),
),
GestureDetector(
onTap: () async {
ReportPageTitle(
title: title,
searchDateFormatted: searchDateFormatted,
onExport: () async {
try {
final status = await PermessionHelper().checkPermission();
if (status) {
@ -77,25 +57,6 @@ class ItemSalesReportWidget extends StatelessWidget {
);
}
},
child: const Row(
children: [
Text(
"PDF",
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.bold,
color: AppColors.primary,
),
),
Icon(
Icons.download_outlined,
color: AppColors.primary,
)
],
),
),
],
),
),
const SpaceHeight(16.0),
Expanded(
@ -105,7 +66,7 @@ class ItemSalesReportWidget extends StatelessWidget {
borderRadius: BorderRadius.circular(12),
child: HorizontalDataTable(
leftHandSideColumnWidth: 80,
rightHandSideColumnWidth: 560,
rightHandSideColumnWidth: 670,
isFixedHeader: true,
headerWidgets: headerWidgets,
@ -116,37 +77,35 @@ class ItemSalesReportWidget extends StatelessWidget {
width: 80,
height: 52,
alignment: Alignment.centerLeft,
child:
Center(child: Text(itemSales[index].id.toString())),
child: Center(child: Text(itemSales[index].id.toString())),
);
},
rightSideItemBuilder: (context, index) {
return Row(
children: <Widget>[
Container(
width: 60,
width: 100,
height: 52,
alignment: Alignment.centerLeft,
child: Center(
child: Text(itemSales[index].orderId.toString())),
),
Container(
width: 160,
width: 200,
height: 52,
alignment: Alignment.centerLeft,
child: Center(
child: Text(itemSales[index].productName!)),
child:
Center(child: Text(itemSales[index].productName!)),
),
Container(
width: 60,
height: 52,
alignment: Alignment.centerLeft,
child: Center(
child:
Text(itemSales[index].quantity.toString())),
child: Text(itemSales[index].quantity.toString())),
),
Container(
width: 140,
width: 150,
height: 52,
padding: const EdgeInsets.fromLTRB(5, 0, 0, 0),
alignment: Alignment.centerLeft,
@ -156,14 +115,13 @@ class ItemSalesReportWidget extends StatelessWidget {
)),
),
Container(
width: 140,
width: 160,
height: 52,
padding: const EdgeInsets.fromLTRB(5, 0, 0, 0),
alignment: Alignment.centerLeft,
child: Center(
child: Text(
(itemSales[index].price! *
itemSales[index].quantity!)
(itemSales[index].price! * itemSales[index].quantity!)
.currencyFormatRp,
)),
),
@ -185,7 +143,6 @@ class ItemSalesReportWidget extends StatelessWidget {
),
),
],
),
);
}
}

View File

@ -1,9 +1,7 @@
import 'package:flutter/material.dart';
import 'package:enaklo_pos/core/constants/colors.dart';
import 'package:enaklo_pos/core/extensions/int_ext.dart';
import 'package:enaklo_pos/core/extensions/string_ext.dart';
import 'package:enaklo_pos/data/models/response/payment_method_response_model.dart';
import 'package:enaklo_pos/presentation/report/widgets/report_title.dart';
import '../../../core/components/spaces.dart';

View File

@ -1,6 +1,7 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'package:enaklo_pos/core/components/spaces.dart';
import 'package:enaklo_pos/presentation/report/widgets/report_page_title.dart';
import 'package:flutter/material.dart';
import 'package:pie_chart/pie_chart.dart';
@ -67,27 +68,26 @@ class _ProductSalesChartWidgetsState extends State<ProductSalesChartWidgets> {
@override
Widget build(BuildContext context) {
return Card(
color: const Color.fromARGB(255, 255, 255, 255),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 16),
child: Column(
return Column(
children: [
const SpaceHeight(24.0),
Center(
child: Text(
widget.title,
style: const TextStyle(
fontWeight: FontWeight.w800, fontSize: 16.0),
),
),
Center(
child: Text(
widget.searchDateFormatted,
style: const TextStyle(fontSize: 16.0),
),
ReportPageTitle(
title: widget.title,
searchDateFormatted: widget.searchDateFormatted,
onExport: () async {},
isExport: false, // Set to false if export is not needed
),
const SpaceHeight(16.0),
Expanded(
child: SingleChildScrollView(
child: Container(
padding: const EdgeInsets.all(16.0),
margin: const EdgeInsets.all(12.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: Column(
children: [
PieChart(
dataMap: dataMap2,
animationDuration: Duration(milliseconds: 800),
@ -116,10 +116,13 @@ class _ProductSalesChartWidgetsState extends State<ProductSalesChartWidgets> {
),
// gradientList: ---To add gradient colors---
// emptyColorGradient: ---Empty Color gradient---
)
),
],
),
),
),
)
],
);
}
}

View File

@ -1,13 +1,12 @@
import 'package:flutter/material.dart';
import 'package:enaklo_pos/core/assets/assets.gen.dart';
import '../../../core/components/spaces.dart';
import '../../../core/constants/colors.dart';
class ReportMenu extends StatelessWidget {
final String label;
final String subtitle;
final IconData icon;
final VoidCallback onPressed;
final bool isActive;
@ -16,6 +15,8 @@ class ReportMenu extends StatelessWidget {
required this.label,
required this.onPressed,
required this.isActive,
required this.subtitle,
required this.icon,
});
@override
@ -23,38 +24,43 @@ class ReportMenu extends StatelessWidget {
return GestureDetector(
onTap: onPressed,
child: Container(
margin: const EdgeInsets.all(5.0),
width: 180.0,
height: 160.0,
alignment: Alignment.center,
padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration(
color:
isActive ? AppColors.primary.withOpacity(0.13) : AppColors.white,
borderRadius: BorderRadius.circular(18.0),
border: Border.all(
color: isActive ? AppColors.primary : AppColors.stroke,
border: Border(
right: BorderSide(
color: isActive ? AppColors.primary : Colors.transparent,
width: 4.0,
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
),
child: Row(
children: [
Assets.icons.report.svg(
colorFilter: isActive
? const ColorFilter.mode(
AppColors.primary,
BlendMode.srcIn,
)
: null,
Icon(
icon,
size: 24.0,
color: isActive ? AppColors.primary : AppColors.grey,
),
const SpaceHeight(28.0),
const SpaceWidth(12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: TextStyle(
color: isActive ? AppColors.primary : AppColors.black,
fontWeight: FontWeight.w600,
style: const TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4.0),
Text(
subtitle,
style:
const TextStyle(fontSize: 14.0, color: AppColors.grey),
),
],
),
),
const SpaceHeight(24.0),
],
),
),

View File

@ -0,0 +1,65 @@
import 'package:enaklo_pos/core/constants/colors.dart';
import 'package:flutter/material.dart';
class ReportPageTitle extends StatelessWidget {
final String title;
final String searchDateFormatted;
final Function() onExport;
final bool isExport;
const ReportPageTitle(
{super.key,
required this.title,
required this.searchDateFormatted,
required this.onExport,
this.isExport = true});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(16.0),
decoration: BoxDecoration(color: AppColors.white),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontWeight: FontWeight.w800, fontSize: 16.0),
),
const SizedBox(
height: 8.0,
),
Text(
searchDateFormatted,
style: const TextStyle(fontSize: 16.0),
),
],
),
if (isExport)
GestureDetector(
onTap: onExport,
child: const Row(
children: [
Text(
"PDF",
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.bold,
color: AppColors.primary,
),
),
Icon(
Icons.download_outlined,
color: AppColors.primary,
)
],
),
),
],
),
);
}
}

View File

@ -1,38 +1,60 @@
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
import 'package:flutter/material.dart';
import 'package:enaklo_pos/core/components/components.dart';
import 'package:enaklo_pos/core/extensions/date_time_ext.dart';
import '../../../core/constants/colors.dart';
class ReportTitle extends StatelessWidget {
const ReportTitle({super.key});
final List<Widget>? actionWidget;
const ReportTitle({super.key, this.actionWidget});
@override
Widget build(BuildContext context) {
return Column(
return Container(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 10.0,
),
width: double.infinity,
height: context.deviceHeight * 0.1,
decoration: const BoxDecoration(
color: AppColors.white,
border: Border(
bottom: BorderSide(
color: AppColors.background,
width: 1.0,
),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Report',
'Laporan',
style: TextStyle(
color: AppColors.primary,
fontSize: 28,
color: AppColors.black,
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
const SpaceHeight(4.0),
Text(
DateTime.now().toFormattedDate(),
style: const TextStyle(
color: AppColors.subtitle,
fontSize: 16,
DateTime.now().toFormattedDate2(),
style: TextStyle(
color: AppColors.grey,
fontSize: 14,
),
),
const SpaceHeight(20.0),
const Divider(),
],
),
if (actionWidget != null)
Row(
children: actionWidget!,
),
],
),
);
}
}

View File

@ -9,7 +9,6 @@ import 'package:enaklo_pos/core/utils/helper_pdf_service.dart';
import 'package:enaklo_pos/core/utils/permession_handler.dart';
import 'package:enaklo_pos/data/models/response/summary_response_model.dart';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import '../../../core/utils/revenue_invoice.dart';
@ -55,7 +54,8 @@ class SummaryReportWidget extends StatelessWidget {
log("PDF button clicked for summary report");
try {
log("Checking permissions...");
final status = await PermessionHelper().checkPermission();
final status =
await PermessionHelper().checkPermission();
log("Permission status: $status");
if (status) {
@ -75,7 +75,8 @@ class SummaryReportWidget extends StatelessWidget {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('PDF saved successfully: ${pdfFile.path}'),
content: Text(
'PDF saved successfully: ${pdfFile.path}'),
backgroundColor: Colors.green,
),
);
@ -83,7 +84,8 @@ class SummaryReportWidget extends StatelessWidget {
log("Permission denied");
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Storage permission is required to save PDF'),
content: Text(
'Storage permission is required to save PDF'),
backgroundColor: Colors.red,
),
);

View File

@ -5,12 +5,12 @@ import 'package:enaklo_pos/core/constants/colors.dart';
import 'package:enaklo_pos/core/extensions/date_time_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/presentation/report/widgets/report_page_title.dart';
import 'package:flutter/material.dart';
import 'package:enaklo_pos/core/utils/permession_handler.dart';
import 'package:enaklo_pos/core/utils/transaction_sales_invoice.dart';
import 'package:enaklo_pos/data/models/response/order_remote_datasource.dart';
import 'package:horizontal_data_table/horizontal_data_table.dart';
import 'package:permission_handler/permission_handler.dart';
class TransactionReportWidget extends StatelessWidget {
final String title;
@ -27,32 +27,12 @@ class TransactionReportWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Card(
color: const Color.fromARGB(255, 255, 255, 255),
child: Column(
return Column(
children: [
const SpaceHeight(24.0),
Center(
child: Text(
title,
style:
const TextStyle(fontWeight: FontWeight.w800, fontSize: 16.0),
),
),
const SizedBox(
height: 8.0,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
searchDateFormatted,
style: const TextStyle(fontSize: 16.0),
),
GestureDetector(
onTap: () async {
ReportPageTitle(
title: title,
searchDateFormatted: searchDateFormatted,
onExport: () async {
try {
final status = await PermessionHelper().checkPermission();
if (status) {
@ -78,25 +58,6 @@ class TransactionReportWidget extends StatelessWidget {
);
}
},
child: const Row(
children: [
Text(
"PDF",
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.bold,
color: AppColors.primary,
),
),
Icon(
Icons.download_outlined,
color: AppColors.primary,
)
],
),
),
],
),
),
const SpaceHeight(16.0),
Expanded(
@ -228,7 +189,6 @@ class TransactionReportWidget extends StatelessWidget {
),
),
],
),
);
}
}

View File

@ -32,7 +32,7 @@ class _SalesPageState extends State<SalesPage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'Enaklo POS ',
'Apskel POS ',
style: TextStyle(
color: AppColors.primary,
fontSize: 22,

View File

@ -171,8 +171,7 @@ class _ManagePrinterPageState extends State<ManagePrinterPage> {
//bytes += generator.setGlobalFont(PosFontType.fontA);
bytes += generator.reset();
bytes +=
generator.text('Enaklo POS', styles: const PosStyles(bold: true));
bytes += generator.text('Apskel POS', styles: const PosStyles(bold: true));
bytes +=
generator.text('Reverse text', styles: const PosStyles(reverse: true));
bytes += generator.text('Underlined text',

View File

@ -1,5 +1,5 @@
name: enaklo_pos
description: "EnakloPOS - Point of Sale Application"
description: "ApskelPOS - Point of Sale Application"
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: "none" # Remove this line if you wish to publish to pub.dev