import Spinner from '@/core/components/elements/Spinner/Spinner'; import NextImage from 'next/image'; import rejectImage from '../../../../public/images/reject.png'; import useTransaction from '../hooks/useTransaction'; import TransactionStatusBadge from './TransactionStatusBadge'; import Divider from '@/core/components/elements/Divider/Divider'; import { useEffect, useMemo, useRef, useState } from 'react'; import ImageNext from 'next/image'; import { downloadPurchaseOrder, downloadQuotation, } from '../utils/transactions'; import BottomPopup from '@/core/components/elements/Popup/BottomPopup'; import uploadPoApi from '../api/uploadPoApi'; import { toast } from 'react-hot-toast'; import getFileBase64 from '@/core/utils/getFileBase64'; import currencyFormat from '@/core/utils/currencyFormat'; import VariantGroupCard from '@/lib/variant/components/VariantGroupCard'; import { EllipsisVerticalIcon, ChevronDownIcon, ChevronRightIcon, ChevronUpIcon, } from '@heroicons/react/24/outline'; import Link from '@/core/components/elements/Link/Link'; import checkoutPoApi from '../api/checkoutPoApi'; import cancelTransactionApi from '../api/cancelTransactionApi'; import MobileView from '@/core/components/views/MobileView'; import DesktopView from '@/core/components/views/DesktopView'; import Menu from '@/lib/auth/components/Menu'; import Image from '@/core/components/elements/Image/Image'; import { createSlug } from '@/core/utils/slug'; import toTitleCase from '@/core/utils/toTitleCase'; import useAirwayBill from '../hooks/useAirwayBill'; import Manifest from '@/lib/treckingAwb/component/Manifest'; import useAuth from '@/core/hooks/useAuth'; import StepApproval from './stepper'; import aprpoveApi from '../api/approveApi'; import rejectApi from '../api/rejectApi'; import rejectProductApi from '../api/rejectProductApi'; import { useRouter } from 'next/router'; import { gtagPurchase } from '@/core/utils/googleTag'; import { deleteItemCart } from '@/core/utils/cart'; import { downloadInvoice, // downloadTaxInvoice, // (unused) } from '@/lib/invoice/utils/invoices'; import { Download } from 'lucide-react'; import axios from 'axios'; import InformationSection from '../../treckingAwb/component/InformationSection'; // import { Button } from '@chakra-ui/react'; // (unused) // import { div } from 'lodash-contrib'; // (unused) const Transaction = ({ id }) => { const PPN = process.env.NEXT_PUBLIC_PPN; const router = useRouter(); const [isModalOpen, setIsModalOpen] = useState(false); const [selectedProduct, setSelectedProduct] = useState(null); const [reason, setReason] = useState(''); const auth = useAuth(); const { transaction } = useTransaction({ id }); const statusApprovalWeb = transaction.data?.approvalStep; const [isLoading, setIsLoading] = useState(false); const { queryAirwayBill } = useAirwayBill({ orderId: id }); const [airwayBillPopup, setAirwayBillPopup] = useState(null); const [isOpen, setIsOpen] = useState(false); const poNumber = useRef(null); const poFile = useRef(null); const [uploadPo, setUploadPo] = useState(false); const [idAWB, setIdAWB] = useState(null); const openUploadPo = () => setUploadPo(true); const closeUploadPo = () => setUploadPo(false); const [copied, setCopied] = useState(false); const [toOthers, setToOthers] = useState(null); const [totalAmount, setTotalAmount] = useState(0); const [totalDiscountAmount, setTotalDiscountAmount] = useState(0); const [contLoading, setContLoading] = useState(false); useEffect(() => { if (transaction?.data?.products) { let calculateTotalAmount = 0; let calculateTotalDiscountAmount = 0; transaction.data.products.forEach((product) => { calculateTotalAmount += product.price.price * product.quantity; calculateTotalDiscountAmount += (product.price.price - product.price.priceDiscount) * product.quantity; }); setTotalAmount(calculateTotalAmount); setTotalDiscountAmount(calculateTotalDiscountAmount); } }, [transaction.data, transaction.isLoading]); const submitUploadPo = async () => { const file = poFile.current.files[0]; const name = poNumber.current.value; if (typeof file === 'undefined' || !name) { toast.error('Nomor dan Dokumen PO harus diisi'); return; } if (file.size > 5000000) { toast.error('Maksimal ukuran file adalah 5MB'); return; } const data = { name, file: await getFileBase64(file) }; const isUploaded = await uploadPoApi({ id, data }); if (isUploaded) { toast.success('Berhasil upload PO'); transaction.refetch(); closeUploadPo(); return; } toast.error( 'Terjadi kesalahan internal, coba lagi nanti atau hubungi kami' ); }; const [cancelTransaction, setCancelTransaction] = useState(false); const [continueNoPo, setContinueNoPo] = useState(false); const [continueTransaction, setContinueTransaction] = useState(false); const openCancelTransaction = () => setCancelTransaction(true); const openContinueTransaction = () => { if (auth.partnerTempo) { checkout(); } else { if (!transaction.data?.purchaseOrderFile) { setContinueTransaction(true); } else { checkoutNoPO(); } } }; const closeCancelTransaction = () => setCancelTransaction(false); const closeContinueTransaction = () => setContinueTransaction(false); const [rejectTransaction, setRejectTransaction] = useState(false); const openRejectTransaction = () => setRejectTransaction(true); const closeRejectTransaction = () => setRejectTransaction(false); const submitCancelTransaction = async () => { const isCancelled = await cancelTransactionApi({ transaction: transaction.data, }); if (isCancelled) { toast.success('Berhasil batalkan transaksi'); transaction.refetch(); } closeCancelTransaction(); }; const checkout = async () => { if (!transaction.data?.purchaseOrderFile) { toast.error('Mohon upload dokumen PO anda sebelum melanjutkan pesanan'); return; } await checkoutPoApi({ id, status: true }); toast.success('Berhasil melanjutkan pesanan'); transaction.refetch(); }; const checkoutNoPO = async () => { setIsLoading(true); gtagPurchase( transaction.data.products, transaction.data.deliveryAmount, transaction.data.name ); gtag('event', 'conversion', { send_to: 'AW-954540379/nDymCL3BhaQYENvClMcD', value: transaction.data?.amountTotal + Math.round(parseInt(transaction.data.deliveryAmount * 1.1) / 1000) * 1000, currency: 'IDR', transaction_id: transaction.data.id, }); for (const product of transaction.data.products) deleteItemCart({ productId: product.id }); if (transaction.data?.amountTotal > 0) { const payment = await axios.post( `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/midtrans-payment?transactionId=${transaction.data.id}` ); setIsLoading(false); window.location.href = payment.data.redirectUrl; } else { window.location.href = `${ process.env.NEXT_PUBLIC_SELF_HOST }/shop/checkout/success?order_id=${transaction.data.name.replace( /\//g, '-' )}`; } toast.success('Berhasil melanjutkan pesanan'); transaction.refetch(); }; const handleApproval = async () => { await aprpoveApi({ id }); toast.success('Berhasil melanjutkan approval'); transaction.refetch(); }; const handleReject = async () => { await rejectApi({ id }); closeRejectTransaction(); transaction.refetch(); }; // ===== Bayar Sekarang (pakai link dari backend; fallback generate via Next API) ===== const handlePayNow = async () => { try { setContLoading(true); const base = (process.env.NEXT_PUBLIC_ODOO_API_HOST || '').replace( /\/$/, '' ); const token = auth?.token; const partnerId = auth?.partnerId; // 1) Minta Odoo ensure payment link const { data: resp } = await axios.get( `${base}/api/v1/partner/${partnerId}/sale_order/${transaction.data.id}`, { params: { ensure_payment_link: 1, ts: Date.now() }, headers: { Token: token }, } ); // console.log('API Response:', resp); // Debug // 2) Akses semua kemungkinan path let url = resp?.result?.payment_summary?.redirect_url || resp?.data?.result?.payment_summary?.redirect_url || resp?.payment_summary?.redirect_url || resp?.paymentSummary?.redirectUrl || ''; // console.log('Extracted URL:', url); // Debug if (url) { window.location.href = url; return; } // 3) Fallback await transaction.refetch(); // console.log('Transaction data:', transaction.data); // Debug url = transaction?.data?.result?.payment_summary?.redirect_url || transaction?.data?.paymentSummary?.redirectUrl || transaction?.data?.payment_summary?.redirect_url || ''; // console.log('Fallback URL:', url); // Debug if (url) { window.location.href = url; return; } throw new Error('Link pembayaran belum tersedia.'); } catch (e) { toast.error( e?.response?.data?.description || e?.message || 'Gagal membuka pembayaran' ); } finally { setContLoading(false); } }; const memoizeVariantGroupCard = useMemo( () => (
Subtotal
{currencyFormat(transaction.data?.amountUntaxed)}
PPN {((PPN - 1) * 100).toFixed(0)}%
{currencyFormat(transaction.data?.amountTax)}
Biaya Pengiriman
{currencyFormat(transaction.data?.deliveryAmount)}
Grand Total
{currencyFormat(transaction.data?.amountTotal)}
{transaction.data?.name}
{transaction?.data?.address?.customer?.name}
Invoice
{invoice?.name}
{currencyFormat(invoice.amountTotal)}
Dokumen PO :
Metode Pembayaran
{transaction.data?.paymentTerm || '-'}
Berat Barang
{transaction.data?.products?.reduce( (total, item) => total + (item.weight || 0), 0 ) + ' Kg'}
Total Belanja
{currencyFormat(totalAmount)}
Diskon Belanja
{'- ' + currencyFormat(totalDiscountAmount)}
Subtotal
{currencyFormat(transaction.data?.amountUntaxed)}
PPN {((PPN - 1) * 100).toFixed(0)}%
{currencyFormat(transaction.data?.amountTax)}
Biaya Pengiriman
{currencyFormat(transaction.data?.deliveryAmount)}
Asuransi Pengiriman
-
Grand Total
{currencyFormat(transaction.data?.amountTotal)}
{transaction?.data?.carrierName}
{invoice?.name}
{currencyFormat(invoice.amountTotal)}
| Nama Produk | Jumlah | Harga | Subtotal | |
|---|---|---|---|---|
|
{product.isSni && (
{product.isTkdn && (
{product?.parent?.name}
{product?.code}{' '}
{product?.attributes.length > 0
? `| ${product?.attributes.join(', ')}`
: ''}
{product.soQty && (
{product.soQty !== product.reservedStockQty
? 'Barang sedang disiapkan'
: `${product.reservedStockQty} barang bisa di
kirim/pickup`}
)}
|
{product.quantity} |
{currencyFormat(product.price.priceDiscount)}
|
{currencyFormat(product.price.subtotal)} | {auth?.feature.soApproval && (auth.webRole == 2 || auth.webRole == 3) && router.asPath.includes('/my/quotations/') && transaction.data?.status == 'draft' && ()} |
| Nama Produk | Jumlah | Harga | Subtotal |
|---|---|---|---|
|
{product?.parent?.name}
{product?.code}{' '}
{product?.attributes.length > 0
? `| ${product?.attributes.join(', ')}`
: ''}
|
{product.quantity} |
{currencyFormat(product.price.priceDiscount)}
|
|