From 3a9a0995dad8d3deb602534814a6bb79b26afb54 Mon Sep 17 00:00:00 2001 From: Miqdad Date: Wed, 27 Aug 2025 13:16:59 +0700 Subject: Push Sementara --- src/lib/transaction/components/Transactions.jsx | 615 ++++++++---------------- 1 file changed, 196 insertions(+), 419 deletions(-) (limited to 'src/lib') diff --git a/src/lib/transaction/components/Transactions.jsx b/src/lib/transaction/components/Transactions.jsx index fab9eb5c..0d7e3f2c 100644 --- a/src/lib/transaction/components/Transactions.jsx +++ b/src/lib/transaction/components/Transactions.jsx @@ -1,11 +1,10 @@ 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,13 +37,11 @@ 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'; +import { fetchPaymentSummary } from '../api/regenerate'; + const Transactions = ({ context = '' }) => { const auth = useAuth(); const router = useRouter(); @@ -59,14 +56,8 @@ 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 [toOthers, setToOthers] = useState(null); const [toCancel, setToCancel] = useState(null); @@ -75,17 +66,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 [cachedAllData, setCachedAllData] = useState(null); + const [currentData, setCurrentData] = useState([]); const calendarRef = useRef(null); - const [isDateSelected, setIsDateSelected] = useState(false); + + // loading id utk tombol lanjutkan transaksi + const [contLoadingId, setContLoadingId] = useState(null); const parseDate = (date) => { if (!date || date === 'null') return null; @@ -96,7 +86,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,6 +106,7 @@ const Transactions = ({ context = '' }) => { site: siteFilter || (auth?.webRole === null && auth?.site ? auth.site : null), }; + const statuses = [ { id: 'all', label: 'Semua' }, { id: 'quotation', label: 'Pending Quotation' }, @@ -142,16 +133,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(); @@ -172,10 +163,7 @@ const Transactions = ({ context = '' }) => { 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 +171,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) => { @@ -211,9 +196,7 @@ const Transactions = ({ context = '' }) => { 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 +209,30 @@ const Transactions = ({ context = '' }) => { XLSX.writeFile(workbook, 'transactions.xlsx'); }; + 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 handleExportCSV = async () => { const dataToExport = await getAllData(); - exportToCSV(dataToExport?.saleOrders, siteFilter); - }; - - const exportToCSV = (data, siteFilter) => { const fieldsToExport = [ 'No. Transaksi', 'No. PO', @@ -242,28 +242,20 @@ 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, + ]; + if (siteFilter) row.push(siteFilter); + return row.join(','); + }) || []; const csvContent = 'data:text/csv;charset=utf-8,' + @@ -278,60 +270,25 @@ 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; @@ -346,9 +303,6 @@ const Transactions = ({ context = '' }) => { const handleBuyBack = async (products) => { try { - // setStatus('loading'); - console.log("Products to add:", products); - const results = await Promise.all( products.map((product) => upsertUserCart({ @@ -359,55 +313,44 @@ 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); - + const failedOperations = results.filter((r) => r && r.error); 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 + toast.error( + `${failedOperations.length} produk gagal ditambahkan ke keranjang` + ); if (failedOperations.length < products.length) { - toast.success(`${products.length - failedOperations.length} produk berhasil ditambahkan ke keranjang`); + toast.success( + `${ + products.length - failedOperations.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); 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); - } - + if (status === 'all') setCachedAllData(data); setCurrentData(data); }; @@ -415,15 +358,8 @@ const Transactions = ({ context = '' }) => { 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,68 @@ const Transactions = ({ context = '' }) => { 'November', 'Desember', ]; - const [day, month, year] = dateString.split('/'); return `${day} ${months[parseInt(month, 10) - 1]} ${year}`; }; + // ==== Lanjutkan Transaksi ==== + const handleContinuePayment = async (saleOrder) => { + try { + const partnerId = + auth?.partnerId || auth?.partner_id || auth?.partner?.id || auth?.id; + + setContLoadingId(saleOrder.id); + + // 1) coba pakai URL yang sudah dikirim dari list + const existingUrl = + saleOrder?.paymentSummary?.redirectUrl || + saleOrder?.paymentLinkMidtrans; + + if (existingUrl) { + window.open(existingUrl, '_blank', 'noopener,noreferrer'); + toast.success('Membuka halaman pembayaran…'); + return; + } + + // 2) fallback: generate baru via API + const res = await fetchPaymentSummary({ + partnerId, + saleOrderId: saleOrder.id, + autogen: true, + }); + + const eligible = + res?.eligible_continue ?? + res?.eligibleContinue ?? + res?.eligible ?? + false; + + if (!eligible) { + toast.error('Transaksi belum memenuhi syarat untuk dilanjutkan.'); + return; + } + + const redirectUrl = res?.redirect_url || res?.redirectUrl; + if (redirectUrl) { + window.open(redirectUrl, '_blank', 'noopener,noreferrer'); + toast.success('Membuka halaman pembayaran…'); + } else { + toast.error('Link pembayaran belum tersedia.'); + } + } catch (e) { + const msg = + e?.response?.data?.description || + e?.message || + 'Gagal melanjutkan transaksi'; + toast.error(msg); + } finally { + setContLoadingId(null); + } + }; + return ( <> + {/* ===== MOBILE ===== */}
@@ -491,7 +482,6 @@ const Transactions = ({ context = '' }) => { {isOpenCalender && (
- {/* Tombol silang di sudut kanan atas */}
)}
- {/*
- 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} - /> -
*/} -
-
-
- - setInputQuery(e.target.value)} - /> -
-
-

- Menampilkan {startItem}- - {endItem - ? endItem - : transactions?.data?.saleOrderTotal - ? transactions?.data?.saleOrderTotal - : limitNew * pageNew}{' '} - dari{' '} - {transactions?.data?.saleOrderTotal - ? transactions?.data?.saleOrderTotal - : limitNew * pageNew} -

- -
{transactions.isLoading && ( @@ -683,9 +588,9 @@ const Transactions = ({ context = '' }) => {
{saleOrder.products .slice(1, 4) - .map((product, index) => ( + .map((product, idx) => ( {product?.name} {
+

Total Harga

@@ -727,7 +633,6 @@ const Transactions = ({ context = '' }) => {

- {/* Tombol aksi: vertikal & full width, responsif */}
- + {saleOrder?.eligibleContinue && ( + + )}
- - {/*
-
- - No. Purchase Order - -

- {saleOrder.purchaseOrderName || '-'} -

-
-
- - Total Invoice - -

- {saleOrder.invoiceCount} Invoice -

-
-
*/} - {/*
-
- Sales -

- {saleOrder.sales} -

-
-
- - Total Harga - -

- {currencyFormat(saleOrder.amountTotal)} -

-
-
*/} ))} @@ -795,7 +671,6 @@ const Transactions = ({ context = '' }) => { @@ -868,6 +743,7 @@ const Transactions = ({ context = '' }) => {
+ {/* ===== DESKTOP ===== */}
@@ -920,29 +796,7 @@ const Transactions = ({ context = '' }) => { )}
-
- -
+
@@ -950,9 +804,7 @@ const Transactions = ({ context = '' }) => { Status
- {/* Container flex: tombol prev - swiper - tombol next */}
- {/* Prev */} - {/* Swiper container scrollable */}
{
- {/* Next */}
+
{listSites?.length > 0 ? ( @@ -1054,6 +905,7 @@ const Transactions = ({ context = '' }) => {
+

Menampilkan {startItem}- @@ -1083,6 +935,7 @@ const Transactions = ({ context = '' }) => { Semua +

{isOpenCalender && (
- {/* Tombol silang di sudut kanan atas */}
+
{!transactions.isLoading && transactions?.data?.saleOrders?.length == 0 && ( @@ -1199,11 +1028,9 @@ const Transactions = ({ context = '' }) => {

{saleOrder.name}

Salesperson:{' '} - { - - {saleOrder.sales} - - } + + {saleOrder.sales} +

@@ -1215,7 +1042,9 @@ const Transactions = ({ context = '' }) => {
+
+
@@ -1250,9 +1079,9 @@ const Transactions = ({ context = '' }) => {
{saleOrder.products .slice(1, 4) - .map((product, index) => ( + .map((product, idx) => ( {product?.name} {

+
+

Total Harga

@@ -1309,16 +1140,22 @@ const Transactions = ({ context = '' }) => { Beli Lagi - + {saleOrder?.eligibleContinue && ( + + )}
@@ -1327,70 +1164,10 @@ const Transactions = ({ context = '' }) => {
)}
- {/* - - - - - - - {auth?.feature?.soApproval && } - - - - - - - {transactions.isLoading && ( - - - - )} - {!transactions.isLoading && - (!transactions?.data?.saleOrders || - transactions?.data?.saleOrders?.length == 0) && ( - - - - )} - {transactions.data?.saleOrders?.map((saleOrder) => ( - - - - - - {auth?.feature?.soApproval && ( - - )} - - - - - ))} - -
No. TransaksiNo. POTanggalCreated BySiteSalespersonTotalStatus
-
- -
-
Tidak ada transaksi
- - {saleOrder.name} - - {saleOrder.purchaseOrderName || '-'}{saleOrder.dateOrder || '-'}{saleOrder.address.customer?.name || '-'}{saleOrder.sitePartner || '-'}{saleOrder.sales} - {currencyFormat(saleOrder.amountTotal)} - -
- -
-
*/} -- cgit v1.2.3