import { useRouter } from 'next/router'; 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 { downloadPurchaseOrder, downloadQuotation, } from '../utils/transactions'; import useTransactions from '../hooks/useTransactions'; import currencyFormat from '@/core/utils/currencyFormat'; import cancelTransactionApi from '../api/cancelTransactionApi'; import TransactionStatusBadge from './TransactionStatusBadge'; import Spinner from '@/core/components/elements/Spinner/Spinner'; import Link from '@/core/components/elements/Link/Link'; import BottomPopup from '@/core/components/elements/Popup/BottomPopup'; import Pagination from '@/core/components/elements/Pagination/Pagination'; import { toQuery } from 'lodash-contrib'; import _ from 'lodash'; import Alert from '@/core/components/elements/Alert/Alert'; import MobileView from '@/core/components/views/MobileView'; import DesktopView from '@/core/components/views/DesktopView'; import Menu from '@/lib/auth/components/Menu'; import * as XLSX from 'xlsx'; import getSite from '../api/listSiteApi'; import transactionsApi from '../api/transactionsApi'; import { motion } from 'framer-motion'; import Image from '@/core/components/elements/Image/Image'; import { upsertUserCart } from '~/services/cart'; import { useProductCartContext } from '@/contexts/ProductCartContext'; import { Swiper, SwiperSlide } from 'swiper/react'; 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'; const Transactions = ({ context = '' }) => { const auth = useAuth(); const router = useRouter(); const swiperRef = useRef(null); const { q = '', page = 1, site = null, limit = 15, status = 'all', sort = 'none', startDate = null, endDate = new Date(), } = router.query; const { productCart, setRefreshCart, setProductCart, refreshCart, isLoading, setIsloading, } = useProductCartContext(); const [inputQuery, setInputQuery] = useState(q); const [toOthers, setToOthers] = useState(null); const [toCancel, setToCancel] = useState(null); const [listSites, setListSites] = useState([]); const [isOpen, setIsOpen] = useState(false); 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 parseDate = (date) => { if (!date || date === 'null') return null; if (date instanceof Date) return date; const [day, month, year] = date.split('/').map(Number); return new Date(year, month - 1, day); }; const [state, setState] = useState([ { startDate: startDate != null || 'null' ? parseDate(startDate) : null, // Gunakan `parseDate` endDate: startDate == null ? endDate : parseDate(endDate), key: 'selection', }, ]); const query = { name: q, offset: (pageNew - 1) * limitNew, context: contextNew, limit: limitNew, status: statusNew, sort: sortNew, startDate: state[0].startDate ? state[0].startDate.toLocaleDateString('id-ID') : null, endDate: state[0]?.endDate?.toLocaleDateString('id-ID'), site: siteFilter || (auth?.webRole === null && auth?.site ? auth.site : null), }; const statuses = [ { id: 'all', label: 'Semua' }, { id: 'quotation', label: 'Pending Quotation' }, { id: 'diproses', label: 'Pesanan Diproses' }, { id: 'dikemas', label: 'Pesanan Dikemas' }, { id: 'partial', label: 'Dikirim Sebagian' }, { id: 'dikirim', label: 'Pesanan Dikirim' }, { id: 'selesai', label: 'Pesanan Selesai' }, { id: 'cancel', label: 'Pesanan Dibatalkan' }, ]; const contextLabelMap = { draft: 'Pending Quotation', waiting: 'Pesanan Diproses', sale: 'Pesanan Dikemas', partial_shipping: 'Dikirim Sebagian', shipping: 'Pesanan Dikirim', done: 'Pesanan Selesai', cancel: 'Pesanan Dibatalkan', }; const sortes = [ { id: 'none', label: 'Urutkan' }, { 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, }); if (isCancelled) { toast.success('Berhasil batalkan transaksi'); transactions.refetch(); } 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') ); 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, }); }; const handleSiteFilterChange = (e) => { setSiteFilter(e.target.value); const queryParams = {}; if (inputQuery) queryParams.q = inputQuery; if (e.target.value) queryParams.site = e.target.value; router.push({ pathname: router.pathname, query: queryParams, }); }; const exportToExcel = (data, siteFilter) => { const fieldsToExport = [ 'No. Transaksi', 'No. PO', 'Tanggal', 'Created By', 'Salesperson', 'Total', 'Status', ]; const rowsToExport = []; data.forEach((saleOrder) => { const row = { 'No. Transaksi': saleOrder.name, 'No. PO': saleOrder.purchaseOrderName || '-', Tanggal: saleOrder.dateOrder || '-', 'Created By': saleOrder.address.customer?.name || '-', Salesperson: saleOrder.sales, Total: currencyFormat(saleOrder.amountTotal), Status: contextLabelMap[saleOrder.status] || saleOrder.status, }; if (siteFilter) { row['Site'] = siteFilter; } rowsToExport.push(row); }); const worksheet = XLSX.utils.json_to_sheet(rowsToExport, { header: fieldsToExport, }); const workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1'); XLSX.writeFile(workbook, 'transactions.xlsx'); }; const handleExportCSV = async () => { const dataToExport = await getAllData(); exportToCSV(dataToExport?.saleOrders, siteFilter); }; const exportToCSV = (data, siteFilter) => { const fieldsToExport = [ 'No. Transaksi', 'No. PO', 'Tanggal', 'Created By', 'Salesperson', '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 csvContent = 'data:text/csv;charset=utf-8,' + [fieldsToExport.join(','), ...rowsToExport].join('\n'); const encodedUri = encodeURI(csvContent); const link = document.createElement('a'); link.setAttribute('href', encodedUri); link.setAttribute('download', 'transactions.csv'); document.body.appendChild(link); link.click(); 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); 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) ) { setIsOpenCalender(false); } }; document.addEventListener("mousedown", handleClickOutside); return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, []); const startItem = 1 + (pageNew - 1) * limitNew; const endItem = Math.min( limitNew * pageNew, transactions?.data?.saleOrderTotal ); useEffect(() => { fetchSite(); }, []); const handleBuyBack = async (products) => { try { // setStatus('loading'); console.log("Products to add:", products); const results = await Promise.all( products.map((product) => upsertUserCart({ userId: auth.id, type: 'product', id: product.id, qty: product.quantity, selected: true, source: 'add_to_cart', qtyAppend: true, }).catch(error => { return { 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`); setRefreshCart(true); router.push('/shop/cart'); } return; } // All operations succeeded setRefreshCart(true); toast.success('Semua produk berhasil ditambahkan ke keranjang belanja'); 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; } const data = await fetchSite(status, 1); if (status === 'all') { setCachedAllData(data); } setCurrentData(data); }; useEffect(() => { setCachedAllData([]); }, []); const handleReset = () => { setState([ { startDate: null, endDate: new Date(), key: 'selection', }, ]); setIsOpenCalender(false); router.push(`${router.pathname}`); }; const formatDate = (dateString) => { const months = [ 'Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember', ]; const [day, month, year] = dateString.split('/'); return `${day} ${months[parseInt(month, 10) - 1]} ${year}`; }; return ( <>
{isOpenCalender && (
{/* Tombol silang di sudut kanan atas */} setState([item.selection])} showSelectionPreview={false} maxDate={new Date()} moveRangeOnFirstSelection={false} months={1} ranges={state} className='w-full' />
)}
{/*
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 && (
)} {!transactions.isLoading && transactions.data?.saleOrders?.length === 0 && ( Tidak ada transaksi )} {transactions.data?.saleOrders?.map((saleOrder, index) => (

{saleOrder.name}

{formatDate(saleOrder.dateOrder.split(' ')[0]) || '-'}
setToOthers(saleOrder)} />
{saleOrder.products[0]?.name}

Nomor PO: {saleOrder.purchaseOrderName || '-'}

{saleOrder.products[0]?.parent?.name}

{saleOrder.products[0]?.quantity} x{' '} {currencyFormat( saleOrder.products[0]?.price?.priceDiscount )}

{saleOrder.products?.length > 1 && (
{saleOrder.products .slice(1, 4) .map((product, index) => ( {product?.name} ))} {saleOrder.products.length > 4 ? ( +{saleOrder.products.length - 4} lihat semua produk ) : ( Lihat semua produk )}
)}
Pesanan dibuat oleh:

{saleOrder.address.customer?.name || '-'}

Total Harga

{currencyFormat(saleOrder.amountTotal)}

{/*
No. Purchase Order

{saleOrder.purchaseOrderName || '-'}

Total Invoice

{saleOrder.invoiceCount} Invoice

*/} {/*
Sales

{saleOrder.sales}

Total Harga

{currencyFormat(saleOrder.amountTotal)}

*/}
))} setToOthers(null)} >
setToCancel(null)} title='Batalkan Transaksi' >
Apakah anda yakin membatalkan transaksi{' '} {toCancel?.name}?

Daftar Transaksi{' '} {transactions?.data?.saleOrders ? `(${transactions?.data?.saleOrderTotal})` : ''}

{isOpen && ( )}
Status
{/* Container flex: tombol prev - swiper - tombol next */}
{/* Prev */} {/* Swiper container scrollable */}
{statuses.map((status) => ( ))}
{/* Next */}
{listSites?.length > 0 ? ( ) : (
)}
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}

{isOpenCalender && (
{/* Tombol silang di sudut kanan atas */} setState([item.selection])} showSelectionPreview={false} maxDate={new Date()} moveRangeOnFirstSelection={false} months={1} ranges={state} className='w-full' />
)}
{!transactions.isLoading && transactions?.data?.saleOrders?.length == 0 && (

Tidak Ada Transaksi

)} {transactions.isLoading && (
)} {!transactions.isLoading && transactions && transactions.data?.saleOrders?.length > 0 && (
{transactions.data.saleOrders.map((saleOrder, index) => (

{saleOrder.name}

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

Tanggal Pesanan:{' '} {formatDate( saleOrder.dateOrder.split(' ')[0] ) || '-'}

{saleOrder.products[0]?.name}

Nomor PO: {saleOrder.purchaseOrderName || '-'}

{saleOrder.products[0]?.parent?.name}

{saleOrder.products[0]?.quantity} x{' '} {currencyFormat( saleOrder.products[0]?.price ?.priceDiscount )}

{saleOrder.products?.length > 1 && (
{saleOrder.products .slice(1, 4) .map((product, index) => ( {product?.name} ))} {saleOrder.products.length > 4 ? ( +{saleOrder.products.length - 4} lihat semua produk ) : ( Lihat semua produk )}
)}
Pesanan dibuat oleh:

{saleOrder.address.customer?.name || '-'}

Total Harga

{currencyFormat(saleOrder.amountTotal)}

))}
)}
{/* {auth?.feature?.soApproval && } {transactions.isLoading && ( )} {!transactions.isLoading && (!transactions?.data?.saleOrders || transactions?.data?.saleOrders?.length == 0) && ( )} {transactions.data?.saleOrders?.map((saleOrder) => ( {auth?.feature?.soApproval && ( )} ))}
No. Transaksi No. PO Tanggal Created BySiteSalesperson Total Status
Tidak ada transaksi
{saleOrder.name} {saleOrder.purchaseOrderName || '-'} {saleOrder.dateOrder || '-'} {saleOrder.address.customer?.name || '-'}{saleOrder.sitePartner || '-'}{saleOrder.sales} {currencyFormat(saleOrder.amountTotal)}
*/}
); }; export default Transactions;