summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorIT Fixcomart <it@fixcomart.co.id>2025-09-08 10:21:55 +0000
committerIT Fixcomart <it@fixcomart.co.id>2025-09-08 10:21:55 +0000
commitb2032b16b36410b8f8185b14a43d234966554da0 (patch)
treec2adf164941a6099bc24619a3734f1bffbe2625f /src/lib
parent0d57c2629d9658afe888fbad8f09a29f4353f115 (diff)
parent57ace93dc3090a54d65d3b84ae50e8a9249eb314 (diff)
Merged in regenerate-midtrans (pull request #453)
<Miqdad> Regenerate midtrans
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/transaction/components/Transaction.jsx576
-rw-r--r--src/lib/transaction/components/TransactionStatusBadge.jsx4
-rw-r--r--src/lib/transaction/components/Transactions.jsx922
3 files changed, 613 insertions, 889 deletions
diff --git a/src/lib/transaction/components/Transaction.jsx b/src/lib/transaction/components/Transaction.jsx
index 77e60dc1..96c89aec 100644
--- a/src/lib/transaction/components/Transaction.jsx
+++ b/src/lib/transaction/components/Transaction.jsx
@@ -43,13 +43,14 @@ import { gtagPurchase } from '@/core/utils/googleTag';
import { deleteItemCart } from '@/core/utils/cart';
import {
downloadInvoice,
- downloadTaxInvoice,
+ // 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';
-import { div } from 'lodash-contrib';
+// 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();
@@ -73,6 +74,7 @@ const Transaction = ({ id }) => {
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) {
@@ -88,6 +90,7 @@ const Transaction = ({ id }) => {
setTotalDiscountAmount(calculateTotalDiscountAmount);
}
}, [transaction.data, transaction.isLoading]);
+
const submitUploadPo = async () => {
const file = poFile.current.files[0];
const name = poNumber.current.value;
@@ -127,10 +130,6 @@ const Transaction = ({ id }) => {
}
}
};
- // const ContinueTransaction = () => {
- // setContinueNoPo(true);
- // checkoutNoPO();
- // };
const closeCancelTransaction = () => setCancelTransaction(false);
const closeContinueTransaction = () => setContinueTransaction(false);
@@ -138,6 +137,7 @@ const Transaction = ({ id }) => {
const openRejectTransaction = () => setRejectTransaction(true);
const closeRejectTransaction = () => setRejectTransaction(false);
+
const submitCancelTransaction = async () => {
const isCancelled = await cancelTransactionApi({
transaction: transaction.data,
@@ -148,6 +148,7 @@ const Transaction = ({ id }) => {
}
closeCancelTransaction();
};
+
const checkout = async () => {
if (!transaction.data?.purchaseOrderFile) {
toast.error('Mohon upload dokumen PO anda sebelum melanjutkan pesanan');
@@ -194,25 +195,6 @@ const Transaction = ({ id }) => {
}
toast.success('Berhasil melanjutkan pesanan');
transaction.refetch();
- // console.log(transaction);
-
- /* const midtrans = async () => {
- for (const product of products) deleteItemCart({ productId: product.id });
- if (grandTotal > 0) {
- const payment = await axios.post(
- `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/midtrans-payment?transactionId=${isCheckouted.id}`
- );
- setIsLoading(false);
- window.location.href = payment.data.redirectUrl;
- } else {
- window.location.href = `${
- process.env.NEXT_PUBLIC_SELF_HOST
- }/shop/checkout/success?order_id=${isCheckouted.name.replace(
- /\//g,
- '-'
- )}`;
- }
- };*/
};
const handleApproval = async () => {
@@ -227,6 +209,73 @@ const Transaction = ({ id }) => {
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(
() => (
<div className='p-4 pt-0 flex flex-col gap-y-3'>
@@ -314,7 +363,7 @@ const Transaction = ({ id }) => {
navigator.clipboard.writeText(textToCopy);
setCopied(true);
toast.success('No Resi Berhasil di Copy');
- setTimeout(() => setCopied(false), 2000); // Reset copied state after 2 seconds
+ setTimeout(() => setCopied(false), 2000);
};
const formatDate = (dateString) => {
@@ -336,7 +385,7 @@ const Transaction = ({ id }) => {
const [day, month, year] = dateString.split('/');
return `${day} ${months[parseInt(month, 10) - 1]} ${year}`;
};
- // console.log(transaction);
+
return (
transaction.data?.name && (
<>
@@ -366,6 +415,7 @@ const Transaction = ({ id }) => {
</button>
</div>
</BottomPopup>
+
<BottomPopup
active={cancelTransaction}
close={closeCancelTransaction}
@@ -452,48 +502,53 @@ const Transaction = ({ id }) => {
active={toOthers}
close={() => setToOthers(null)}
>
- <div className='flex flex-col gap-y-4 mt-2'>
+ {transaction.data?.status === 'draft' && (
+ <>
<button
className='text-left disabled:opacity-60'
- disabled={!toOthers?.purchaseOrderFile}
+ disabled={toOthers?.status != 'draft'}
onClick={() => {
- downloadPurchaseOrder(toOthers);
+ downloadQuotation(toOthers);
setToOthers(null);
}}
>
- Download PO
+ Download Quotation
</button>
<button
className='text-left disabled:opacity-60'
- disabled={toOthers?.status != 'draft'}
+ disabled={toOthers?.status != 'waiting'}
onClick={() => {
- downloadQuotation(toOthers);
+ setToCancel(toOthers);
setToOthers(null);
}}
>
- Download Quotation
+ Batalkan Transaksi
</button>
+ </>
+ )}
+ <div className='flex flex-col gap-y-4 mt-2'>
<button
className='text-left disabled:opacity-60'
- disabled={toOthers?.status != 'waiting'}
+ disabled={!toOthers?.purchaseOrderFile}
onClick={() => {
- setToCancel(toOthers);
+ downloadPurchaseOrder(toOthers);
setToOthers(null);
}}
>
- Batalkan Transaksi
+ Download PO
</button>
</div>
</BottomPopup>
<Manifest idAWB={idAWB} closePopup={closePopup}></Manifest>
+ {/* ============ MOBILE ============ */}
<MobileView>
<div className='px-4'>
<div className='flex flex-row w-full justify-between items-center py-2 px-3 mb-4 text-sm border border-yellow-500 text-yellow-800 rounded-lg bg-yellow-50 gap-2'>
- <div class='flex items-center w-full ' role='alert'>
+ <div className='flex items-center w-full ' role='alert'>
<svg
- class='flex-shrink-0 inline w-4 h-4 mr-2'
+ className='flex-shrink-0 inline w-4 h-4 mr-2'
aria-hidden='true'
fill='currentColor'
viewBox='0 0 20 20'
@@ -514,6 +569,7 @@ const Transaction = ({ id }) => {
</span>
</div>
</div>
+
{auth?.feature?.soApproval && (
<div className='p-4'>
<StepApproval
@@ -559,7 +615,7 @@ const Transaction = ({ id }) => {
<Divider />
<div className='flex flex-col gap-y-4 p-4'>
- <h4 className="font-semibold">Detail Order</h4>
+ <h4 className='font-semibold'>Detail Order</h4>
<DescriptionRow label='No Transaksi'>
<p className='font-semibold'>{transaction.data?.name}</p>
</DescriptionRow>
@@ -579,9 +635,11 @@ const Transaction = ({ id }) => {
<Divider />
<div className='flex flex-col gap-y-4 p-4'>
- <h4 className="font-semibold">Alamat Pengiriman</h4>
+ <h4 className='font-semibold'>Alamat Pengiriman</h4>
<DescriptionRow label='Nama Penerima'>
- <p className='font-semibold'>{transaction?.data?.address?.customer?.name}</p>
+ <p className='font-semibold'>
+ {transaction?.data?.address?.customer?.name}
+ </p>
</DescriptionRow>
<DescriptionRow label='No. Telp'>
{transaction?.data?.address?.customer?.phone
@@ -602,9 +660,7 @@ const Transaction = ({ id }) => {
<div className='p-4'>
<div className='font-medium mb-4'>Info Pengiriman</div>
{transaction?.data?.pickings.length == 0 && (
- <div className='badge-red text-sm'>
- Belum ada pengiriman
- </div>
+ <div className='badge-red text-sm'>Belum ada pengiriman</div>
)}
{transaction?.data?.pickings?.map((airway) => (
<div
@@ -627,28 +683,7 @@ const Transaction = ({ id }) => {
</button>
</div>
</div>
- // <button
- // className='shadow rounded-md p-3 text-gray_r-12 font-normal flex justify-between items-center text-left h-20'
- // key={airway?.id}
- // onClick={() => setIdAWB(airway?.id)}
- // >
- // <div>
- // <span className='text-sm text-gray_r-11'>
- // No Resi : {airway?.trackingNumber || '-'}{' '}
- // </span>
- // <p className='mt-1 font-medium'>{airway?.name}</p>
- // </div>
- // <div className='flex gap-x-2'>
- // <div className='text-sm text-gray-600 badge-green leading-[1.5] mt-1'>
- // {airway?.delivered
- // ? 'Pesanan Tiba'
- // : 'Sedang Dikirim'}
- // </div>
- // <ChevronRightIcon className='w-5 stroke-2' />
- // </div>
- // </button>
))}
-
</div>
<Divider />
@@ -717,17 +752,20 @@ const Transaction = ({ id }) => {
<div className='font-medium p-4'>Detail Produk</div>
{transaction?.data?.products.length > 0 ? (
<div className='p-4 pt-0 flex flex-col gap-y-3'>
- <VariantGroupCard variants={transaction.data?.products}/>
+ <VariantGroupCard variants={transaction.data?.products} />
<div className='font-medium'>Rincian Pembayaran</div>
<div className='flex justify-between mt-1'>
<p className='text-gray_r-12/70'>Metode Pembayaran</p>
- <p>
- {transaction.data?.paymentTerm || '-'}
- </p>
+ <p>{transaction.data?.paymentTerm || '-'}</p>
</div>
<div className='flex justify-between mt-1'>
<p className='text-gray_r-12/70'>Berat Barang</p>
- <p>{(transaction.data?.products?.reduce((total, item) => total + (item.weight || 0), 0)) + ' Kg'}</p>
+ <p>
+ {transaction.data?.products?.reduce(
+ (total, item) => total + (item.weight || 0),
+ 0
+ ) + ' Kg'}
+ </p>
</div>
<hr className='mt-1 border border-gray-100' />
<div className='flex justify-between mt-1'>
@@ -774,12 +812,7 @@ const Transaction = ({ id }) => {
</div>
)}
- {/* <Divider /> */}
-
- {/* <SectionAddress address={transaction.data?.address} /> */}
-
- {/* <Divider /> */}
-
+ {/* Tombol aksi (Mobile) */}
{transaction.data?.status === 'draft' && (
<div className='p-4 pt-0'>
<button
@@ -796,18 +829,33 @@ const Transaction = ({ id }) => {
Batalkan Transaksi
</button>
{transaction.data?.status == 'draft' &&
- transaction?.data?.purchaseOrderFile && (
- <button
- className='btn-yellow w-full mt-4'
- onClick={openContinueTransaction}
- >
- Lanjutkan Transaksi
- </button>
- )}
+ transaction?.data?.purchaseOrderFile && (
+ <button
+ className='btn-yellow w-full mt-4'
+ onClick={openContinueTransaction}
+ >
+ Lanjutkan Transaksi
+ </button>
+ )}
+ </div>
+ )}
+
+ {/* Bayar Sekarang (Mobile) — tampil jika eligible */}
+ {transaction.data?.eligibleContinue && (
+ <div className='p-4 pt-0'>
+ <button
+ type='button'
+ disabled={contLoading}
+ onClick={handlePayNow}
+ className='w-full py-2 text-center rounded-md border border-red-500 text-red-500 bg-white disabled:opacity-60'
+ >
+ {contLoading ? 'Memproses…' : 'Bayar Sekarang'}
+ </button>
</div>
)}
</MobileView>
+ {/* ============ DESKTOP ============ */}
<DesktopView>
<div className='container mx-auto flex py-10'>
<div className='w-3/12 pr-4'>
@@ -827,44 +875,36 @@ const Transaction = ({ id }) => {
)}
</div>
- {/*new-release*/}
- {/*<div className='flex items-center justify-between mb-3'>*/}
- {/* <div className='flex items-center gap-x-2'>*/}
- {/* <span className='text-h-sm font-medium'>*/}
- {/* {transaction?.data?.name}*/}
- {/* </span>*/}
- {/* <TransactionStatusBadge status={transaction?.data?.status} />*/}
- {/* </div>*/}
- {/* <div className='text-h-sm'>*/}
- {/* Estimasi Barang Siap:{' '}*/}
- {/* <span className='text-red-500 font-semibold'>*/}
- {/* {transaction?.data?.expectedReadyToShip}*/}
- {/* </span>*/}
- {/* </div>*/}
- <div className='flex items-center gap-x-2 mb-3'>
- <span className='text-h-sm font-medium'>
- {transaction?.data?.name}
- </span>
- <TransactionStatusBadge status={transaction?.data?.status} />
- {transaction.data?.status === 'draft' && (
- <div className='flex items-center justify-between w-full'>
- <button
- type='button'
- className='btn-light px-3 py-2'
- onClick={() => downloadQuotation(transaction.data)}
- >
- <Download size={12} />
- </button>
+ {/* HEADER (Desktop) — sejajarkan kiri & kanan */}
+ <div className='flex items-center justify-between gap-3 mb-3'>
+ {/* Kiri: SO + badge */}
+ <div className='flex items-center gap-x-2 min-w-0'>
+ <span className='text-h-sm font-medium truncate'>
+ {transaction?.data?.name}
+ </span>
+ <TransactionStatusBadge status={transaction?.data?.status} />
+ </div>
+
+ {/* Kanan: aksi */}
+ <div className='flex items-center gap-3'>
+ {transaction.data?.status === 'draft' && (
+ <>
+ <button
+ type='button'
+ className='btn-light px-3 py-2'
+ onClick={() => downloadQuotation(transaction.data)}
+ >
+ <Download size={12} />
+ </button>
- <div className="flex gap-x-4">
<button
className='btn-solid-red'
onClick={openCancelTransaction}
>
Batalkan Transaksi
</button>
- {transaction.data?.status == 'draft' &&
- transaction?.data?.purchaseOrderFile && (
+
+ {transaction?.data?.purchaseOrderFile && (
<button
className='btn-yellow'
onClick={openContinueTransaction}
@@ -872,37 +912,20 @@ const Transaction = ({ id }) => {
Lanjutkan Transaksi
</button>
)}
- </div>
- </div>
- )}
- </div>
- {/* {transaction.data?.status === 'draft' && (
- <div className='flex gap-x-4'>
- <button
- type='button'
- className='btn-light px-3 py-2 mr-auto'
- onClick={() => downloadQuotation(transaction.data)}
- >
- <Download size={12} />
- </button>
- <button
- className='btn-solid-red'
- onClick={openCancelTransaction}
- >
- Batalkan Transaksi
- </button>
-
- {transaction.data?.status == 'draft' &&
- transaction?.data?.purchaseOrderFile && (
- <button
- className='btn-yellow'
- onClick={openContinueTransaction}
- >
- Lanjutkan Transaksi
- </button>
- )}
+ </>
+ )}
+
+ {transaction.data?.eligibleContinue && (
+ <button
+ className='px-4 py-2 rounded-md border border-red-500 text-red-500 bg-white disabled:opacity-60 mb-3'
+ disabled={contLoading}
+ onClick={handlePayNow}
+ >
+ {contLoading ? 'Memproses…' : 'Bayar Sekarang'}
+ </button>
+ )}
</div>
- )} */}
+ </div>
<div className='grid grid-cols-2 gap-x-6 mt-4'>
<div className='grid grid-cols-[35%_65%] gap-y-4'>
@@ -967,29 +990,13 @@ const Transaction = ({ id }) => {
key={index}
>
{invoice?.name}
- {/* <div className='shadow rounded-md p-4 text-gray_r-12 font-normal flex justify-between'>
- <div>
- <p className='mb-1'>{invoice?.name}</p>
- <div className='flex items-center gap-x-1'>
- {invoice.amountResidual > 0 ? (
- <div className='badge-red'>Belum Lunas</div>
- ) : (
- <div className='badge-green'>Lunas</div>
- )}
- <p className='text-caption-2 text-gray_r-11'>
- {currencyFormat(invoice.amountTotal)}
- </p>
- </div>
- </div>
- <ChevronRightIcon className='w-5 stroke-2' />
- </div> */}
</Link>
))}
</div>
</div>
</div>
<hr className='mt-4 mb-4 border border-gray-100' />
- {/* <div className='grid grid-cols-2 gap-x-6'> */}
+
<div className='flex flex-row justify-between items-start w-full h-fit '>
<div className='flex flex-col w-1/2 justify-start items-start'>
<span className='text-h-sm font-medium mb-2'>
@@ -1039,17 +1046,17 @@ const Transaction = ({ id }) => {
) : (
'-'
)}
- {transaction?.data?.carrierId !== 32 &&(
- <>
- <div>Jenis Service</div>
- <div>: </div>
- <div>
- {' '}
- {transaction?.data?.serviceType
- ? transaction?.data?.serviceType
- : '-'}
- </div>
- </>
+ {transaction?.data?.carrierId !== 32 && (
+ <>
+ <div>Jenis Service</div>
+ <div>: </div>
+ <div>
+ {' '}
+ {transaction?.data?.serviceType
+ ? transaction?.data?.serviceType
+ : '-'}
+ </div>
+ </>
)}
<div>Estimasi Tanggal Kirim</div>
@@ -1059,41 +1066,42 @@ const Transaction = ({ id }) => {
? transaction?.data?.expectedReadyToShip
: '-'}
</div>
- {transaction?.data?.carrierId !== 32 &&(
- <>
- <div>Estimasi Tiba</div>
- <div>: </div>
- <div className=''>
- {transaction?.data?.etaDateStart && transaction?.data?.etaDateEnd ? (
- `${transaction.data.etaDateStart} - ${transaction.data.etaDateEnd}`
- ) : (
- '-'
- )}
- </div>
- </>
+ {transaction?.data?.carrierId !== 32 && (
+ <>
+ <div>Estimasi Tiba</div>
+ <div>: </div>
+ <div className=''>
+ {transaction?.data?.etaDateStart &&
+ transaction?.data?.etaDateEnd
+ ? `${transaction.data.etaDateStart} - ${transaction.data.etaDateEnd}`
+ : '-'}
+ </div>
+ </>
)}
- {transaction?.data?.pickings[0] && transaction?.data?.carrierId !== 32 && (
- <div className='w-full bagian-informasi col-span-3'>
- <div
- class='flex items-center w-fit py-2 px-3 mb-4 text-sm border border-yellow-500 text-yellow-800 rounded-lg bg-yellow-50'
- role='alert'
- >
- <svg
- class='flex-shrink-0 inline w-4 h-4 mr-2'
- aria-hidden='true'
- fill='currentColor'
- viewBox='0 0 20 20'
+ {transaction?.data?.pickings[0] &&
+ transaction?.data?.carrierId !== 32 && (
+ <div className='w-full bagian-informasi col-span-3'>
+ <div
+ className='flex items-center w-fit py-2 px-3 mb-4 text-sm border border-yellow-500 text-yellow-800 rounded-lg bg-yellow-50'
+ role='alert'
>
- <path d='M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z' />
- </svg>
- <div className='text-justify flex flex-col gap-1'>
- <span className='text-black text-xs'>
- Pesanan anda mungkin mengalami keterlambatan tiba
- </span>
+ <svg
+ className='flex-shrink-0 inline w-4 h-4 mr-2'
+ aria-hidden='true'
+ fill='currentColor'
+ viewBox='0 0 20 20'
+ >
+ <path d='M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z' />
+ </svg>
+ <div className='text-justify flex flex-col gap-1'>
+ <span className='text-black text-xs'>
+ Pesanan anda mungkin mengalami keterlambatan
+ tiba
+ </span>
+ </div>
</div>
</div>
- </div>
- )}
+ )}
</div>
</div>
</div>
@@ -1103,9 +1111,7 @@ const Transaction = ({ id }) => {
</div>
<div className='grid grid-cols-1 md:grid-cols-2 gap-3'>
{transaction?.data?.pickings.length == 0 && (
- <div className='badge-red text-sm'>
- Belum ada pengiriman
- </div>
+ <div className='badge-red text-sm'>Belum ada pengiriman</div>
)}
{transaction?.data?.pickings?.map((airway) => (
<div
@@ -1129,39 +1135,9 @@ const Transaction = ({ id }) => {
</div>
</div>
))}
- {/* </div> */}
-
</div>
<div className='flex '>
- {/*New release*/}
- {/* <div className='grid grid-cols-1 gap-1 w-2/3'>*/}
- {/* {transaction?.data?.pickings?.map((airway) => (*/}
- {/* <button*/}
- {/* className='shadow rounded-md p-3 text-gray_r-12 font-normal flex justify-between items-center text-left h-20'*/}
- {/* key={airway?.id}*/}
- {/* onClick={() => setIdAWB(airway?.id)}*/}
- {/* >*/}
- {/* <div>*/}
- {/* <p className='text-sm text-gray_r-11'>*/}
- {/* {airway?.name}*/}
- {/* </p>*/}
- {/* <span className='text-md text-bold mt-1'>*/}
- {/* No Resi : {airway?.trackingNumber || '-'}{' '}*/}
- {/* </span>*/}
- {/* </div>*/}
- {/* <div className='flex gap-x-2'>*/}
- {/* <div className='text-sm text-gray-600 badge-green leading-[1.5] mt-1 text-center'>*/}
- {/* {airway?.delivered*/}
- {/* ? 'Pesanan Tiba'*/}
- {/* : 'Sedang Dikirim'}*/}
- {/* </div>*/}
- {/* <ChevronRightIcon className='w-5 stroke-2' />*/}
- {/* </div>*/}
- {/* </button>*/}
- {/* ))}*/}
- {/* </div>*/}
- {/*</div>*/}
<div className='invoice w-1/2 '>
<div className='text-h-sm font-semibold mt-10 mb-4 '>
Invoice
@@ -1202,7 +1178,6 @@ const Transaction = ({ id }) => {
<thead>
<tr>
<th>Nama Produk</th>
- {/* <th>Diskon</th> */}
<th>Jumlah</th>
<th>Harga</th>
<th>Subtotal</th>
@@ -1280,24 +1255,13 @@ const Transaction = ({ id }) => {
)}
</div>
</td>
- {/* <td>
- {product.price.discountPercentage > 0
- ? `${product.price.discountPercentage}%`
- : ''}
- </td> */}
<td>{product.quantity}</td>
<td>
- {/* {product.price.discountPercentage > 0 && (
- <div className='line-through mb-1 text-caption-1 text-gray_r-12/70'>
- {currencyFormat(product.price.price)}
- </div>
- )} */}
<div>
{currencyFormat(product.price.priceDiscount)}
</div>
</td>
<td>{currencyFormat(product.price.subtotal)}</td>
- {/* {auth?.feature.soApproval && (auth.webRole == 2 || auth.webRole == 3) && (transaction.data.isReaject == false) && ( */}
{auth?.feature.soApproval &&
(auth.webRole == 2 || auth.webRole == 3) &&
router.asPath.includes('/my/quotations/') &&
@@ -1354,34 +1318,6 @@ const Transaction = ({ id }) => {
)}
{transaction?.data?.products?.length > 0 && (
- // <div className='flex justify-end mt-4'>
- // <div className='w-1/4 grid grid-cols-2 gap-y-3 text-gray_r-12/80'>
- // <div className='text-right'>Subtotal</div>
- // <div className='text-right font-medium'>
- // {currencyFormat(transaction.data?.amountUntaxed)}
- // </div>
-
- // <div className='text-right'>
- // PPN {((PPN - 1) * 100).toFixed(0)}%
- // </div>
- // <div className='text-right font-medium'>
- // {currencyFormat(transaction.data?.amountTax)}
- // </div>
-
- // <div className='text-right whitespace-nowrap'>
- // Biaya Pengiriman
- // </div>
- // <div className='text-right font-medium'>
- // {currencyFormat(transaction.data?.deliveryAmount)}
- // </div>
-
- // <div className='text-right'>Grand Total</div>
- // <div className='text-right font-medium text-gray_r-12'>
- // {currencyFormat(transaction.data?.amountTotal)}
- // </div>
- // </div>
- // </div>
-
<div className='flex justify-end mt-4 flex-col items-end'>
<div className='w-1/4 grid grid-cols-2 gap-y-3 text-gray_r-12/80'>
<div className='text-right'>Total Belanja</div>
@@ -1437,7 +1373,6 @@ const Transaction = ({ id }) => {
<thead>
<tr>
<th>Nama Produk</th>
- {/* <th>Diskon</th> */}
<th>Jumlah</th>
<th>Harga</th>
<th>Subtotal</th>
@@ -1480,18 +1415,8 @@ const Transaction = ({ id }) => {
</div>
</div>
</td>
- {/* <td>
- {product.price.discountPercentage > 0
- ? `${product.price.discountPercentage}%`
- : ''}
- </td> */}
<td>{product.quantity}</td>
<td>
- {/* {product.price.discountPercentage > 0 && (
- <div className='line-through mb-1 text-caption-1 text-gray_r-12/70'>
- {currencyFormat(product.price.price)}
- </div>
- )} */}
<div>
{currencyFormat(product.price.priceDiscount)}
</div>
@@ -1512,58 +1437,6 @@ const Transaction = ({ id }) => {
</div>
</div>
</DesktopView>
-
- {/* {queryAirwayBill.data?.airways?.map((airway) => (
- <BottomPopup
- key={airway.waybillNumber}
- title='Detail Pengiriman'
- active={airwayBillPopup == airway.waybillNumber}
- close={() => setAirwayBillPopup(null)}
- >
- <div className='flex flex-col gap-y-4 my-4'>
- <div className='flex justify-between'>
- <div className='text-gray_r-11'>No Pengiriman</div>
- <div>{airway?.deliveryOrder?.name}</div>
- </div>
- <div className='flex justify-between'>
- <div className='text-gray_r-11'>Kurir</div>
- <div>{airway?.deliveryOrder?.carrier}</div>
- </div>
- <div className='flex justify-between'>
- <div className='text-gray_r-11'>No Resi</div>
- <div>{airway?.waybillNumber}</div>
- </div>
- </div>
-
- <div className='pt-4'>
- <div className='font-semibold text-body-1 mb-4'>Status Pengiriman</div>
- <ol class='relative border-l border-gray_r-7'>
- {airway?.manifests?.map((manifest, index) => (
- <li class='mb-6 ml-4' key={index}>
- <div
- class={`absolute w-3 h-3 rounded-full mt-1.5 -left-1.5 border ${
- index == 0 ? 'bg-red-600 border-red-600' : 'bg-gray_r-7 border-white'
- }`}
- />
- <time class='text-sm leading-none text-gray-400'>
- {manifest.datetime}
- </time>
- <p
- class={`leading-6 font-medium text-body-2 mt-2 ${
- index == 0 ? 'text-red-600' : 'text-gray_r-11'
- }`}
- >
- {manifest.description}
- </p>
- </li>
- ))}
- {(!airway?.manifests || airway?.manifests?.length == 0) && (
- <div className='badge-red text-sm'>Belum ada pengiriman</div>
- )}
- </ol>
- </div>
- </BottomPopup>
- ))} */}
</>
)
);
@@ -1589,24 +1462,7 @@ const SectionAddress = ({ address }) => {
{section.customer && <SectionContent address={address?.customer} />}
- {/* <Divider />
-
- <SectionButton
- label='Detail Pengiriman'
- active={section.shipping}
- toggle={() => toggleSection('shipping')}
- />
-
- {section.shipping && <SectionContent address={address?.shipping} />}
-
- <Divider />
-
- <SectionButton
- label='Detail Penagihan'
- active={section.invoice}
- toggle={() => toggleSection('invoice')}
- />
- {section.invoice && <SectionContent address={address?.invoice} />} */}
+ {/* Bagian shipping/invoice disembunyikan */}
</>
);
};
diff --git a/src/lib/transaction/components/TransactionStatusBadge.jsx b/src/lib/transaction/components/TransactionStatusBadge.jsx
index cb8cbcd9..d23b17cd 100644
--- a/src/lib/transaction/components/TransactionStatusBadge.jsx
+++ b/src/lib/transaction/components/TransactionStatusBadge.jsx
@@ -4,6 +4,10 @@ const TransactionStatusBadge = ({ status }) => {
text: ''
}
switch (status) {
+ case 'belum_bayar':
+ badgeProps.className.push('badge-solid-red')
+ badgeProps.text = 'Belum Bayar'
+ break
case 'cancel':
badgeProps.className.push('badge-solid-red')
badgeProps.text = 'Pesanan Batal'
diff --git a/src/lib/transaction/components/Transactions.jsx b/src/lib/transaction/components/Transactions.jsx
index 7efa773a..600518fa 100644
--- a/src/lib/transaction/components/Transactions.jsx
+++ b/src/lib/transaction/components/Transactions.jsx
@@ -1,11 +1,11 @@
+import axios from 'axios';
import { useRouter } from 'next/router';
-import { useEffect, useState, useRef } from 'react';
+import { useEffect, useState, useRef } from 'react';
import { toast } from 'react-hot-toast';
import {
EllipsisVerticalIcon,
MagnifyingGlassIcon,
ChevronDownIcon,
- ChevronUpIcon,
} from '@heroicons/react/24/outline';
import useAuth from '@/core/hooks/useAuth';
import {
@@ -38,17 +38,13 @@ import { Navigation } from 'swiper';
import 'swiper/css';
import 'swiper/css/navigation';
import { Calendar } from 'lucide-react';
-import DatePicker from 'react-datepicker';
-import 'react-datepicker/dist/react-datepicker.css';
import { DateRangePicker } from 'react-date-range';
-import { addDays } from 'date-fns';
-import 'react-date-range/dist/styles.css'; // main style file
-import 'react-date-range/dist/theme/default.css'; // theme css file
-import { Popover } from '@headlessui/react';
+import 'react-date-range/dist/styles.css';
+import 'react-date-range/dist/theme/default.css';
+
const Transactions = ({ context = '' }) => {
const auth = useAuth();
const router = useRouter();
- const swiperRef = useRef(null);
const {
q = '',
page = 1,
@@ -59,15 +55,11 @@ const Transactions = ({ context = '' }) => {
startDate = null,
endDate = new Date(),
} = router.query;
- const {
- productCart,
- setRefreshCart,
- setProductCart,
- refreshCart,
- isLoading,
- setIsloading,
- } = useProductCartContext();
+ const { setRefreshCart } = useProductCartContext();
+
const [inputQuery, setInputQuery] = useState(q);
+ const [cachedAllData, setCachedAllData] = useState(null); // Simpan data "All"
+ const [currentData, setCurrentData] = useState([]); // Data yang ditampilkan
const [toOthers, setToOthers] = useState(null);
const [toCancel, setToCancel] = useState(null);
const [listSites, setListSites] = useState([]);
@@ -75,17 +67,16 @@ const Transactions = ({ context = '' }) => {
const [siteFilter, setSiteFilter] = useState(site);
const [pageNew, setPageNew] = useState(page);
const [limitNew, setLimitNew] = useState(limit);
- // const [status, setStatus] = useState('idle');
const [statusNew, setStatusNew] = useState(status);
const [sortNew, setSortNew] = useState(sort);
const [contextNew, setcontextNew] = useState(router.query.context || context);
- const [dateRange, setDateRange] = useState([null, null]);
- // const [startDate, endDate] = dateRange;
const [isOpenCalender, setIsOpenCalender] = useState(false);
- const [cachedAllData, setCachedAllData] = useState(null); // Simpan data "All"
- const [currentData, setCurrentData] = useState([]); // Data yang ditampilkan
const calendarRef = useRef(null);
- const [isDateSelected, setIsDateSelected] = useState(false);
+ const isUnpaid = (s) =>
+ ['belum_bayar'].includes(String(s || '').toLowerCase());
+
+ // loading id utk tombol lanjutkan transaksi
+ const [contLoadingId, setContLoadingId] = useState(null);
const parseDate = (date) => {
if (!date || date === 'null') return null;
@@ -96,7 +87,7 @@ const Transactions = ({ context = '' }) => {
const [state, setState] = useState([
{
- startDate: startDate != null || 'null' ? parseDate(startDate) : null, // Gunakan `parseDate`
+ startDate: startDate != null || 'null' ? parseDate(startDate) : null,
endDate: startDate == null ? endDate : parseDate(endDate),
key: 'selection',
},
@@ -116,9 +107,11 @@ const Transactions = ({ context = '' }) => {
site:
siteFilter || (auth?.webRole === null && auth?.site ? auth.site : null),
};
+
const statuses = [
{ id: 'all', label: 'Semua' },
{ id: 'quotation', label: 'Pending Quotation' },
+ { id: 'belum_bayar', label: 'Belum Bayar' },
{ id: 'diproses', label: 'Pesanan Diproses' },
{ id: 'dikemas', label: 'Pesanan Dikemas' },
{ id: 'partial', label: 'Dikirim Sebagian' },
@@ -135,6 +128,7 @@ const Transactions = ({ context = '' }) => {
shipping: 'Pesanan Dikirim',
done: 'Pesanan Selesai',
cancel: 'Pesanan Dibatalkan',
+ belum_bayar: 'Belum Bayar',
};
const sortes = [
@@ -142,16 +136,16 @@ const Transactions = ({ context = '' }) => {
{ id: 'asc', label: 'dari yang terkecil' },
{ id: 'desc', label: 'dari yang terbesar' },
];
+
const { transactions } = useTransactions({ query });
+
const fetchSite = async () => {
const site = await getSite();
setListSites(site.sites);
};
const submitCancelTransaction = async () => {
- const isCancelled = await cancelTransactionApi({
- transaction: toCancel,
- });
+ const isCancelled = await cancelTransactionApi({ transaction: toCancel });
if (isCancelled) {
toast.success('Berhasil batalkan transaksi');
transactions.refetch();
@@ -159,23 +153,16 @@ const Transactions = ({ context = '' }) => {
setToCancel(null);
};
- const pageCount = Math.ceil(transactions?.data?.saleOrderTotal / limitNew);
- let pageQuery = _.omit(query, ['limit', 'offset', 'context']);
- pageQuery = _.pickBy(
- pageQuery,
- (value, key) => value !== '' && !(key === 'page' && value === '1')
+ const pageCount = Math.ceil(
+ (transactions?.data?.saleOrderTotal || 0) / (limitNew || 1)
);
- pageQuery = toQuery(pageQuery);
const handleSubmit = (e) => {
e.preventDefault();
const queryParams = {};
if (inputQuery) queryParams.q = inputQuery;
if (siteFilter) queryParams.site = siteFilter;
- router.push({
- pathname: router.pathname,
- query: queryParams,
- });
+ router.push({ pathname: router.pathname, query: queryParams });
};
const handleSiteFilterChange = (e) => {
@@ -183,10 +170,7 @@ const Transactions = ({ context = '' }) => {
const queryParams = {};
if (inputQuery) queryParams.q = inputQuery;
if (e.target.value) queryParams.site = e.target.value;
- router.push({
- pathname: router.pathname,
- query: queryParams,
- });
+ router.push({ pathname: router.pathname, query: queryParams });
};
const exportToExcel = (data, siteFilter) => {
@@ -201,19 +185,17 @@ const Transactions = ({ context = '' }) => {
];
const rowsToExport = [];
- data.forEach((saleOrder) => {
+ (data || []).forEach((saleOrder) => {
const row = {
'No. Transaksi': saleOrder.name,
'No. PO': saleOrder.purchaseOrderName || '-',
Tanggal: saleOrder.dateOrder || '-',
- 'Created By': saleOrder.address.customer?.name || '-',
+ 'Created By': saleOrder.address?.customer?.name || '-',
Salesperson: saleOrder.sales,
Total: currencyFormat(saleOrder.amountTotal),
Status: contextLabelMap[saleOrder.status] || saleOrder.status,
};
- if (siteFilter) {
- row['Site'] = siteFilter;
- }
+ if (siteFilter) row['Site'] = siteFilter;
rowsToExport.push(row);
});
@@ -226,13 +208,30 @@ const Transactions = ({ context = '' }) => {
XLSX.writeFile(workbook, 'transactions.xlsx');
};
+ const getAllData = async () => {
+ const qobj = {
+ name: q,
+ offset: (pageNew - 1) * limitNew,
+ limit: limitNew,
+ context: contextNew[statusNew] || 'all',
+ sort: sortNew,
+ startDate: state[0]?.startDate
+ ? state[0].startDate.toLocaleDateString('id-ID')
+ : null,
+ endDate: state[0]?.endDate
+ ? state[0].endDate.toLocaleDateString('id-ID')
+ : null,
+ site:
+ siteFilter || (auth?.webRole === null && auth?.site ? auth.site : null),
+ };
+ const queryString = toQuery(qobj);
+ const data = await transactionsApi({ query: queryString });
+ return data;
+ };
+
const handleExportCSV = async () => {
const dataToExport = await getAllData();
- exportToCSV(dataToExport?.saleOrders, siteFilter);
- };
-
- const exportToCSV = (data, siteFilter) => {
const fieldsToExport = [
'No. Transaksi',
'No. PO',
@@ -242,28 +241,23 @@ const Transactions = ({ context = '' }) => {
'Total',
'Status',
];
-
- if (siteFilter) {
- fieldsToExport.push('Site');
- }
-
- const rowsToExport = data.map((saleOrder) => {
- const row = [
- saleOrder.name,
- saleOrder.purchaseOrderName || '-',
- saleOrder.dateOrder || '-',
- saleOrder.address.customer?.name || '-',
- saleOrder.sales,
- currencyFormat(saleOrder.amountTotal),
- contextLabelMap[saleOrder.status] || saleOrder.status,
- ];
-
- if (siteFilter) {
- row.push(siteFilter);
- }
-
- return row.join(',');
- });
+ const rowsToExport =
+ dataToExport?.saleOrders?.map((saleOrder) => {
+ const row = [
+ saleOrder.name,
+ saleOrder.purchaseOrderName || '-',
+ saleOrder.dateOrder || '-',
+ saleOrder.address?.customer?.name || '-',
+ saleOrder.sales,
+ currencyFormat(saleOrder.amountTotal),
+ (contextLabelMap[saleOrder.status] || saleOrder.status || '').replace(
+ /,/g,
+ ' '
+ ),
+ ];
+ if (siteFilter) row.push((siteFilter || '').replace(/,/g, ' '));
+ return row.join(',');
+ }) || [];
const csvContent =
'data:text/csv;charset=utf-8,' +
@@ -278,66 +272,31 @@ const Transactions = ({ context = '' }) => {
document.body.removeChild(link);
};
- const getAllData = async () => {
- const query = {
- name: q,
- offset: (pageNew - 1) * limitNew,
- limit: limitNew,
- context: contextNew[statusNew] || 'all',
- sort: sortNew,
- startDate: state[0]?.startDate
- ? state[0].startDate.toLocaleDateString('id-ID')
- : null,
- endDate: state[0]?.endDate
- ? state[0].endDate.toLocaleDateString('id-ID')
- : null,
- site:
- siteFilter || (auth?.webRole === null && auth?.site ? auth.site : null),
- };
- const queryString = toQuery(query);
- const data = await transactionsApi({ query: queryString });
- return data;
- };
-
const handleExportExcel = async () => {
const dataToExport = await getAllData();
-
exportToExcel(dataToExport?.saleOrders, siteFilter);
};
const handleDownload = (format) => {
- handleExport(format);
+ if (format === 'csv') handleExportCSV();
+ else if (format === 'xlsx') handleExportExcel();
setIsOpen(false);
};
- const handleExport = (format) => {
- if (format === 'csv') {
- handleExportCSV();
- } else if (format === 'xlsx') {
- handleExportExcel();
- }
- };
-
useEffect(() => {
const handleClickOutside = (event) => {
- if (
- calendarRef.current &&
- !calendarRef.current.contains(event.target)
- ) {
+ if (calendarRef.current && !calendarRef.current.contains(event.target)) {
setIsOpenCalender(false);
}
};
-
- document.addEventListener("mousedown", handleClickOutside);
- return () => {
- document.removeEventListener("mousedown", handleClickOutside);
- };
+ document.addEventListener('mousedown', handleClickOutside);
+ return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
const startItem = 1 + (pageNew - 1) * limitNew;
const endItem = Math.min(
limitNew * pageNew,
- transactions?.data?.saleOrderTotal
+ transactions?.data?.saleOrderTotal || 0
);
useEffect(() => {
@@ -346,11 +305,8 @@ const Transactions = ({ context = '' }) => {
const handleBuyBack = async (products) => {
try {
- // setStatus('loading');
- console.log("Products to add:", products);
-
const results = await Promise.all(
- products.map((product) =>
+ (products || []).map((product) =>
upsertUserCart({
userId: auth.id,
type: 'product',
@@ -359,51 +315,42 @@ const Transactions = ({ context = '' }) => {
selected: true,
source: 'add_to_cart',
qtyAppend: true,
- }).catch(error => {
- return { error, product };
- })
+ }).catch((error) => ({ error, product }))
)
);
- const failedOperations = results.filter(result => result && result.error);
- // console.log(results);
-
- if (failedOperations.length > 0) {
- console.error('Some products failed to add to cart:', failedOperations);
- toast.error(`${failedOperations.length} produk gagal ditambahkan ke keranjang`);
-
- // You might want to proceed with the successful ones or handle differently
- if (failedOperations.length < products.length) {
- toast.success(`${products.length - failedOperations.length} produk berhasil ditambahkan ke keranjang`);
+ const failed = results.filter((r) => r && r.error);
+ if (failed.length > 0) {
+ toast.error(`${failed.length} produk gagal ditambahkan ke keranjang`);
+ if (failed.length < (products || []).length) {
+ toast.success(
+ `${
+ (products || []).length - failed.length
+ } produk berhasil ditambahkan`
+ );
setRefreshCart(true);
router.push('/shop/cart');
}
return;
}
-
- // All operations succeeded
setRefreshCart(true);
- toast.success('Semua produk berhasil ditambahkan ke keranjang belanja');
+ toast.success('Semua produk berhasil ditambahkan ke keranjang');
router.push('/shop/cart');
-
- } catch (error) {
- console.error('Gagal menambahkan produk ke keranjang:', error);
+ } catch {
toast.error('Terjadi kesalahan saat menambahkan produk ke keranjang');
- // setStatus('error');
}
};
-
const handleStatusChange = async (status) => {
setStatusNew(status);
setPageNew(1);
if (status === 'all' && cachedAllData) {
- setCurrentData(cachedAllData);
- return;
+ setCurrentData(cachedAllData);
+ return;
}
const data = await fetchSite(status, 1);
-
+
if (status === 'all') {
setCachedAllData(data);
}
@@ -411,19 +358,8 @@ const Transactions = ({ context = '' }) => {
setCurrentData(data);
};
- useEffect(() => {
- setCachedAllData([]);
- }, []);
-
-
const handleReset = () => {
- setState([
- {
- startDate: null,
- endDate: new Date(),
- key: 'selection',
- },
- ]);
+ setState([{ startDate: null, endDate: new Date(), key: 'selection' }]);
setIsOpenCalender(false);
router.push(`${router.pathname}`);
};
@@ -443,13 +379,88 @@ const Transactions = ({ context = '' }) => {
'November',
'Desember',
];
-
- const [day, month, year] = dateString.split('/');
+ const [day, month, year] = (dateString || '').split('/');
+ if (!day || !month || !year) return dateString || '-';
return `${day} ${months[parseInt(month, 10) - 1]} ${year}`;
};
+ // ==== Lanjutkan Transaksi (tanpa endpoint baru) ====
+ const handleContinuePayment = async (saleOrder) => {
+ try {
+ setContLoadingId(saleOrder.id);
+
+ const base = (process.env.NEXT_PUBLIC_ODOO_API_HOST || '').replace(
+ /\/$/,
+ ''
+ );
+ const token = auth?.token;
+ const partnerId = auth?.partnerId;
+
+ // 1. TRIGGER GENERATE + GET URL
+ const { data: response } = await axios.get(
+ `${base}/api/v1/partner/${partnerId}/sale_order/${saleOrder.id}`,
+ {
+ params: { ensure_payment_link: 1, ts: Date.now() },
+ headers: { Token: token, 'Cache-Control': 'no-cache' },
+ timeout: 10000,
+ }
+ );
+
+ // 2. EKSTRAK URL
+ let paymentUrl =
+ response?.result?.payment_summary?.redirect_url ||
+ response?.data?.result?.payment_summary?.redirect_url;
+
+ // 3. JIKA DAPAT URL, BUKA
+ if (paymentUrl) {
+ window.location.href = paymentUrl;
+ toast.success('Membuka halaman pembayaran…');
+ return;
+ }
+
+ // 4. FALLBACK: COBA TANPA ensure_payment_link
+ try {
+ const { data: fallbackResponse } = await axios.get(
+ `${base}/api/v1/partner/${partnerId}/sale_order/${saleOrder.id}`,
+ { headers: { Token: token }, timeout: 5000 }
+ );
+
+ const fallbackUrl =
+ fallbackResponse?.result?.payment_summary?.redirect_url ||
+ fallbackResponse?.data?.result?.payment_summary?.redirect_url;
+
+ if (fallbackUrl) {
+ window.location.href = fallbackUrl;
+ toast.success('Membuka halaman pembayaran…');
+ return;
+ }
+ } catch (fallbackError) {
+ // Continue to next fallback
+ }
+
+ // 5. ULTIMATE FALLBACK: PAKAI URL DARI DATA LAMA
+ const existingUrl =
+ saleOrder?.paymentSummary?.redirectUrl ||
+ saleOrder?.payment_summary?.redirect_url;
+
+ if (existingUrl) {
+ window.open(existingUrl, '_blank', 'noopener,noreferrer');
+ toast.success('Membuka halaman pembayaran…');
+ } else {
+ toast.error('Link pembayaran tidak ditemukan. Silakan coba lagi.');
+ }
+ } catch (error) {
+ toast.error(
+ error.response?.data?.description || 'Gagal memproses pembayaran'
+ );
+ } finally {
+ setContLoadingId(null);
+ }
+ };
+
return (
<>
+ {/* ===== MOBILE ===== */}
<MobileView>
<div className=' flex flex-col gap-y-4'>
<div className='grid grid-cols-[40%_40%_15%] justify-between items-center gap-2 w-full '>
@@ -475,23 +486,22 @@ const Transactions = ({ context = '' }) => {
</option>
))}
</select>
- <div ref={calendarRef} className="relative inline-block">
- <button
- type='button'
- className='p-1 w-full h-auto cursor-pointer border hover:bg-gray-100 rounded transition duration-150 ease-in-out flex items-center justify-center'
- onClick={() => setIsOpenCalender((prev) => !prev)}
- >
- <span className='text-nowrap px-1 truncate flex items-center gap-1'>
- {state[0]?.startDate ? (
- `${state[0].startDate.toLocaleDateString()} - ${state[0].endDate.toLocaleDateString()}`
- ) : (
- <Calendar size={20} className="text-gray-500" />
- )}
- </span>
- </button>
- {isOpenCalender && (
+ <div ref={calendarRef} className='relative inline-block'>
+ <button
+ type='button'
+ className='p-1 w-full h-auto cursor-pointer border hover:bg-gray-100 rounded transition duration-150 ease-in-out flex items-center justify-center'
+ onClick={() => setIsOpenCalender((prev) => !prev)}
+ >
+ <span className='text-nowrap px-1 truncate flex items-center gap-1'>
+ {state[0]?.startDate ? (
+ `${state[0].startDate.toLocaleDateString()} - ${state[0].endDate.toLocaleDateString()}`
+ ) : (
+ <Calendar size={20} className='text-gray-500' />
+ )}
+ </span>
+ </button>
+ {isOpenCalender && (
<div className='absolute right-1 mt-2 bg-white p-4 rounded shadow-lg z-50'>
- {/* Tombol silang di sudut kanan atas */}
<button
onClick={() => setIsOpenCalender(false)}
className='absolute top-2 right-2 text-gray-600 hover:text-black text-xl font-bold'
@@ -508,37 +518,13 @@ const Transactions = ({ context = '' }) => {
className='w-full'
/>
<style>{`
- /* Atur container agar menjadi column */
- .rdrCalendarWrapper {
- display: flex;
- flex-direction: column;
- }
- .rdrDateRangePickerWrapper {
- display: flex;
- flex-direction: column;
- }
-
- /* Pindahkan rdrStaticRanges ke atas */
- .rdrDefinedRangesWrapper {
- order: -1;
- width: fit-content;
- }
- .rdrStaticRanges {
- flex-direction: row;
- margin-right: 2px;
- }
-
- /* Sembunyikan bagian input manual */
- .rdrInputRanges {
- display: none !important;
- }
-
- .rdrStaticRangeLabel {
- padding: 10px 10px;
- }
- .rdrMonth {
- width: -moz-available;
- }
+ .rdrCalendarWrapper{display:flex;flex-direction:column;}
+ .rdrDateRangePickerWrapper{display:flex;flex-direction:column;}
+ .rdrDefinedRangesWrapper{order:-1;width:fit-content;}
+ .rdrStaticRanges{flex-direction:row;margin-right:2px;}
+ .rdrInputRanges{display:none !important;}
+ .rdrStaticRangeLabel{padding:10px 10px;}
+ .rdrMonth{width:-moz-available;}
`}</style>
<div className='flex flex-row justify-end gap-3 mt-2'>
<button
@@ -549,68 +535,7 @@ const Transactions = ({ context = '' }) => {
</button>
</div>
</div>
- )}
- </div>
- {/* <div className='border border-gray-300 rounded-lg px-1 py-1 bg-white shadow-sm focus:outline-none focus:ring-2 focus:ring-red-500 text-xs'>
- <DatePicker
- closeOnScroll={(e) => e.target === document}
- selectsRange={true}
- startDate={startDate}
- endDate={endDate}
- dateFormat='dd/MM'
- className='w-full'
- maxDate={new Date()}
- placeholderText='Semua Tanggal'
- onChange={(update) => {
- setDateRange(update);
- }}
- withPortal
- isClearable={true}
- />
- </div> */}
- </div>
- <div className='flex flex-row justify-between items-center gap-2'>
- <form className='flex' onSubmit={handleSubmit}>
- <button
- className='btn-light border-r-0 rounded-r-none bg-transparent px-3'
- type='submit'
- >
- <MagnifyingGlassIcon className='w-6' />
- </button>
- <input
- type='text'
- className='form-input border-l-0 rounded-l-none text-xs'
- placeholder='Cari Transaksi...'
- value={inputQuery}
- onChange={(e) => setInputQuery(e.target.value)}
- />
- </form>
- <div className='flex flex-row gap-2 items-center justify-center text-nowrap'>
- <p className='text-xs'>
- Menampilkan {startItem}-
- {endItem
- ? endItem
- : transactions?.data?.saleOrderTotal
- ? transactions?.data?.saleOrderTotal
- : limitNew * pageNew}{' '}
- dari{' '}
- {transactions?.data?.saleOrderTotal
- ? transactions?.data?.saleOrderTotal
- : limitNew * pageNew}
- </p>
- <select
- id='limitSelect'
- value={limitNew}
- onChange={(e) => {
- setLimitNew(Number(e.target.value));
- setPageNew(1);
- }}
- className='border p-2 text-xs'
- >
- <option value={10}>10</option>
- <option value={15}>15</option>
- <option value={20}>20</option>
- </select>
+ )}
</div>
</div>
@@ -634,7 +559,9 @@ const Transactions = ({ context = '' }) => {
>
<div className='flex flex-row justify-between items-start'>
<Link href={`${router.pathname}/${saleOrder.id}`}>
- <h2 className='text-danger-500 text-base'>{saleOrder.name}</h2>
+ <h2 className='text-danger-500 text-base'>
+ {saleOrder.name}
+ </h2>
<span className='font-medium text-black opacity-75'>
{formatDate(saleOrder.dateOrder.split(' ')[0]) || '-'}
</span>
@@ -681,29 +608,30 @@ const Transactions = ({ context = '' }) => {
<div className='flex flex-row gap-1 justify-start items-center'>
{saleOrder.products
.slice(1, 4)
- .map((product, index) => (
+ .map((product, idx) => (
<Image
- key={index} // Tambahkan key untuk setiap elemen dalam map()
+ key={idx}
src={product?.parent?.image}
alt={product?.name}
className='object-contain object-center border border-gray_r-6 h-8 w-8 rounded-md'
/>
))}
{saleOrder.products.length > 4 ? (
- <Link
- href={`${router.pathname}/${saleOrder?.id}`}
- className='text-red-500 text-nowrap'
- >
- +{saleOrder.products.length - 4} lihat semua produk
- </Link>
- ) : (
- <Link
- href={`${router.pathname}/${saleOrder?.id}`}
- className='text-red-500 text-nowrap'
- >
- Lihat semua produk
- </Link>
- )}
+ <Link
+ href={`${router.pathname}/${saleOrder?.id}`}
+ className='text-red-500 text-nowrap'
+ >
+ +{saleOrder.products.length - 4} lihat semua
+ produk
+ </Link>
+ ) : (
+ <Link
+ href={`${router.pathname}/${saleOrder?.id}`}
+ className='text-red-500 text-nowrap'
+ >
+ Lihat semua produk
+ </Link>
+ )}
</div>
)}
</div>
@@ -716,58 +644,50 @@ const Transactions = ({ context = '' }) => {
</div>
</div>
<div className='col-span-2 h-[1px] w-full bg-gray-300'></div>
- <div className='flex flex-row gap-3 justify-between items-center text-sm'>
- <div className='flex flex-col text-black text-xs'>
- <p className='font-extralight text-sm'>Total Harga</p>
+
+ <div className='flex flex-col gap-3 text-sm'>
+ <div className='flex flex-col text-black'>
+ <p className='font-extralight'>Total Harga</p>
<p className='font-semibold text-lg'>
{currencyFormat(saleOrder.amountTotal)}
</p>
</div>
- <div>
- <button
- type='button'
- onClick={() => handleBuyBack(saleOrder.products)}
- className='flex-1 py-2 btn-solid-red text-nowrap'
- >
- Beli Lagi
- </button>
- </div>
- </div>
- {/* <div className='grid grid-cols-2 mt-3'>
- <div>
- <span className='text-caption-2 text-gray_r-11'>
- No. Purchase Order
- </span>
- <p className='mt-1 font-medium text-gray_r-12'>
- {saleOrder.purchaseOrderName || '-'}
- </p>
- </div>
- <div className='text-right'>
- <span className='text-caption-2 text-gray_r-11'>
- Total Invoice
- </span>
- <p className='mt-1 font-medium text-gray_r-12'>
- {saleOrder.invoiceCount} Invoice
- </p>
- </div>
- </div> */}
- {/* <div className='grid grid-cols-2 mt-3'>
- <div>
- <span className='text-caption-2 text-gray_r-11'>Sales</span>
- <p className='mt-1 font-medium text-gray_r-12'>
- {saleOrder.sales}
- </p>
- </div>
- <div className='text-right'>
- <span className='text-caption-2 text-gray_r-11'>
- Total Harga
- </span>
- <p className='mt-1 font-medium text-gray_r-12'>
- {currencyFormat(saleOrder.amountTotal)}
- </p>
+ <div className='flex flex-col gap-2 w-full'>
+ {/* Beli Lagi hanya muncul jika status bukan unpaid */}
+ {!isUnpaid(saleOrder.status) && (
+ <button
+ type='button'
+ onClick={(e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ handleBuyBack(saleOrder.products);
+ }}
+ className='w-full py-2 btn-solid-red text-center rounded-md'
+ >
+ Beli Lagi
+ </button>
+ )}
+
+ {/* Bayar Sekarang hanya kalau eligible */}
+ {saleOrder?.eligibleContinue && (
+ <button
+ type='button'
+ disabled={contLoadingId === saleOrder.id}
+ onClick={(e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ handleContinuePayment(saleOrder);
+ }}
+ className='w-full py-2 text-center rounded-md border border-red-300 text-red-500 bg-white disabled:opacity-60'
+ >
+ {contLoadingId === saleOrder.id
+ ? 'Memproses…'
+ : 'Bayar Sekarang'}
+ </button>
+ )}
</div>
- </div> */}
+ </div>
</Link>
</div>
))}
@@ -775,7 +695,6 @@ const Transactions = ({ context = '' }) => {
<Pagination
pageCount={pageCount}
currentPage={parseInt(pageNew)}
- // url={router.pathname + pageQuery}
url={`${router.pathname}?${toQuery(_.omit(query, ['page']))}`}
className='mt-2 mb-2'
/>
@@ -785,6 +704,30 @@ const Transactions = ({ context = '' }) => {
active={toOthers}
close={() => setToOthers(null)}
>
+ {transactions.data?.status === 'draft' && (
+ <>
+ <button
+ className='text-left disabled:opacity-60'
+ disabled={toOthers?.status != 'draft'}
+ onClick={() => {
+ downloadQuotation(toOthers);
+ setToOthers(null);
+ }}
+ >
+ Download Quotation
+ </button>
+ <button
+ className='text-left disabled:opacity-60'
+ disabled={toOthers?.status != 'waiting'}
+ onClick={() => {
+ setToCancel(toOthers);
+ setToOthers(null);
+ }}
+ >
+ Batalkan Transaksi
+ </button>
+ </>
+ )}
<div className='flex flex-col gap-y-4 mt-2'>
<button
className='text-left disabled:opacity-60'
@@ -796,26 +739,6 @@ const Transactions = ({ context = '' }) => {
>
Download PO
</button>
- <button
- className='text-left disabled:opacity-60'
- disabled={toOthers?.status != 'draft'}
- onClick={() => {
- downloadQuotation(toOthers);
- setToOthers(null);
- }}
- >
- Download Quotation
- </button>
- <button
- className='text-left disabled:opacity-60'
- disabled={toOthers?.status != 'waiting'}
- onClick={() => {
- setToCancel(toOthers);
- setToOthers(null);
- }}
- >
- Batalkan Transaksi
- </button>
</div>
</BottomPopup>
@@ -848,6 +771,7 @@ const Transactions = ({ context = '' }) => {
</div>
</MobileView>
+ {/* ===== DESKTOP ===== */}
<DesktopView>
<div className='container mx-auto flex py-10'>
<div className='w-3/12 pr-4'>
@@ -900,51 +824,36 @@ const Transactions = ({ context = '' }) => {
)}
</div>
</div>
- <div className=''>
- <div
- class='flex items-center p-4 mb-4 text-sm border border-yellow-500 text-yellow-800 rounded-lg bg-yellow-50'
- role='alert'
- >
- <svg
- class='flex-shrink-0 inline w-5 h-5 mr-2'
- aria-hidden='true'
- fill='currentColor'
- viewBox='0 0 20 20'
- >
- <path d='M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z' />
- </svg>
- <span class='sr-only'>Info</span>
- <div className='text-justify flex flex-col gap-1'>
- <p className='font-bold text-black'>Info Transaksi</p>
- <span className='text-black'>
- Gunakan filter status untuk mempermudah pencarian transaksi anda di Daftar Transaksi
- </span>
- </div>
- </div>
- </div>
+
<div className='flex flex-col gap-y-2 border rounded-lg mb-2 w-full'>
<div className='p-2'>
<div className='flex items-center space-x-3'>
<span className='text-base font-semibold text-gray-600'>
Status
</span>
- <div className="relative w-full overflow-hidden">
- {/* Container flex: tombol prev - swiper - tombol next */}
- <div className="flex items-center space-x-2">
-
- {/* Prev */}
- <button className="custom-prev w-8 h-8 flex-shrink-0 flex items-center justify-center bg-white border rounded-full shadow z-1">
- <svg className="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
+ <div className='relative w-full overflow-hidden'>
+ <div className='flex items-center space-x-2'>
+ <button className='custom-prev w-8 h-8 flex-shrink-0 flex items-center justify-center bg-white border rounded-full shadow z-1'>
+ <svg
+ className='w-4 h-4 text-gray-500'
+ fill='none'
+ stroke='currentColor'
+ viewBox='0 0 24 24'
+ >
+ <path
+ strokeLinecap='round'
+ strokeLinejoin='round'
+ strokeWidth={2}
+ d='M15 19l-7-7 7-7'
+ />
</svg>
</button>
- {/* Swiper container scrollable */}
- <div className="w-full overflow-hidden">
+ <div className='w-full overflow-hidden'>
<Swiper
spaceBetween={10}
- slidesPerView="auto"
- className="status-swiper"
+ slidesPerView='auto'
+ className='status-swiper'
modules={[Navigation]}
navigation={{
nextEl: '.custom-next',
@@ -952,12 +861,13 @@ const Transactions = ({ context = '' }) => {
}}
>
{statuses.map((status) => (
- <SwiperSlide key={status.id} className="!w-auto">
+ <SwiperSlide key={status.id} className='!w-auto'>
<button
className={`px-4 py-1 text-sm font-medium border rounded-lg transition whitespace-nowrap
- ${statusNew === status.id
- ? 'border-red-500 text-red-500 bg-white'
- : 'border-gray-300 text-gray-400 bg-gray-100 hover:bg-gray-200'
+ ${
+ statusNew === status.id
+ ? 'border-red-500 text-red-500 bg-white'
+ : 'border-gray-300 text-gray-400 bg-gray-100 hover:bg-gray-200'
}`}
onClick={() => handleStatusChange(status.id)}
>
@@ -968,16 +878,26 @@ const Transactions = ({ context = '' }) => {
</Swiper>
</div>
- {/* Next */}
- <button className="custom-next w-8 h-8 flex-shrink-0 flex items-center justify-center bg-white border rounded-full shadow z-10">
- <svg className="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
+ <button className='custom-next w-8 h-8 flex-shrink-0 flex items-center justify-center bg-white border rounded-full shadow z-10'>
+ <svg
+ className='w-4 h-4 text-gray-500'
+ fill='none'
+ stroke='currentColor'
+ viewBox='0 0 24 24'
+ >
+ <path
+ strokeLinecap='round'
+ strokeLinejoin='round'
+ strokeWidth={2}
+ d='M9 5l7 7-7 7'
+ />
</svg>
</button>
</div>
</div>
</div>
</div>
+
<div className='flex flex-row items-center justify-between mb-2 p-2'>
<div className='flex flex-col gap-2 pb-2'>
{listSites?.length > 0 ? (
@@ -1013,6 +933,7 @@ const Transactions = ({ context = '' }) => {
</button>
</form>
</div>
+
<div className='flex flex-row gap-4 items-center justify-center'>
<p>
Menampilkan {startItem}-
@@ -1038,87 +959,66 @@ const Transactions = ({ context = '' }) => {
<option value={10}>10</option>
<option value={15}>15</option>
<option value={20}>20</option>
- <option value={transactions?.data?.saleOrderTotal}>Semua</option>
+ <option value={transactions?.data?.saleOrderTotal}>
+ Semua
+ </option>
</select>
- <div ref={calendarRef} className="relative inline-block">
+
+ <div ref={calendarRef} className='relative inline-block'>
<button
type='button'
className='p-2 w-auto h-auto cursor-pointer border hover:bg-gray-100 rounded transition duration-150 ease-in-out flex items-center justify-center'
onClick={() => setIsOpenCalender((prev) => !prev)}
>
<span className='text-nowrap px-1 truncate flex items-center gap-1'>
- {state[0]?.startDate ? (
- `${state[0].startDate.toLocaleDateString()} - ${state[0].endDate.toLocaleDateString()}`
- ) : (
- <Calendar size={16} className="text-gray-500" />
- )}
+ {state[0]?.startDate ? (
+ `${state[0].startDate.toLocaleDateString()} - ${state[0].endDate.toLocaleDateString()}`
+ ) : (
+ <Calendar size={16} className='text-gray-500' />
+ )}
</span>
</button>
{isOpenCalender && (
- <div className='absolute right-10 mt-2 bg-white p-4 rounded shadow-lg z-50'>
- {/* Tombol silang di sudut kanan atas */}
- <button
- onClick={() => setIsOpenCalender(false)}
- className='absolute top-2 right-2 text-gray-600 hover:text-black text-xl font-bold'
- >
- &times;
- </button>
- <DateRangePicker
- onChange={(item) => setState([item.selection])}
- showSelectionPreview={false}
- maxDate={new Date()}
- moveRangeOnFirstSelection={false}
- months={1}
- ranges={state}
- className='w-full'
- />
- <style>{`
- /* Atur container agar menjadi column */
- .rdrCalendarWrapper {
- display: flex;
- flex-direction: column;
- }
- .rdrDateRangePickerWrapper {
- display: flex;
- flex-direction: column;
- }
-
- /* Pindahkan rdrStaticRanges ke atas */
- .rdrDefinedRangesWrapper {
- order: -1;
- width: fit-content;
- }
- .rdrStaticRanges {
- flex-direction: row;
- margin-right: 2px;
- }
-
- /* Sembunyikan bagian input manual */
- .rdrInputRanges {
- display: none !important;
- }
-
- .rdrStaticRangeLabel {
- padding: 10px 10px;
- }
- .rdrMonth {
- width: -moz-available;
- }
- `}</style>
- <div className='flex flex-row justify-end gap-3 mt-2'>
+ <div className='absolute right-10 mt-2 bg-white p-4 rounded shadow-lg z-50'>
<button
- className='px-4 py-1 bg-red-500 text-white rounded'
- onClick={handleReset}
+ onClick={() => setIsOpenCalender(false)}
+ className='absolute top-2 right-2 text-gray-600 hover:text-black text-xl font-bold'
>
- Reset
+ &times;
</button>
+ <DateRangePicker
+ onChange={(item) => setState([item.selection])}
+ showSelectionPreview={false}
+ maxDate={new Date()}
+ moveRangeOnFirstSelection={false}
+ months={1}
+ ranges={state}
+ className='w-full'
+ />
+ <style>{`
+ .rdrCalendarWrapper{display:flex;flex-direction:column;}
+ .rdrDateRangePickerWrapper{display:flex;flex-direction:column;}
+ .rdrDefinedRangesWrapper{order:-1;width:fit-content;}
+ .rdrStaticRanges{flex-direction:row;margin-right:2px;}
+ .rdrInputRanges{display:none !important;}
+ .rdrStaticRangeLabel{padding:10px 10px;}
+ .rdrMonth{width:-moz-available;}
+ `}</style>
+ <div className='flex flex-row justify-end gap-3 mt-2'>
+ <button
+ className='px-4 py-1 bg-red-500 text-white rounded'
+ onClick={handleReset}
+ >
+ Reset
+ </button>
+ </div>
</div>
- </div>
)}
</div>
</div>
</div>
</div>
+
<div className='flex justify-center items-center'>
{!transactions.isLoading &&
transactions?.data?.saleOrders?.length == 0 && (
@@ -1156,11 +1056,9 @@ const Transactions = ({ context = '' }) => {
<p className='text-red-500'>{saleOrder.name}</p>
<p className='text-black'>
Salesperson:{' '}
- {
- <span className='font-semibold'>
- {saleOrder.sales}
- </span>
- }
+ <span className='font-semibold'>
+ {saleOrder.sales}
+ </span>
</p>
</div>
<div className='text-black'>
@@ -1172,7 +1070,9 @@ const Transactions = ({ context = '' }) => {
</span>
</div>
</div>
+
<hr className='mt-3 mb-3 border border-gray-100' />
+
<div className='flex flex-row gap-2 justify-between items-center '>
<div className='flex justify-start w-3/4 flex-col gap-2'>
<div className='flex gap-2'>
@@ -1207,9 +1107,9 @@ const Transactions = ({ context = '' }) => {
<div className='flex flex-row gap-1 justify-start items-center'>
{saleOrder.products
.slice(1, 4)
- .map((product, index) => (
+ .map((product, idx) => (
<Image
- key={index} // Tambahkan key untuk setiap elemen dalam map()
+ key={idx}
src={product?.parent?.image}
alt={product?.name}
className='object-contain object-center border border-gray_r-6 h-16 w-16 rounded-md'
@@ -1220,7 +1120,8 @@ const Transactions = ({ context = '' }) => {
href={`${router.pathname}/${saleOrder?.id}`}
className='text-red-500 text-nowrap'
>
- +{saleOrder.products.length - 4} lihat semua produk
+ +{saleOrder.products.length - 4}{' '}
+ lihat semua produk
</Link>
) : (
<Link
@@ -1244,25 +1145,48 @@ const Transactions = ({ context = '' }) => {
</p>
</div>
</div>
+
<div className='w-[1px] h-24 bg-gray-300'></div>
- <div className='w-1/4 flex flex-row gap-3 justify-center items-center'>
+
+ <div className='w-1/4 flex flex-col gap-2 items-center justify-center text-center pl-5'>
<div className='flex flex-col text-black'>
- <p>Total Harga</p>
- <p className='font-bold'>
+ <p className='text-sm'>Total Harga</p>
+ <p className='font-bold text-lg'>
{currencyFormat(saleOrder.amountTotal)}
</p>
</div>
- <div>
+
+ {!isUnpaid(saleOrder.status) && (
<button
type='button'
- onClick={() =>
- handleBuyBack(saleOrder.products)
- }
- className='flex-1 py-2 btn-solid-red text-nowrap'
+ onClick={(e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ handleBuyBack(saleOrder.products);
+ }}
+ className='w-full py-2 btn-solid-red text-nowrap rounded-md'
>
Beli Lagi
</button>
- </div>
+ )}
+
+ {/* Bayar Sekarang: hanya kalau eligible */}
+ {saleOrder?.eligibleContinue && (
+ <button
+ type='button'
+ disabled={contLoadingId === saleOrder.id}
+ onClick={(e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ handleContinuePayment(saleOrder);
+ }}
+ className='w-full py-2 text-nowrap border border-red-500 text-red-500 rounded-md disabled:opacity-60'
+ >
+ {contLoadingId === saleOrder.id
+ ? 'Memproses…'
+ : 'Bayar Sekarang'}
+ </button>
+ )}
</div>
</div>
</Link>
@@ -1271,70 +1195,10 @@ const Transactions = ({ context = '' }) => {
</div>
)}
</div>
- {/* <table className='table-data'>
- <thead>
- <tr>
- <th>No. Transaksi</th>
- <th>No. PO</th>
- <th>Tanggal</th>
- <th>Created By</th>
- {auth?.feature?.soApproval && <th>Site</th>}
- <th className='!text-left'>Salesperson</th>
- <th className='!text-left'>Total</th>
- <th>Status</th>
- </tr>
- </thead>
- <tbody>
- {transactions.isLoading && (
- <tr>
- <td colSpan={7}>
- <div className='flex justify-center my-2'>
- <Spinner className='w-6 text-gray_r-12/50 fill-gray_r-12' />
- </div>
- </td>
- </tr>
- )}
- {!transactions.isLoading &&
- (!transactions?.data?.saleOrders ||
- transactions?.data?.saleOrders?.length == 0) && (
- <tr>
- <td colSpan={7}>Tidak ada transaksi</td>
- </tr>
- )}
- {transactions.data?.saleOrders?.map((saleOrder) => (
- <tr key={saleOrder.id}>
- <td>
- <Link
- className='whitespace-nowrap'
- href={`${router.pathname}/${saleOrder.id}`}
- >
- {saleOrder.name}
- </Link>
- </td>
- <td>{saleOrder.purchaseOrderName || '-'}</td>
- <td>{saleOrder.dateOrder || '-'}</td>
- <td>{saleOrder.address.customer?.name || '-'}</td>
- {auth?.feature?.soApproval && (
- <td>{saleOrder.sitePartner || '-'}</td>
- )}
- <td className='!text-left'>{saleOrder.sales}</td>
- <td className='!text-left'>
- {currencyFormat(saleOrder.amountTotal)}
- </td>
- <td>
- <div className='flex justify-center'>
- <TransactionStatusBadge status={saleOrder.status} />
- </div>
- </td>
- </tr>
- ))}
- </tbody>
- </table> */}
<Pagination
pageCount={pageCount}
currentPage={parseInt(pageNew)}
- // url={router.pathname + (pageQuery ? `?${pageQuery}` : '')}
url={`${router.pathname}?${toQuery(_.omit(query, ['page']))}`}
className='mt-2 mb-2'
/>