feat: syn product

This commit is contained in:
efrilm
2025-08-03 00:35:00 +07:00
parent 576f687d21
commit 7678b4791d
28 changed files with 1922 additions and 948 deletions
@@ -26,7 +26,7 @@ class DraftOrderItem {
Map<String, dynamic> toMapForLocal(int orderId) {
return {
'id_draft_order': orderId,
'id_product': product.productId,
'id_product': product.id,
'quantity': quantity,
'price': product.price,
};
@@ -46,7 +46,7 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
PaymentMethod? selectedPaymentMethod;
int totalPriceFinal = 0;
int discountAmountFinal = 0;
// Helper method to handle post-payment cleanup
Future<void> _handlePostPaymentCleanup() async {
if (widget.table != null && widget.draftOrder?.id != null) {
@@ -60,21 +60,22 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
startTime: DateTime.now().toIso8601String(),
position: widget.table!.position,
);
// Update table status
await ProductLocalDatasource.instance.updateStatusTable(newTable);
// Remove draft order
await ProductLocalDatasource.instance.removeDraftOrderById(widget.draftOrder!.id!);
await ProductLocalDatasource.instance
.removeDraftOrderById(widget.draftOrder!.id!);
// Refresh table status
context.read<GetTableStatusBloc>().add(
GetTableStatusEvent.getTablesStatus('all'),
);
GetTableStatusEvent.getTablesStatus('all'),
);
log("Table ${widget.table!.tableName} freed up and draft order removed");
}
// Safely navigate back - pop multiple times to get to table management
// Pop the success dialog first, then the payment page
if (Navigator.of(context).canPop()) {
@@ -84,6 +85,7 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
}
}
}
@override
void initState() {
context
@@ -93,7 +95,7 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
.read<PaymentMethodsBloc>()
.add(PaymentMethodsEvent.fetchPaymentMethods());
super.initState();
// Set a default payment method in case API fails
selectedPaymentMethod = PaymentMethod(
id: 1,
@@ -135,7 +137,8 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Kembali'),
content: const Text('Apakah Anda yakin ingin kembali? Order akan tetap tersimpan.'),
content: const Text(
'Apakah Anda yakin ingin kembali? Order akan tetap tersimpan.'),
actions: [
TextButton(
onPressed: () {
@@ -146,7 +149,8 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
TextButton(
onPressed: () {
Navigator.of(context).pop(); // Close dialog
Navigator.of(context).pop(); // Go back to previous page
Navigator.of(context)
.pop(); // Go back to previous page
},
child: const Text('Ya'),
),
@@ -157,7 +161,8 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
},
child: const Text(
'Kembali',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.bold),
),
),
],
@@ -251,7 +256,8 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
serviceCharge,
totalQuantity,
totalPrice,
draftName, orderType) {
draftName,
orderType) {
if (products.isEmpty) {
return const Center(
child: Text('No Items'),
@@ -294,13 +300,13 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
serviceCharge,
totalQuantity,
totalPrice,
draftName, orderType) =>
draftName,
orderType) =>
products.fold(
0,
(previousValue, element) =>
previousValue +
(element.product.price!
.toIntegerFromText *
(element.product.price! *
element.quantity),
));
return Text(
@@ -374,7 +380,8 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
serviceCharge,
totalQuantity,
totalPrice,
draftName, orderType) =>
draftName,
orderType) =>
tax,
);
final price = state.maybeWhen(
@@ -387,13 +394,13 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
serviceCharge,
totalQuantity,
totalPrice,
draftName, orderType) =>
draftName,
orderType) =>
products.fold(
0,
(previousValue, element) =>
previousValue +
(element.product.price!
.toIntegerFromText *
(element.product.price! *
element.quantity),
),
);
@@ -408,7 +415,8 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
serviceCharge,
totalQuantity,
totalPrice,
draftName, orderType) {
draftName,
orderType) {
return discountAmount;
});
@@ -446,7 +454,8 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
serviceCharge,
totalQuantity,
totalPrice,
draftName, orderType) =>
draftName,
orderType) =>
tax,
);
final price = state.maybeWhen(
@@ -459,13 +468,13 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
serviceCharge,
totalQuantity,
totalPrice,
draftName, orderType) =>
draftName,
orderType) =>
products.fold(
0,
(previousValue, element) =>
previousValue +
(element.product.price!
.toIntegerFromText *
(element.product.price! *
element.quantity),
),
);
@@ -480,7 +489,8 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
serviceCharge,
totalQuantity,
totalPrice,
draftName, orderType) {
draftName,
orderType) {
return discountAmount;
});
@@ -494,7 +504,8 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
serviceCharge,
totalQuantity,
totalPrice,
draftName, orderType) =>
draftName,
orderType) =>
serviceCharge,
);
@@ -536,13 +547,13 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
serviceCharge,
totalQuantity,
totalPrice,
draftName, orderType) =>
draftName,
orderType) =>
products.fold(
0,
(previousValue, element) =>
previousValue +
(element.product.price!
.toIntegerFromText *
(element.product.price! *
element.quantity),
),
);
@@ -557,7 +568,8 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
serviceCharge,
totalQuantity,
totalPrice,
draftName, orderType) {
draftName,
orderType) {
return discountAmount;
});
@@ -571,7 +583,8 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
serviceCharge,
totalQuantity,
totalPrice,
draftName, orderType) =>
draftName,
orderType) =>
serviceCharge,
);
@@ -585,7 +598,8 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
serviceCharge,
totalQuantity,
totalPrice,
draftName, orderType) =>
draftName,
orderType) =>
tax,
);
@@ -666,7 +680,8 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
serviceCharge,
totalQuantity,
totalPrice,
draftName, orderType) {
draftName,
orderType) {
customerController.text = draftName;
return TextFormField(
readOnly: true,
@@ -699,7 +714,8 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
),
),
const SpaceHeight(12.0),
BlocBuilder<PaymentMethodsBloc, PaymentMethodsState>(
BlocBuilder<PaymentMethodsBloc,
PaymentMethodsState>(
builder: (context, state) {
return state.maybeWhen(
orElse: () => const Center(
@@ -717,14 +733,16 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
error: (message) => Column(
children: [
Center(
child: Text('Error loading payment methods: $message'),
child: Text(
'Error loading payment methods: $message'),
),
const SpaceHeight(16.0),
Button.filled(
onPressed: () {
context
.read<PaymentMethodsBloc>()
.add(PaymentMethodsEvent.fetchPaymentMethods());
.add(PaymentMethodsEvent
.fetchPaymentMethods());
},
label: 'Retry',
),
@@ -739,32 +757,39 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
return Column(
children: [
const Center(
child: Text('No payment methods available'),
child: Text(
'No payment methods available'),
),
const SpaceHeight(16.0),
Button.filled(
onPressed: () {
context
.read<PaymentMethodsBloc>()
.add(PaymentMethodsEvent.fetchPaymentMethods());
.add(PaymentMethodsEvent
.fetchPaymentMethods());
},
label: 'Retry',
),
],
);
}
// Set default selected payment method if none selected or if current selection is not in the list
if (selectedPaymentMethod == null ||
!paymentMethods.any((method) => method.id == selectedPaymentMethod?.id)) {
selectedPaymentMethod = paymentMethods.first;
if (selectedPaymentMethod == null ||
!paymentMethods.any((method) =>
method.id ==
selectedPaymentMethod?.id)) {
selectedPaymentMethod =
paymentMethods.first;
}
return Wrap(
spacing: 12.0,
runSpacing: 8.0,
children: paymentMethods.map((method) {
final isSelected = selectedPaymentMethod?.id == method.id;
final isSelected =
selectedPaymentMethod?.id ==
method.id;
return Container(
constraints: const BoxConstraints(
minWidth: 120.0,
@@ -775,31 +800,44 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
color: AppColors.primary,
width: 2.0,
),
borderRadius: BorderRadius.circular(8.0),
borderRadius:
BorderRadius.circular(
8.0),
)
: null,
child: Tooltip(
message: method.description ?? 'No description available',
message: method.description ??
'No description available',
child: isSelected
? Button.filled(
width: double.infinity,
height: 50.0,
onPressed: () {
setState(() {
selectedPaymentMethod = method;
selectedPaymentMethod =
method;
});
},
label: method.name?.isNotEmpty == true ? method.name! : 'Unknown',
label: method.name
?.isNotEmpty ==
true
? method.name!
: 'Unknown',
)
: Button.outlined(
width: double.infinity,
height: 50.0,
onPressed: () {
setState(() {
selectedPaymentMethod = method;
selectedPaymentMethod =
method;
});
},
label: method.name?.isNotEmpty == true ? method.name! : 'Unknown',
label: method.name
?.isNotEmpty ==
true
? method.name!
: 'Unknown',
),
),
);
@@ -881,30 +919,41 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
builder: (context) => AlertDialog(
title: Row(
children: [
Icon(Icons.warning, color: AppColors.red),
Icon(Icons.warning,
color: AppColors.red),
SizedBox(width: 8),
Text('Batalkan Pesanan?'),
Text('Batalkan Pesanan?'),
],
),
content: Text(
'Apakah anda yakin ingin membatalkan pesanan untuk meja ${widget.table?.tableName ?? "ini"}?\n\nPesanan akan dihapus secara permanen.'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('Tidak',
style: TextStyle(color: AppColors.primary)),
onPressed: () =>
Navigator.pop(context),
child: Text('Tidak',
style: TextStyle(
color:
AppColors.primary)),
),
BlocListener<StatusTableBloc, StatusTableState>(
BlocListener<StatusTableBloc,
StatusTableState>(
listener: (context, state) {
state.maybeWhen(
orElse: () {},
success: () {
Navigator.pop(context); // Close void dialog
Navigator.pop(context); // Close payment page
ScaffoldMessenger.of(context).showSnackBar(
Navigator.pop(
context); // Close void dialog
Navigator.pop(
context); // Close payment page
ScaffoldMessenger.of(
context)
.showSnackBar(
const SnackBar(
content: Text('Pesanan berhasil dibatalkan'),
backgroundColor: AppColors.primary,
content: Text(
'Pesanan berhasil dibatalkan'),
backgroundColor:
AppColors.primary,
),
);
},
@@ -912,34 +961,47 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
},
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.red,
backgroundColor:
AppColors.red,
),
onPressed: () {
// Void the order
if (widget.table != null) {
final newTable = TableModel(
id: widget.table!.id,
tableName: widget.table!.tableName,
tableName: widget
.table!.tableName,
status: 'available',
orderId: 0,
paymentAmount: 0,
startTime: DateTime.now().toIso8601String(),
position: widget.table!.position,
startTime: DateTime.now()
.toIso8601String(),
position: widget
.table!.position,
);
context.read<StatusTableBloc>().add(
StatusTableEvent.statusTabel(newTable),
context
.read<StatusTableBloc>()
.add(
StatusTableEvent
.statusTabel(
newTable),
);
}
// Remove draft order from local storage
if (widget.draftOrder?.id != null) {
ProductLocalDatasource.instance
.removeDraftOrderById(widget.draftOrder!.id!);
if (widget.draftOrder?.id !=
null) {
ProductLocalDatasource
.instance
.removeDraftOrderById(
widget.draftOrder!
.id!);
}
log("Voided order from payment page");
},
child: const Text(
"Ya, Batalkan",
style: TextStyle(color: Colors.white),
style: TextStyle(
color: Colors.white),
),
),
),
@@ -1011,8 +1073,7 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
0,
(previousValue, element) =>
previousValue +
(element.product.price!
.toIntegerFromText *
(element.product.price! *
element.quantity),
),
);
@@ -1080,22 +1141,29 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
child: Button.filled(
onPressed: () async {
if (selectedPaymentMethod == null) {
ScaffoldMessenger.of(context).showSnackBar(
ScaffoldMessenger.of(context)
.showSnackBar(
const SnackBar(
content: Text('Please select a payment method'),
content: Text(
'Please select a payment method'),
backgroundColor: Colors.red,
),
);
return;
}
final paymentMethodName = selectedPaymentMethod?.name?.toLowerCase() ?? '';
final paymentMethodName =
selectedPaymentMethod?.name
?.toLowerCase() ??
'';
log("Selected payment method: ${selectedPaymentMethod?.name} (normalized: $paymentMethodName)");
if (paymentMethodName == 'cash' ||
if (paymentMethodName == 'cash' ||
paymentMethodName == 'tunai' ||
paymentMethodName == 'uang tunai' ||
paymentMethodName == 'cash payment') {
paymentMethodName ==
'uang tunai' ||
paymentMethodName ==
'cash payment') {
context.read<OrderBloc>().add(
OrderEvent.order(
items,
@@ -1109,7 +1177,9 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
widget.table?.id ?? 0,
'completed',
'paid',
selectedPaymentMethod?.name ?? 'Cash',
selectedPaymentMethod
?.name ??
'Cash',
totalPriceFinal,
orderType));
@@ -1149,7 +1219,9 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
widget.table?.id ?? 0,
'completed',
'paid',
selectedPaymentMethod?.name ?? 'Unknown Payment Method',
selectedPaymentMethod
?.name ??
'Unknown Payment Method',
totalPriceFinal,
orderType));
@@ -1173,7 +1245,7 @@ class _PaymentTablePageState extends State<PaymentTablePage> {
customerController.text,
),
);
// Handle post-payment cleanup
await _handlePostPaymentCleanup();
}