// ignore_for_file: public_member_api_docs, sort_constructors_first import 'dart:developer'; import 'package:enaklo_pos/core/components/dashed_divider.dart'; import 'package:enaklo_pos/core/components/flushbar.dart'; import 'package:enaklo_pos/core/extensions/build_context_ext.dart'; import 'package:enaklo_pos/data/models/response/customer_response_model.dart'; import 'package:enaklo_pos/data/models/response/delivery_response_model.dart'; import 'package:enaklo_pos/presentation/home/bloc/order_form/order_form_bloc.dart'; import 'package:enaklo_pos/presentation/home/dialog/save_dialog.dart'; import 'package:enaklo_pos/presentation/home/models/order_type.dart'; import 'package:enaklo_pos/presentation/home/models/product_quantity.dart'; import 'package:enaklo_pos/presentation/home/widgets/confirm_payment_title.dart'; import 'package:enaklo_pos/presentation/home/widgets/customer_auto_complete_field.dart'; import 'package:enaklo_pos/presentation/success/pages/success_order_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.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/table_model.dart'; import 'package:enaklo_pos/presentation/home/bloc/payment_methods/payment_methods_bloc.dart'; import 'package:enaklo_pos/data/models/response/payment_methods_response_model.dart'; import '../../../core/components/buttons.dart'; import '../../../core/components/spaces.dart'; import '../../../core/constants/colors.dart'; import '../bloc/checkout/checkout_bloc.dart'; import '../widgets/order_menu.dart'; class ConfirmPaymentPage extends StatefulWidget { final bool isTable; final TableModel? table; const ConfirmPaymentPage({ super.key, required this.isTable, this.table, }); @override State createState() => _ConfirmPaymentPageState(); } class _ConfirmPaymentPageState extends State { final totalPriceController = TextEditingController(); final customerController = TextEditingController(); bool isPayNow = true; bool isAddToOrder = false; PaymentMethod? selectedPaymentMethod; TableModel? selectTable; int discountAmount = 0; int priceValue = 0; int uangPas = 0; int uangPas2 = 0; int uangPas3 = 0; Customer? selectedCustomer; // int discountAmountValue = 0; int totalPriceFinal = 0; // int taxFinal = 0; // int serviceChargeFinal = 0; @override void initState() { // Fetch available tables by default context .read() .add(PaymentMethodsEvent.fetchPaymentMethods()); // Set a default payment method in case API fails selectedPaymentMethod = PaymentMethod( id: "4b1c0d21-c98a-4fc0-a2f9-8d90a0c9d905", organizationId: "3e8b1793-d18b-40c4-a03d-0c6480b630c7", name: "CASH", type: "cash", isActive: true, createdAt: DateTime.tryParse('2025-07-18T03:43:13.857048+07:00'), updatedAt: DateTime.tryParse('2025-07-18T03:43:13.857048+07:00'), ); // if (selectTable == null && widget.table != null) { // selectTable = tables.firstWhere( // (t) => t.id == widget.table!.id, // orElse: () => null, // ); // } if (widget.table != null) { // selectTable = TableModel( // tableNumber: widget.table!.tableNumber, // startTime: widget.table!.startTime, // status: widget.table!.status, // orderId: widget.table!.orderId, // paymentAmount: widget.table!.paymentAmount, // position: widget.table!.position, // ); } super.initState(); } @override void dispose() { totalPriceController.dispose(); customerController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return SafeArea( child: Hero( tag: 'payment_confirmation_screen', child: Scaffold( backgroundColor: AppColors.white, body: Row( children: [ Expanded( flex: 3, child: Align( alignment: Alignment.topCenter, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ConfirmPaymentTitle( isBack: false, title: 'Konfirmasi', subtitle: widget.isTable ? 'Orders Table ${widget.table?.tableName}' : 'Orders #1', actionWidget: [ BlocBuilder( builder: (context, state) { return state.maybeWhen( orElse: () => const SizedBox(), loaded: ( products, discountModel, discount, discountAmount, tax, serviceCharge, totalQuantity, totalPrice, draftName, orderType, deliveryType, ) { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(8.0), border: Border.all( color: AppColors.primary, width: 1.0, ), ), padding: const EdgeInsets.all(8.0), child: Text( orderType.value, style: TextStyle( color: AppColors.primary, fontSize: 16, fontWeight: FontWeight.w600, ), ), ); }, ); }, ), ], ), Container( padding: const EdgeInsets.all(16.0).copyWith(bottom: 8), decoration: const BoxDecoration( border: Border( bottom: BorderSide( color: AppColors.grey, width: 1.0, ), ), ), child: const Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Item', style: TextStyle( color: AppColors.primary, fontSize: 16, fontWeight: FontWeight.w600, ), ), SizedBox( width: 160, ), SizedBox( width: 50.0, child: Text( 'Qty', style: TextStyle( color: AppColors.primary, fontSize: 16, fontWeight: FontWeight.w600, ), ), ), SizedBox( child: Text( 'Price', style: TextStyle( color: AppColors.primary, fontSize: 16, fontWeight: FontWeight.w600, ), ), ), ], ), ), Expanded( child: BlocBuilder( builder: (context, state) { return state.maybeWhen( orElse: () => const Center( child: Text('No Items'), ), loaded: ( products, discountModel, discount, discountAmount, tax, serviceCharge, totalQuantity, totalPrice, draftName, orderType, deliveryType, ) { if (products.isEmpty) { return const Center( child: Text('No Items'), ); } return ListView.separated( shrinkWrap: true, padding: const EdgeInsets.all(16.0) .copyWith(top: 8.0), itemBuilder: (context, index) => OrderMenu(data: products[index]), separatorBuilder: (context, index) => const SpaceHeight(12.0), itemCount: products.length, ); }, ); }, ), ), Container( padding: const EdgeInsets.all(16.0), decoration: const BoxDecoration( border: Border( top: BorderSide( color: AppColors.grey, width: 1.0, ), ), ), child: Column( children: [ BlocBuilder( builder: (context, state) { return state.maybeWhen( orElse: () => const SizedBox.shrink(), loaded: ( products, discountModel, discount, discountAmount, tax, serviceCharge, totalQuantity, totalPrice, draftName, orderType, deliveryType, ) { if (deliveryType == null) { return const SizedBox.shrink(); } return Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Pengiriman', style: TextStyle( color: AppColors.black, fontWeight: FontWeight.w600, ), ), Text( deliveryType.name, style: TextStyle( color: AppColors.black, fontWeight: FontWeight.w600, ), ), ], ), const SpaceHeight(8.0), DashedDivider( color: AppColors.grey, ), const SpaceHeight(8.0), ], ); }); }, ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Sub total', style: TextStyle( color: AppColors.black, fontWeight: FontWeight.w600, ), ), BlocBuilder( builder: (context, state) { final price = state.maybeWhen( orElse: () => 0, loaded: ( products, discountModel, discount, discountAmount, tax, serviceCharge, totalQuantity, totalPrice, draftName, orderType, deliveryType, ) => products.fold( 0, (previousValue, element) => previousValue + (element.product.price! * element.quantity), )); return Text( price.currencyFormatRp, style: TextStyle( color: AppColors.black, fontWeight: FontWeight.w600, ), ); }, ), ], ), const SpaceHeight(8.0), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Pajak PB1', style: TextStyle( color: AppColors.black, fontWeight: FontWeight.w400, ), ), BlocBuilder( builder: (context, state) { final tax = state.maybeWhen( orElse: () => 0, loaded: ( products, discountModel, discount, discountAmount, tax, serviceCharge, totalQuantity, totalPrice, draftName, orderType, deliveryType, ) => tax, ); final price = state.maybeWhen( orElse: () => 0, loaded: ( products, discountModel, discount, discountAmount, tax, serviceCharge, totalQuantity, totalPrice, draftName, orderType, deliveryType, ) => products.fold( 0, (previousValue, element) => previousValue + (element.product.price! * element.quantity), ), ); final discount = state.maybeWhen( orElse: () => 0, loaded: ( products, discountModel, discount, discountAmount, tax, serviceCharge, totalQuantity, totalPrice, draftName, orderType, deliveryType, ) { if (discountModel == null) { return 0; } return discountModel.value! .replaceAll('.00', '') .toIntegerFromText; }); final subTotal = price - (discount / 100 * price); final finalTax = subTotal * (tax / 100); // final finalDiscount = discount / 100 * subTotal; // discountAmountValue = finalDiscount.toInt(); // taxFinal = finalTax.toInt(); return Text( '$tax % (${finalTax.toInt().currencyFormatRp})', style: TextStyle( color: AppColors.black, fontWeight: FontWeight.w500, ), ); }, ), ], ), const SpaceHeight(8.0), DashedDivider( color: AppColors.grey, ), const SpaceHeight(8.0), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Total', style: TextStyle( color: AppColors.black, fontWeight: FontWeight.bold, fontSize: 18, ), ), BlocBuilder( builder: (context, state) { final price = state.maybeWhen( orElse: () => 0, loaded: ( products, discountModel, discount, discountAmount, tax, serviceCharge, totalQuantity, totalPrice, draftName, orderType, deliveryType, ) => products.fold( 0, (previousValue, element) => previousValue + (element.product.price! * element.quantity), ), ); final discount = state.maybeWhen( orElse: () => 0, loaded: ( products, discountModel, discount, discountAmount, tax, serviceCharge, totalQuantity, totalPrice, draftName, orderType, deliveryType, ) { if (discountModel == null) { return 0; } return discountModel.value! .replaceAll('.00', '') .toIntegerFromText; }); final tax = state.maybeWhen( orElse: () => 0, loaded: ( products, discountModel, discount, discountAmount, tax, serviceCharge, totalQuantity, totalPrice, draftName, orderType, deliveryType, ) => tax, ); final serviceCharge = state.maybeWhen( orElse: () => 0, loaded: ( products, discountModel, discount, discountAmount, tax, serviceCharge, totalQuantity, totalPrice, draftName, orderType, deliveryType, ) => serviceCharge, ); final subTotal = price - (discount / 100 * price); final finalTax = subTotal * (tax / 100); final service = (serviceCharge / 100) * subTotal; final total = subTotal + finalTax + service; priceValue = total.toInt(); WidgetsBinding.instance .addPostFrameCallback((_) { totalPriceController.text = total.ceil().currencyFormatRpV2; }); uangPas = total.ceil(); uangPas2 = uangPas ~/ 50000 * 50000 + 50000; uangPas3 = uangPas ~/ 50000 * 50000 + 100000; totalPriceFinal = total.ceil(); // log("totalPriceFinal: $totalPriceFinal"); return Text( total.ceil().currencyFormatRp, style: const TextStyle( color: AppColors.primary, fontWeight: FontWeight.bold, fontSize: 18, ), ); }, ), ], ), ], ), ), ], ), ), ), SpaceWidth(2), Expanded( flex: 3, child: Align( alignment: Alignment.topCenter, child: Column( children: [ ConfirmPaymentTitle( title: 'Pembayaran', isBack: false, subtitle: 'Silahkan lakukan pembayaran', ), Expanded( child: SingleChildScrollView( child: Column( children: [ Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppColors.white, border: Border( bottom: BorderSide( color: AppColors.grey, width: 1.0, ), ), ), child: CustomerAutocomplete( controller: customerController, selectedCustomer: selectedCustomer, onSelected: (customer) { setState(() { selectedCustomer = customer; }); }, ), ), Container( padding: const EdgeInsets.all(16), width: double.infinity, decoration: BoxDecoration( color: AppColors.white, border: Border( bottom: BorderSide( color: AppColors.grey, width: 1.0, ), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Metode Pembayaran', style: TextStyle( color: AppColors.black, fontSize: 16, fontWeight: FontWeight.w600, ), ), const SpaceHeight(12.0), BlocBuilder( builder: (context, state) { return state.maybeWhen( orElse: () => const Center( child: CircularProgressIndicator(), ), loading: () => const Center( child: Column( children: [ CircularProgressIndicator(), SizedBox(height: 8.0), Text( 'Loading payment methods...'), ], ), ), error: (message) => Column( children: [ Center( child: Text( 'Error loading payment methods: $message'), ), const SpaceHeight(16.0), Button.filled( onPressed: () { context .read< PaymentMethodsBloc>() .add(PaymentMethodsEvent .fetchPaymentMethods()); }, label: 'Retry', ), ], ), loaded: (paymentMethods) { log("Loaded ${paymentMethods.length} payment methods"); paymentMethods.forEach((method) { log("Payment method: ${method.name} (ID: ${method.id})"); }); if (paymentMethods.isEmpty) { return Column( children: [ const Center( child: Text( 'No payment methods available'), ), const SpaceHeight(16.0), Button.filled( onPressed: () { context .read< PaymentMethodsBloc>() .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; } return Wrap( spacing: 12.0, runSpacing: 8.0, children: paymentMethods.map((method) { final isSelected = selectedPaymentMethod?.id == method.id; return GestureDetector( onTap: () { setState(() { selectedPaymentMethod = method; }); }, child: Container( height: 60, width: 80, alignment: Alignment.center, padding: const EdgeInsets.all( 8.0), decoration: BoxDecoration( color: isSelected ? AppColors.primary : AppColors.white, border: Border.all( color: AppColors.primary, width: 1.0, ), borderRadius: BorderRadius.circular( 8.0), ), child: Text( method.name ?? "", style: TextStyle( color: isSelected ? AppColors.white : AppColors.primary, fontWeight: FontWeight.bold, ), textAlign: TextAlign.center, ), ), ); }).toList(), ); }, ); }, ), ], ), ), if (selectedPaymentMethod?.type == "cash") Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppColors.white, border: Border( bottom: BorderSide( color: AppColors.grey, width: 1.0, ), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Total Bayar', style: TextStyle( color: AppColors.black, fontSize: 16, fontWeight: FontWeight.w600, ), ), const SpaceHeight(8.0), BlocBuilder( builder: (context, state) { return TextFormField( controller: totalPriceController, keyboardType: TextInputType.number, decoration: InputDecoration( border: OutlineInputBorder( borderRadius: BorderRadius.circular(8.0), ), hintText: 'Total harga', ), onChanged: (value) { priceValue = value.toIntegerFromText; final int newValue = value.toIntegerFromText; totalPriceController.text = newValue.currencyFormatRp; totalPriceController.selection = TextSelection.fromPosition( TextPosition( offset: totalPriceController .text .length)); }, ); }, ), const SpaceHeight(20.0), BlocBuilder( builder: (context, state) { return SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: [ Button.outlined( width: 150.0, onPressed: () { totalPriceController.text = uangPas .toString() .currencyFormatRpV2; priceValue = uangPas; }, label: 'UANG PAS', ), const SpaceWidth(20.0), Button.outlined( width: 150.0, onPressed: () { totalPriceController.text = uangPas2 .toString() .currencyFormatRpV2; priceValue = uangPas2; }, label: uangPas2 .toString() .currencyFormatRpV2, ), const SpaceWidth(20.0), Button.outlined( width: 150.0, onPressed: () { totalPriceController.text = uangPas3 .toString() .currencyFormatRpV2; priceValue = uangPas3; }, label: uangPas3 .toString() .currencyFormatRpV2, ), ], ), ); }, ), ], ), ), ], ), ), ), BlocBuilder( builder: (context, state) { final orderType = state.maybeWhen( orElse: () => OrderType.dineIn, loaded: ( products, discountModel, discount, discountAmount, tax, serviceCharge, totalQuantity, totalPrice, draftName, orderType, deliveryType, ) => orderType, ); DeliveryModel? delivery = state.maybeWhen( orElse: () => null, loaded: ( products, discountModel, discount, discountAmount, tax, serviceCharge, totalQuantity, totalPrice, draftName, orderType, deliveryType, ) => deliveryType, ); List items = state.maybeWhen( orElse: () => [], loaded: ( products, discountModel, discount, discountAmount, tax, serviceCharge, totalQuantity, totalPrice, draftName, orderType, deliveryType, ) => products, ); return Container( padding: EdgeInsets.all(16), decoration: BoxDecoration( color: AppColors.white, border: Border( top: BorderSide( color: AppColors.background, width: 1.0, ), ), ), child: Row( children: [ Expanded( child: Button.outlined( onPressed: () => context.pop(), label: 'Batalkan', ), ), SpaceWidth(12), Expanded( child: Button.filled( onPressed: () { if (customerController.text == '') { AppFlushbar.showError( context, 'Pilih Pelanggan terlebih dahulu', ); return; } showDialog( context: context, builder: (dcontext) => SaveDialog( selectedTable: widget.table, customerName: customerController.text, items: items, orderType: orderType, customer: selectedCustomer, deliveryModel: delivery, ), ); }, label: 'Simpan', ), ), SpaceWidth(12), BlocListener( listener: (lcontext, state) { state.maybeWhen( orElse: () {}, success: (data) { context .pushReplacement(SuccessOrderPage( productQuantity: items, order: data, )); }, error: (message) => AppFlushbar.showError( context, message, ), ); }, child: BlocBuilder( builder: (context, state) { return Expanded( child: state.maybeMap( orElse: () => Button.filled( onPressed: () { if (customerController.text == '') { AppFlushbar.showError( context, 'Pilih Pelanggan terlebih dahulu', ); return; } context.read().add( OrderFormEvent.create( items: items, customerName: customerController .text, orderType: orderType, table: widget.table, customer: selectedCustomer, ), ); }, label: 'Bayar', ), loading: (_) => Center( child: CircularProgressIndicator(), ), ), ); }, ), ), ], ), ); }, ), ], ), ), ), ], ), ), ), ); } }