fear: rev 2

This commit is contained in:
efrilm 2025-09-02 19:49:01 +07:00
parent 3624f75bea
commit 6599e6fe7c
15 changed files with 563 additions and 1023 deletions

View File

@ -35,18 +35,25 @@ Future<void> onPrint(
// Checker printer // Checker printer
if (checkerPrinter != null) { if (checkerPrinter != null) {
try { try {
final productByPrinter = productQuantity
.where((item) => item.product.printerType == 'checker')
.toList();
final printValue = await PrintDataoutputs.instance.printChecker( final printValue = await PrintDataoutputs.instance.printChecker(
productQuantity, productByPrinter,
order.tableNumber ?? "", order.tableNumber ?? "",
order.orderNumber ?? "", order.orderNumber ?? "",
authData.user?.name ?? "", authData.user?.name ?? "",
order.metadata?['customer_name'] ?? "",
checkerPrinter.paper.toIntegerFromText, checkerPrinter.paper.toIntegerFromText,
order.orderType ?? "", order.orderType ?? "",
); );
await PrinterService() if (productByPrinter.isNotEmpty) {
// ignore: use_build_context_synchronously await PrinterService()
.printWithPrinter(checkerPrinter, printValue, context); // ignore: use_build_context_synchronously
.printWithPrinter(checkerPrinter, printValue, context);
}
} catch (e) { } catch (e) {
log("Error printing checker: $e"); log("Error printing checker: $e");
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
@ -58,17 +65,23 @@ Future<void> onPrint(
// Kitchen printer // Kitchen printer
if (kitchenPrinter != null) { if (kitchenPrinter != null) {
try { try {
final productByPrinter = productQuantity
.where((item) => item.product.printerType == 'kitchen')
.toList();
final printValue = await PrintDataoutputs.instance.printKitchen( final printValue = await PrintDataoutputs.instance.printKitchen(
productQuantity, productByPrinter,
order.tableNumber!, order.tableNumber!,
order.orderNumber ?? "", order.orderNumber ?? "",
authData.user?.name ?? "", authData.user?.name ?? "",
order.metadata?['customer_name'] ?? "",
kitchenPrinter.paper.toIntegerFromText, kitchenPrinter.paper.toIntegerFromText,
order.orderType ?? "", order.orderType ?? "",
); );
await PrinterService() if (productByPrinter.isNotEmpty) {
.printWithPrinter(kitchenPrinter, printValue, context); await PrinterService()
.printWithPrinter(kitchenPrinter, printValue, context);
}
} catch (e) { } catch (e) {
log("Error printing kitchen order: $e"); log("Error printing kitchen order: $e");
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
@ -80,17 +93,24 @@ Future<void> onPrint(
// Bar printer // Bar printer
if (barPrinter != null) { if (barPrinter != null) {
try { try {
final productByPrinter = productQuantity
.where((item) => item.product.printerType == 'bar')
.toList();
final printValue = await PrintDataoutputs.instance.printBar( final printValue = await PrintDataoutputs.instance.printBar(
productQuantity, productByPrinter,
order.tableNumber ?? "", order.tableNumber ?? "",
order.orderNumber ?? "", order.orderNumber ?? "",
authData.user?.name ?? "", authData.user?.name ?? "",
order.metadata?['customer_name'] ?? "",
barPrinter.paper.toIntegerFromText, barPrinter.paper.toIntegerFromText,
order.orderType ?? "", order.orderType ?? "",
); );
await PrinterService() if (productByPrinter.isNotEmpty) {
.printWithPrinter(barPrinter, printValue, context); await PrinterService()
.printWithPrinter(barPrinter, printValue, context);
}
} catch (e) { } catch (e) {
log("Error printing bar order: $e"); log("Error printing bar order: $e");
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(

View File

@ -1275,6 +1275,7 @@ class PrintDataoutputs {
String tableName, String tableName,
String draftName, String draftName,
String cashierName, String cashierName,
String customerName,
int paper, int paper,
String orderType) async { String orderType) async {
List<int> bytes = []; List<int> bytes = [];
@ -1330,9 +1331,6 @@ class PrintDataoutputs {
styles: const PosStyles(align: PosAlign.right), styles: const PosStyles(align: PosAlign.right),
), ),
]); ]);
// bytes += generator.text(
// 'Receipt: JF-${DateFormat('yyyyMMddhhmm').format(DateTime.now())}',
// styles: const PosStyles(bold: false, align: PosAlign.left));
//cashier name //cashier name
bytes += generator.row([ bytes += generator.row([
PosColumn( PosColumn(
@ -1352,7 +1350,7 @@ class PrintDataoutputs {
//column 2 //column 2
bytes += generator.row([ bytes += generator.row([
PosColumn( PosColumn(
text: 'Customer - $draftName', text: '$customerName - $draftName',
width: 6, width: 6,
styles: const PosStyles(align: PosAlign.left), styles: const PosStyles(align: PosAlign.left),
), ),
@ -1410,6 +1408,7 @@ class PrintDataoutputs {
String tableNumber, String tableNumber,
String draftName, String draftName,
String cashierName, String cashierName,
String customerName,
int paper, int paper,
String orderType) async { String orderType) async {
List<int> bytes = []; List<int> bytes = [];
@ -1462,14 +1461,26 @@ class PrintDataoutputs {
styles: const PosStyles(align: PosAlign.left), styles: const PosStyles(align: PosAlign.left),
), ),
PosColumn( PosColumn(
text: 'JF-${DateFormat('yyyyMMddhhmm').format(DateTime.now())}', text: DateFormat('yyyyMMddhhmm').format(DateTime.now()),
width: 6, width: 6,
styles: const PosStyles(align: PosAlign.right), styles: const PosStyles(align: PosAlign.right),
), ),
]); ]);
bytes += generator.row([ bytes += generator.row([
PosColumn( PosColumn(
text: 'Customer - $draftName', text: 'Cashier',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: cashierName,
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.row([
PosColumn(
text: '$customerName - $draftName',
width: 6, width: 6,
styles: const PosStyles(align: PosAlign.left), styles: const PosStyles(align: PosAlign.left),
), ),
@ -1524,8 +1535,15 @@ class PrintDataoutputs {
return bytes; return bytes;
} }
Future<List<int>> printBar(List<ProductQuantity> products, String tableNumber, Future<List<int>> printBar(
String draftName, String cashierName, int paper, String orderType) async { List<ProductQuantity> products,
String tableNumber,
String draftName,
String cashierName,
String customerName,
int paper,
String orderType,
) async {
List<int> bytes = []; List<int> bytes = [];
final profile = await CapabilityProfile.load(); final profile = await CapabilityProfile.load();
@ -1576,14 +1594,26 @@ class PrintDataoutputs {
styles: const PosStyles(align: PosAlign.left), styles: const PosStyles(align: PosAlign.left),
), ),
PosColumn( PosColumn(
text: 'JF-${DateFormat('yyyyMMddhhmm').format(DateTime.now())}', text: DateFormat('yyyyMMddhhmm').format(DateTime.now()),
width: 6, width: 6,
styles: const PosStyles(align: PosAlign.right), styles: const PosStyles(align: PosAlign.right),
), ),
]); ]);
bytes += generator.row([ bytes += generator.row([
PosColumn( PosColumn(
text: 'Customer - $draftName', text: 'Cashier',
width: 6,
styles: const PosStyles(align: PosAlign.left),
),
PosColumn(
text: cashierName,
width: 6,
styles: const PosStyles(align: PosAlign.right),
),
]);
bytes += generator.row([
PosColumn(
text: '$customerName - $draftName',
width: 6, width: 6,
styles: const PosStyles(align: PosAlign.left), styles: const PosStyles(align: PosAlign.left),
), ),

View File

@ -12,7 +12,7 @@ import 'package:intl/intl.dart';
import 'package:enaklo_pos/core/extensions/build_context_ext.dart'; import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
import 'package:enaklo_pos/presentation/home/bloc/qris/qris_bloc.dart'; import 'package:enaklo_pos/presentation/home/bloc/qris/qris_bloc.dart';
import 'package:enaklo_pos/presentation/home/widgets/success_payment_dialog.dart'; // import 'package:enaklo_pos/presentation/home/widgets/success_payment_dialog.dart';
import 'package:widgets_to_image/widgets_to_image.dart'; import 'package:widgets_to_image/widgets_to_image.dart';
import 'package:enaklo_pos/core/utils/printer_service.dart'; import 'package:enaklo_pos/core/utils/printer_service.dart';
@ -124,36 +124,36 @@ class _PaymentQrisDialogState extends State<PaymentQrisDialog> {
)); ));
}); });
}, success: (message) async { }, success: (message) async {
context.read<OrderBloc>().add(OrderEvent.order( // context.read<OrderBloc>().add(OrderEvent.order(
widget.items, // widget.items,
widget.discount, // widget.discount,
widget.discountAmount, // widget.discountAmount,
widget.tax, // widget.tax,
widget.serviceCharge, // widget.serviceCharge,
widget.paymentAmount, // widget.paymentAmount,
widget.customerName, // widget.customerName,
widget.tableNumber, // widget.tableNumber,
'completed', // 'completed',
'paid', // 'paid',
'Qris', // 'Qris',
widget.price, // widget.price,
OrderType.dineIn)); // OrderType.dineIn));
await showDialog( // await showDialog(
context: context, // context: context,
barrierDismissible: false, // barrierDismissible: false,
builder: (context) => SuccessPaymentDialog( // builder: (context) => SuccessPaymentDialog(
isTablePaymentPage: widget.isTablePaymentPage, // isTablePaymentPage: widget.isTablePaymentPage,
data: widget.items, // data: widget.items,
totalQty: widget.totalQty, // totalQty: widget.totalQty,
totalPrice: widget.price, // totalPrice: widget.price,
totalTax: widget.tax, // totalTax: widget.tax,
totalDiscount: widget.discountAmount, // totalDiscount: widget.discountAmount,
subTotal: widget.subTotal, // subTotal: widget.subTotal,
normalPrice: widget.price, // normalPrice: widget.price,
totalService: widget.serviceCharge, // totalService: widget.serviceCharge,
draftName: widget.customerName, // draftName: widget.customerName,
), // ),
); // );
}); });
}, },
child: BlocBuilder<QrisBloc, QrisState>( child: BlocBuilder<QrisBloc, QrisState>(

View File

@ -13,7 +13,7 @@ import 'package:enaklo_pos/presentation/home/bloc/order/order_bloc.dart';
import 'package:enaklo_pos/presentation/home/bloc/payment_methods/payment_methods_bloc.dart'; import 'package:enaklo_pos/presentation/home/bloc/payment_methods/payment_methods_bloc.dart';
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart'; import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
import 'package:enaklo_pos/presentation/home/models/order_type.dart'; import 'package:enaklo_pos/presentation/home/models/order_type.dart';
import 'package:enaklo_pos/presentation/home/widgets/save_order_dialog.dart'; // import 'package:enaklo_pos/presentation/home/widgets/save_order_dialog.dart';
import 'package:enaklo_pos/data/models/response/payment_methods_response_model.dart'; import 'package:enaklo_pos/data/models/response/payment_methods_response_model.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart'; import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';

View File

@ -214,8 +214,24 @@ class _HomePageState extends State<HomePage> {
final filteredProducts = final filteredProducts =
_filterProducts(products); _filterProducts(products);
if (filteredProducts.isEmpty) { if (filteredProducts.isEmpty) {
return const Center( return Center(
child: Text('No Items Found'), child: Column(
children: [
Text('No Items Found'),
SpaceHeight(20),
Button.filled(
width: 120,
onPressed: () {
context
.read<
ProductLoaderBloc>()
.add(const ProductLoaderEvent
.getProduct());
},
label: 'Retry',
),
],
),
); );
} }
return GridView.builder( return GridView.builder(

View File

@ -1,211 +0,0 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:enaklo_pos/core/extensions/string_ext.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
import 'package:enaklo_pos/data/dataoutputs/print_dataoutputs.dart';
import 'package:enaklo_pos/data/models/response/table_model.dart';
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
import '../../../core/assets/assets.gen.dart';
import '../../../core/components/buttons.dart';
import '../../../core/components/spaces.dart';
import '../../table/blocs/get_table/get_table_bloc.dart';
import '../bloc/checkout/checkout_bloc.dart';
import '../models/order_type.dart';
import 'package:enaklo_pos/core/utils/printer_service.dart';
class SaveOrderDialog extends StatefulWidget {
const SaveOrderDialog({
super.key,
required this.data,
required this.totalQty,
required this.totalPrice,
required this.totalTax,
required this.totalDiscount,
required this.subTotal,
required this.normalPrice,
required this.table,
required this.draftName,
});
final List<ProductQuantity> data;
final int totalQty;
final int totalPrice;
final int totalTax;
final int totalDiscount;
final int subTotal;
final int normalPrice;
final TableModel table;
final String draftName;
@override
State<SaveOrderDialog> createState() => _SaveOrderDialogState();
}
class _SaveOrderDialogState extends State<SaveOrderDialog> {
// List<ProductQuantity> data = [];
// int totalQty = 0;
// int totalPrice = 0;
@override
Widget build(BuildContext context) {
return AlertDialog(
content: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(child: Assets.icons.success.svg()),
const SpaceHeight(16.0),
const Center(
child: Text(
'Order Berhasil Disimpan',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
),
),
),
const SpaceHeight(20.0),
Row(
children: [
Flexible(
child: Button.outlined(
onPressed: () {
context
.read<CheckoutBloc>()
.add(const CheckoutEvent.started([]));
context
.read<GetTableBloc>()
.add(const GetTableEvent.getTables());
context.popToRoot();
},
label: 'Kembali',
),
),
const SpaceWidth(8.0),
Flexible(
child: BlocBuilder<CheckoutBloc, CheckoutState>(
builder: (context, state) {
final orderType = state.maybeWhen(
orElse: () => OrderType.dineIn,
loaded: (
items,
discountModel,
discount,
discountAmount,
tax,
serviceCharge,
totalQuantity,
totalPrice,
draftName,
orderType,
deliveryType,
) =>
orderType,
);
return Button.filled(
onPressed: () async {
final checkerPrinter = await ProductLocalDatasource
.instance
.getPrinterByCode('checker');
final kitchenPrinter = await ProductLocalDatasource
.instance
.getPrinterByCode('kitchen');
final barPrinter = await ProductLocalDatasource
.instance
.getPrinterByCode('bar');
log("Checker printer: ${checkerPrinter?.toMap()}");
log("Kitchen printer: ${kitchenPrinter?.toMap()}");
log("Bar printer: ${barPrinter?.toMap()}");
// Checker printer
if (checkerPrinter != null) {
try {
final printValue = await PrintDataoutputs.instance
.printChecker(
widget.data,
widget.table.tableName!,
widget.draftName,
'kasir',
checkerPrinter.paper.toIntegerFromText,
orderType.value);
await PrinterService().printWithPrinter(
checkerPrinter, printValue, context);
} catch (e) {
log("Error printing checker: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content:
Text('Error printing checker: $e')),
);
}
}
// Kitchen printer
if (kitchenPrinter != null) {
try {
final printValue =
await PrintDataoutputs.instance.printKitchen(
widget.data,
widget.table.tableName!,
widget.draftName,
'kasir',
kitchenPrinter.paper.toIntegerFromText,
orderType.value,
);
await PrinterService().printWithPrinter(
kitchenPrinter, printValue, context);
} catch (e) {
log("Error printing kitchen order: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Error printing kitchen order: $e')),
);
}
}
// Bar printer
if (barPrinter != null) {
try {
final printValue =
await PrintDataoutputs.instance.printBar(
widget.data,
widget.table.tableName!,
widget.draftName,
'kasir',
barPrinter.paper.toIntegerFromText,
orderType.value,
);
await PrinterService().printWithPrinter(
barPrinter, printValue, context);
} catch (e) {
log("Error printing bar order: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content:
Text('Error printing bar order: $e')),
);
}
}
},
label: 'Print Checker',
);
},
),
),
],
),
],
),
),
);
}
}

View File

@ -1,349 +0,0 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart';
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
import 'package:enaklo_pos/core/extensions/int_ext.dart';
import 'package:enaklo_pos/core/extensions/string_ext.dart';
import 'package:enaklo_pos/data/dataoutputs/print_dataoutputs.dart';
import 'package:enaklo_pos/data/datasources/product_local_datasource.dart';
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
import 'package:enaklo_pos/presentation/home/models/order_type.dart';
import 'package:enaklo_pos/core/utils/printer_service.dart';
import 'package:enaklo_pos/data/datasources/settings_local_datasource.dart';
import '../../../core/assets/assets.gen.dart';
import '../../../core/components/buttons.dart';
import '../../../core/components/spaces.dart';
import '../../table/blocs/get_table/get_table_bloc.dart';
import '../bloc/checkout/checkout_bloc.dart';
import '../bloc/order/order_bloc.dart';
class SuccessPaymentDialog extends StatefulWidget {
const SuccessPaymentDialog({
Key? key,
required this.data,
required this.totalQty,
required this.totalPrice,
required this.totalTax,
required this.totalDiscount,
required this.subTotal,
required this.normalPrice,
required this.totalService,
required this.draftName,
this.isTablePaymentPage = false,
}) : super(key: key);
final List<ProductQuantity> data;
final int totalQty;
final int totalPrice;
final int totalTax;
final int totalDiscount;
final int subTotal;
final int normalPrice;
final int totalService;
final String draftName;
final bool? isTablePaymentPage;
@override
State<SuccessPaymentDialog> createState() => _SuccessPaymentDialogState();
}
class _SuccessPaymentDialogState extends State<SuccessPaymentDialog> {
// List<ProductQuantity> data = [];
// int totalQty = 0;
// int totalPrice = 0;
@override
Widget build(BuildContext context) {
return AlertDialog(
content: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(child: Assets.icons.success.svg()),
const SpaceHeight(16.0),
const Center(
child: Text(
'Pembayaran telah sukses dilakukan',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
),
),
),
const SpaceHeight(20.0),
const Text('METODE BAYAR'),
const SpaceHeight(5.0),
BlocBuilder<OrderBloc, OrderState>(
builder: (context, state) {
final paymentMethod = state.maybeWhen(
orElse: () => 'Cash',
loaded: (model, orderId) => model.paymentMethod,
);
return Text(
paymentMethod,
style: const TextStyle(
fontWeight: FontWeight.w700,
),
);
},
),
const SpaceHeight(10.0),
const Divider(),
const SpaceHeight(8.0),
const Text('TOTAL TAGIHAN'),
const SpaceHeight(5.0),
BlocBuilder<OrderBloc, OrderState>(
builder: (context, state) {
state.maybeWhen(
orElse: () => 0,
loaded: (model, orderId) => model.total,
);
return Text(
widget.totalPrice.currencyFormatRp,
style: const TextStyle(
fontWeight: FontWeight.w700,
),
);
},
),
const SpaceHeight(10.0),
const Divider(),
const SpaceHeight(8.0),
const Text('NOMINAL BAYAR'),
const SpaceHeight(5.0),
BlocBuilder<OrderBloc, OrderState>(
builder: (context, state) {
final paymentAmount = state.maybeWhen(
orElse: () => 0,
loaded: (model, orderId) => model.paymentAmount,
);
return Text(
paymentAmount.ceil().currencyFormatRp,
style: const TextStyle(
fontWeight: FontWeight.w700,
),
);
},
),
const Divider(),
const SpaceHeight(8.0),
const Text('KEMBALIAN'),
const SpaceHeight(5.0),
BlocBuilder<OrderBloc, OrderState>(
builder: (context, state) {
final paymentAmount = state.maybeWhen(
orElse: () => 0,
loaded: (model, orderId) => model.paymentAmount,
);
final total = state.maybeWhen(
orElse: () => 0,
loaded: (model, orderId) => model.total,
);
final diff = paymentAmount - total;
log("DIFF: $diff paymentAmount: $paymentAmount total: $total");
return Text(
diff.ceil().currencyFormatRp,
style: const TextStyle(
fontWeight: FontWeight.w700,
),
);
},
),
const SpaceHeight(10.0),
const Divider(),
const SpaceHeight(8.0),
const Text('WAKTU PEMBAYARAN'),
const SpaceHeight(5.0),
Text(
DateFormat('dd MMMM yyyy, HH:mm').format(DateTime.now()),
style: const TextStyle(
fontWeight: FontWeight.w700,
),
),
const SpaceHeight(20.0),
Row(
children: [
Flexible(
child: Button.outlined(
onPressed: () {
// For table payment page, just close the dialog
// The cleanup and navigation is handled by the payment page
if (widget.isTablePaymentPage == true) {
Navigator.of(context).pop(); // Close dialog only
} else {
// For regular payment flow, reset and go to root
context
.read<CheckoutBloc>()
.add(const CheckoutEvent.started([]));
context
.read<GetTableBloc>()
.add(const GetTableEvent.getTables());
context.popToRoot();
}
},
label: 'Kembali',
),
),
const SpaceWidth(8.0),
Flexible(
child: BlocBuilder<OrderBloc, OrderState>(
builder: (context, state) {
final paymentAmount = state.maybeWhen(
orElse: () => 0,
loaded: (model, orderId) => model.paymentAmount,
);
final kembalian = paymentAmount - widget.totalPrice;
return BlocBuilder<CheckoutBloc, CheckoutState>(
builder: (context, checkoutState) {
final orderType = checkoutState.maybeWhen(
orElse: () => OrderType.dineIn,
loaded: (
items,
discountModel,
discount,
discountAmount,
tax,
serviceCharge,
totalQuantity,
totalPrice,
draftName,
orderType,
deliveryType,
) =>
orderType,
);
return Button.filled(
onPressed: () async {
final receiptPrinter =
await ProductLocalDatasource.instance
.getPrinterByCode('receipt');
final kitchenPrinter =
await ProductLocalDatasource.instance
.getPrinterByCode('kitchen');
final barPrinter = await ProductLocalDatasource
.instance
.getPrinterByCode('bar');
// Receipt Printer
if (receiptPrinter != null) {
try {
final settingsLocalDatasource =
SettingsLocalDatasource();
final taxModel =
await settingsLocalDatasource.getTax();
final serviceChargeValue =
await settingsLocalDatasource
.getServiceCharge();
// Get the actual payment method from OrderBloc
final paymentMethod = state.maybeWhen(
orElse: () => 'Cash',
loaded: (model, orderId) =>
model.paymentMethod,
);
final printValue = await PrintDataoutputs
.instance
.printOrderV3(
widget.data,
widget.totalQty,
widget.totalPrice,
paymentMethod,
paymentAmount,
kembalian,
widget.subTotal,
widget.totalDiscount,
widget.totalTax,
widget.totalService,
'kasir',
widget.draftName,
receiptPrinter.paper.toIntegerFromText,
taxPercentage: taxModel.value,
serviceChargePercentage: serviceChargeValue,
);
await PrinterService().printWithPrinter(
receiptPrinter, printValue, context);
} catch (e) {
log("Error printing receipt: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content:
Text('Error printing receipt: $e')),
);
}
}
// Kitchen Printer
if (kitchenPrinter != null &&
widget.isTablePaymentPage == false) {
try {
final printValue = await PrintDataoutputs
.instance
.printKitchen(
widget.data,
'',
widget.draftName,
'kasir',
kitchenPrinter.paper.toIntegerFromText,
orderType.value,
);
await PrinterService().printWithPrinter(
kitchenPrinter, printValue, context);
} catch (e) {
log("Error printing kitchen order: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Error printing kitchen order: $e')),
);
}
}
// Bar printer
if (barPrinter != null &&
widget.isTablePaymentPage == false) {
try {
final printValue =
await PrintDataoutputs.instance.printBar(
widget.data,
'',
widget.draftName,
'kasir',
barPrinter.paper.toIntegerFromText,
orderType.value,
);
await PrinterService().printWithPrinter(
barPrinter, printValue, context);
} catch (e) {
log("Error printing bar order: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Error printing bar order: $e')),
);
}
}
},
label: 'Print',
);
},
);
},
),
),
],
),
],
),
),
);
}
}

View File

@ -160,48 +160,53 @@ class _BarPrinterPageState extends State<BarPrinterPage> {
SpaceHeight(16), SpaceHeight(16),
// button test print // button test print
Button.outlined( Button.outlined(
onPressed: () async { onPressed: () async {
if (addressController!.text.isNotEmpty && printNameController!.text.isNotEmpty) { if (addressController!.text.isNotEmpty &&
try { printNameController!.text.isNotEmpty) {
// Create a test print model try {
final testPrinter = PrintModel( // Create a test print model
code: 'bar', final testPrinter = PrintModel(
name: printNameController!.text, code: 'bar',
address: addressController!.text, name: printNameController!.text,
paper: paper, address: addressController!.text,
type: selectedPrinter, paper: paper,
); type: selectedPrinter,
);
// Generate test print data // Generate test print data
final testPrintData = await PrintDataoutputs.instance.printBar( final testPrintData =
[], // Empty product list for test await PrintDataoutputs.instance.printBar(
'Test Table', [], // Empty product list for test
'Test Order', 'Test Table',
'Test Cashier', 'Test Order',
int.parse(paper), 'Test Cashier',
'DINE IN', 'Test Customer',
); int.parse(paper),
'DINE IN',
);
// Print test // Print test
await PrinterService().printWithPrinter( await PrinterService().printWithPrinter(
testPrinter, testPrinter,
testPrintData, testPrintData,
context, context,
); );
} catch (e) { } catch (e) {
log("Error test printing: $e"); log("Error test printing: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error test printing: $e')),
);
}
} else {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error test printing: $e')), SnackBar(
content: Text(
'Please fill in printer details first')),
); );
} }
} else { },
ScaffoldMessenger.of(context).showSnackBar( label: 'Test Print'),
SnackBar(content: Text('Please fill in printer details first')),
);
}
},
label: 'Test Print'
),
SpaceHeight(8), SpaceHeight(8),
// button save // button save
data == null data == null

View File

@ -162,48 +162,54 @@ class _CheckerPrinterPageState extends State<CheckerPrinterPage> {
SpaceHeight(16), SpaceHeight(16),
// button test print // button test print
Button.outlined( Button.outlined(
onPressed: () async { onPressed: () async {
if (addressController!.text.isNotEmpty && printNameController!.text.isNotEmpty) { if (addressController!.text.isNotEmpty &&
try { printNameController!.text.isNotEmpty) {
// Create a test print model try {
final testPrinter = PrintModel( // Create a test print model
code: 'checker', final testPrinter = PrintModel(
name: printNameController!.text, code: 'checker',
address: addressController!.text, name: printNameController!.text,
paper: paper, address: addressController!.text,
type: selectedPrinter, paper: paper,
); type: selectedPrinter,
);
// Generate test print data // Generate test print data
final testPrintData = await PrintDataoutputs.instance.printChecker( final testPrintData = await PrintDataoutputs
[], // Empty product list for test .instance
'Test Table', .printChecker(
'Test Order', [], // Empty product list for test
'Test Cashier', 'Test Table',
int.parse(paper), 'Test Order',
'DINE IN', 'Test Cashier',
); 'Test Customer',
int.parse(paper),
'DINE IN',
);
// Print test // Print test
await PrinterService().printWithPrinter( await PrinterService().printWithPrinter(
testPrinter, testPrinter,
testPrintData, testPrintData,
context, context,
); );
} catch (e) { } catch (e) {
log("Error test printing: $e"); log("Error test printing: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error test printing: $e')),
);
}
} else {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error test printing: $e')), SnackBar(
content: Text(
'Please fill in printer details first')),
); );
} }
} else { },
ScaffoldMessenger.of(context).showSnackBar( label: 'Test Print'),
SnackBar(content: Text('Please fill in printer details first')),
);
}
},
label: 'Test Print'
),
SpaceHeight(8), SpaceHeight(8),
// button save // button save
data == null data == null

View File

@ -160,48 +160,54 @@ class _KitchenPrinterPageState extends State<KitchenPrinterPage> {
SpaceHeight(16), SpaceHeight(16),
// button test print // button test print
Button.outlined( Button.outlined(
onPressed: () async { onPressed: () async {
if (addressController!.text.isNotEmpty && printNameController!.text.isNotEmpty) { if (addressController!.text.isNotEmpty &&
try { printNameController!.text.isNotEmpty) {
// Create a test print model try {
final testPrinter = PrintModel( // Create a test print model
code: 'kitchen', final testPrinter = PrintModel(
name: printNameController!.text, code: 'kitchen',
address: addressController!.text, name: printNameController!.text,
paper: paper, address: addressController!.text,
type: selectedPrinter, paper: paper,
); type: selectedPrinter,
);
// Generate test print data // Generate test print data
final testPrintData = await PrintDataoutputs.instance.printKitchen( final testPrintData = await PrintDataoutputs
[], // Empty product list for test .instance
'Test Table', .printKitchen(
'Test Order', [], // Empty product list for test
'Test Cashier', 'Test Table',
int.parse(paper), 'Test Order',
'DINE IN', 'Test Cashier',
); 'Test Customer',
int.parse(paper),
'DINE IN',
);
// Print test // Print test
await PrinterService().printWithPrinter( await PrinterService().printWithPrinter(
testPrinter, testPrinter,
testPrintData, testPrintData,
context, context,
); );
} catch (e) { } catch (e) {
log("Error test printing: $e"); log("Error test printing: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error test printing: $e')),
);
}
} else {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error test printing: $e')), SnackBar(
content: Text(
'Please fill in printer details first')),
); );
} }
} else { },
ScaffoldMessenger.of(context).showSnackBar( label: 'Test Print'),
SnackBar(content: Text('Please fill in printer details first')),
);
}
},
label: 'Test Print'
),
SpaceHeight(8), SpaceHeight(8),
// button save // button save
data == null data == null

View File

@ -204,180 +204,186 @@ class _SuccessOrderPageState extends State<SuccessOrderPage>
child: Column( child: Column(
children: [ children: [
// Success Header with Glassmorphism Effect // Success Header with Glassmorphism Effect
Container(
width: double.infinity,
padding: const EdgeInsets.all(32.0),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
AppColors.primary.withOpacity(0.1),
AppColors.primary.withOpacity(0.05),
],
),
borderRadius: const BorderRadius.vertical(
top: Radius.circular(24),
),
),
child: Column(
children: [
// Animated Success Icon with Floating Effect
AnimatedBuilder(
animation: _floatingAnimation,
builder: (context, child) {
return Transform.translate(
offset: Offset(0, _floatingAnimation.value),
child: ScaleTransition(
scale: _successIconAnimation,
child: Container(
padding: const EdgeInsets.all(20.0),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
AppColors.primary,
AppColors.primary.withOpacity(0.8),
],
),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: AppColors.primary.withOpacity(0.3),
blurRadius: 20,
offset: const Offset(0, 10),
),
],
),
child: const Icon(
Icons.check_rounded,
size: 48,
color: Colors.white,
),
),
),
);
},
),
const SizedBox(height: 24),
// Success Title with Shimmer Effect
FadeTransition(
opacity: _fadeInAnimation,
child: ShaderMask(
shaderCallback: (bounds) {
return LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: const [
AppColors.primary,
Colors.amber,
AppColors.primary,
],
stops: [
_shimmerAnimation.value - 1,
_shimmerAnimation.value,
_shimmerAnimation.value + 1,
],
).createShader(bounds);
},
child: const Text(
'Pesanan Berhasil!',
style: TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
),
const SizedBox(height: 12),
FadeTransition(
opacity: _fadeInAnimation,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
),
child: const Text(
'Pesanan telah diterima dan sedang diproses',
style: TextStyle(
fontSize: 14,
color: AppColors.primary,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
),
),
],
),
),
// Order Information Section
Expanded( Expanded(
child: Padding( child: SingleChildScrollView(
padding: const EdgeInsets.all(24.0),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_buildSectionTitle('Informasi Pesanan'), Container(
const SizedBox(height: 24), width: double.infinity,
padding: const EdgeInsets.all(32.0),
// Customer Card with Gradient Background decoration: BoxDecoration(
_buildInfoCard( gradient: LinearGradient(
icon: Icons.person_outline_rounded, begin: Alignment.topLeft,
title: 'Nama Pelanggan', end: Alignment.bottomRight,
value: widget.order.metadata?['customer_name'] ?? "-", colors: [
gradient: [ AppColors.primary.withOpacity(0.1),
Colors.blue.withOpacity(0.1), AppColors.primary.withOpacity(0.05),
Colors.purple.withOpacity(0.1), ],
], ),
), borderRadius: const BorderRadius.vertical(
top: Radius.circular(24),
const SizedBox(height: 16), ),
),
// Order Details Grid
Expanded(
child: Column( child: Column(
children: [ children: [
_buildInfoRow( // Animated Success Icon with Floating Effect
icon: Icons.receipt_long_outlined, AnimatedBuilder(
label: 'No. Pesanan', animation: _floatingAnimation,
value: widget.order.orderNumber ?? "-", builder: (context, child) {
delay: 0.3, return Transform.translate(
offset: Offset(0, _floatingAnimation.value),
child: ScaleTransition(
scale: _successIconAnimation,
child: Container(
padding: const EdgeInsets.all(20.0),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
AppColors.primary,
AppColors.primary.withOpacity(0.8),
],
),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: AppColors.primary
.withOpacity(0.3),
blurRadius: 20,
offset: const Offset(0, 10),
),
],
),
child: const Icon(
Icons.check_rounded,
size: 48,
color: Colors.white,
),
),
),
);
},
), ),
const SizedBox(height: 12),
_buildInfoRow( const SizedBox(height: 24),
icon: Icons.table_restaurant_outlined,
label: 'No. Meja', // Success Title with Shimmer Effect
value: widget.order.tableNumber ?? "-", FadeTransition(
delay: 0.4, opacity: _fadeInAnimation,
child: ShaderMask(
shaderCallback: (bounds) {
return LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: const [
AppColors.primary,
Colors.amber,
AppColors.primary,
],
stops: [
_shimmerAnimation.value - 1,
_shimmerAnimation.value,
_shimmerAnimation.value + 1,
],
).createShader(bounds);
},
child: const Text(
'Pesanan Berhasil!',
style: TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
_buildInfoRow(
icon: Icons.access_time_rounded, FadeTransition(
label: 'Waktu', opacity: _fadeInAnimation,
value: (widget.order.createdAt ?? DateTime.now()) child: Container(
.toFormattedDate3(), padding: const EdgeInsets.symmetric(
delay: 0.5, horizontal: 16,
vertical: 8,
),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
),
child: const Text(
'Pesanan telah diterima dan sedang diproses',
style: TextStyle(
fontSize: 14,
color: AppColors.primary,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
),
), ),
const SizedBox(height: 12), ],
_buildInfoRow( ),
icon: Icons.check_circle_outline, ),
label: 'Status Pembayaran', // Order Information Section
value: 'Lunas', Padding(
delay: 0.6, padding: const EdgeInsets.all(24.0),
valueColor: Colors.green, child: Column(
showBadge: true, crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildSectionTitle('Informasi Pesanan'),
const SizedBox(height: 24),
// Customer Card with Gradient Background
_buildInfoCard(
icon: Icons.person_outline_rounded,
title: 'Nama Pelanggan',
value:
widget.order.metadata?['customer_name'] ?? "-",
gradient: [
Colors.blue.withOpacity(0.1),
Colors.purple.withOpacity(0.1),
],
),
const SizedBox(height: 16),
// Order Details Grid
Column(
children: [
_buildInfoRow(
icon: Icons.receipt_long_outlined,
label: 'No. Pesanan',
value: widget.order.orderNumber ?? "-",
delay: 0.3,
),
const SizedBox(height: 12),
_buildInfoRow(
icon: Icons.wallet_outlined,
label: 'Metode Pembayaran',
value: widget.paymentMethod ?? "-",
delay: 0.3,
),
if (widget.order.tableNumber != "") ...[
const SizedBox(height: 12),
_buildInfoRow(
icon: Icons.table_restaurant_outlined,
label: 'No. Meja',
value: widget.order.tableNumber ?? "-",
delay: 0.4,
),
],
const SizedBox(height: 12),
_buildInfoRow(
icon: Icons.access_time_rounded,
label: 'Waktu',
value:
(widget.order.createdAt ?? DateTime.now())
.toFormattedDate3(),
delay: 0.5,
),
],
), ),
], ],
), ),

View File

@ -108,141 +108,146 @@ class _SuccessPaymentPageState extends State<SuccessPaymentPage> {
child: Column( child: Column(
children: [ children: [
// Success Header // Success Header
Container(
width: double.infinity,
padding: const EdgeInsets.all(32.0),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
AppColors.primary.withOpacity(0.1),
AppColors.primary.withOpacity(0.05),
],
),
borderRadius: const BorderRadius.vertical(
top: Radius.circular(24),
),
),
child: Column(
children: [
// Success Icon
Container(
padding: const EdgeInsets.all(20.0),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
AppColors.primary,
AppColors.primary.withOpacity(0.8),
],
),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: AppColors.primary.withOpacity(0.3),
blurRadius: 20,
offset: const Offset(0, 10),
),
],
),
child: const Icon(
Icons.check_rounded,
size: 48,
color: Colors.white,
),
),
const SizedBox(height: 24),
// Success Title
const Text(
'Pesanan Berhasil!',
style: TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
color: AppColors.primary,
),
),
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
),
child: const Text(
'Pesanan telah diterima dan sedang diproses',
style: TextStyle(
fontSize: 14,
color: AppColors.primary,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
),
],
),
),
// Order Information Section
Expanded( Expanded(
child: Padding( child: SingleChildScrollView(
padding: const EdgeInsets.all(24.0),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_buildSectionTitle('Informasi Pesanan'), Container(
const SizedBox(height: 24), width: double.infinity,
padding: const EdgeInsets.all(32.0),
// Customer Card decoration: BoxDecoration(
_buildInfoCard( gradient: LinearGradient(
icon: Icons.person_outline_rounded, begin: Alignment.topLeft,
title: 'Nama Pelanggan', end: Alignment.bottomRight,
value: order.metadata?['customer_name'] ?? "-", colors: [
gradient: [ AppColors.primary.withOpacity(0.1),
Colors.blue.withOpacity(0.1), AppColors.primary.withOpacity(0.05),
Colors.purple.withOpacity(0.1), ],
], ),
), borderRadius: const BorderRadius.vertical(
top: Radius.circular(24),
const SizedBox(height: 16), ),
),
// Order Details
Expanded(
child: Column( child: Column(
children: [ children: [
_buildInfoRow( // Success Icon
icon: Icons.receipt_long_outlined, Container(
label: 'No. Pesanan', padding: const EdgeInsets.all(20.0),
value: order.orderNumber ?? "-", decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
AppColors.primary,
AppColors.primary.withOpacity(0.8),
],
),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: AppColors.primary.withOpacity(0.3),
blurRadius: 20,
offset: const Offset(0, 10),
),
],
),
child: const Icon(
Icons.check_rounded,
size: 48,
color: Colors.white,
),
), ),
const SizedBox(height: 12),
_buildInfoRow( const SizedBox(height: 24),
icon: Icons.table_restaurant_outlined,
label: 'No. Meja', // Success Title
value: order.tableNumber ?? "-", const Text(
'Pesanan Berhasil!',
style: TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
color: AppColors.primary,
),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
_buildInfoRow(
icon: Icons.access_time_rounded, Container(
label: 'Waktu', padding: const EdgeInsets.symmetric(
value: (order.createdAt ?? DateTime.now()) horizontal: 16,
.toFormattedDate3(), vertical: 8,
),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
),
child: const Text(
'Pesanan telah diterima dan sedang diproses',
style: TextStyle(
fontSize: 14,
color: AppColors.primary,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
), ),
const SizedBox(height: 12), ],
_buildInfoRow( ),
icon: Icons.check_circle_outline, ),
label: 'Status Pembayaran', // Order Information Section
value: 'Lunas', Padding(
valueColor: Colors.green, padding: const EdgeInsets.all(24.0),
showBadge: true, child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildSectionTitle('Informasi Pesanan'),
const SizedBox(height: 24),
// Customer Card
_buildInfoCard(
icon: Icons.person_outline_rounded,
title: 'Nama Pelanggan',
value: order.metadata?['customer_name'] ?? "-",
gradient: [
Colors.blue.withOpacity(0.1),
Colors.purple.withOpacity(0.1),
],
),
const SizedBox(height: 16),
// Order Details
Expanded(
child: Column(
children: [
_buildInfoRow(
icon: Icons.receipt_long_outlined,
label: 'No. Pesanan',
value: order.orderNumber ?? "-",
),
const SizedBox(height: 12),
_buildInfoRow(
icon: Icons.receipt_long_outlined,
label: 'Metode Pembayaran',
value: widget.paymentMethod,
),
const SizedBox(height: 12),
_buildInfoRow(
icon: Icons.access_time_rounded,
label: 'Waktu',
value: (order.createdAt ?? DateTime.now())
.toFormattedDate3(),
),
const SizedBox(height: 12),
_buildInfoRow(
icon: Icons.check_circle_outline,
label: 'Status Pembayaran',
value: 'Lunas',
valueColor: Colors.green,
showBadge: true,
),
],
),
), ),
], ],
), ),

View File

@ -79,6 +79,12 @@ class _TablePageState extends State<TablePage> {
appBar: AppBar( appBar: AppBar(
title: const Text("Layout Meja"), title: const Text("Layout Meja"),
actions: [ actions: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: () {
context.read<GetTableBloc>().add(const GetTableEvent.getTables());
},
),
BlocListener<CreateTableBloc, CreateTableState>( BlocListener<CreateTableBloc, CreateTableState>(
listener: (context, state) { listener: (context, state) {
state.maybeWhen( state.maybeWhen(

View File

@ -16,7 +16,7 @@ import 'package:enaklo_pos/presentation/home/bloc/status_table/status_table_bloc
import 'package:enaklo_pos/presentation/home/pages/home_page.dart'; import 'package:enaklo_pos/presentation/home/pages/home_page.dart';
import 'package:enaklo_pos/presentation/table/blocs/get_table/get_table_bloc.dart'; import 'package:enaklo_pos/presentation/table/blocs/get_table/get_table_bloc.dart';
import 'package:enaklo_pos/presentation/table/models/draft_order_model.dart'; import 'package:enaklo_pos/presentation/table/models/draft_order_model.dart';
import 'package:enaklo_pos/presentation/table/pages/payment_table_page.dart'; import 'package:enaklo_pos/presentation/table/pages/payment_table_page.dart.old';
class CardTableWidget extends StatefulWidget { class CardTableWidget extends StatefulWidget {
final TableModel table; final TableModel table;