diff options
| author | it-fixcomart <it@fixcomart.co.id> | 2025-02-26 16:29:27 +0700 |
|---|---|---|
| committer | it-fixcomart <it@fixcomart.co.id> | 2025-02-26 16:29:27 +0700 |
| commit | cd072c875c334b1140e4f797a37a9e991d53e2b5 (patch) | |
| tree | 24b69fc585e2842e2e761192becf3c30ad221752 /src/lib/transaction/components | |
| parent | 6c4fe7aec3aec0879fd0880cffe02263b4655f29 (diff) | |
<iman> CR repear order
Diffstat (limited to 'src/lib/transaction/components')
| -rw-r--r-- | src/lib/transaction/components/Transactions.jsx | 294 |
1 files changed, 265 insertions, 29 deletions
diff --git a/src/lib/transaction/components/Transactions.jsx b/src/lib/transaction/components/Transactions.jsx index 92bdd276..a8685105 100644 --- a/src/lib/transaction/components/Transactions.jsx +++ b/src/lib/transaction/components/Transactions.jsx @@ -4,9 +4,10 @@ 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, @@ -28,32 +29,31 @@ 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'; const Transactions = ({ context = '' }) => { const auth = useAuth(); const router = useRouter(); - const { q = '', page = 1, site = null } = router.query; - - const limit = 15; + const { q = '', page = 1, site = null, limit = 15 } = router.query; 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 query = { name: q, - offset: (page - 1) * limit, + offset: (pageNew - 1) * limitNew, context, - limit, + limit: limitNew, site: siteFilter || (auth?.webRole === null && auth?.site ? auth.site : null), }; - const { transactions } = useTransactions({ query }); - + console.log('transactions', transactions); const fetchSite = async () => { const site = await getSite(); setListSites(site.sites); @@ -70,7 +70,7 @@ const Transactions = ({ context = '' }) => { setToCancel(null); }; - const pageCount = Math.ceil(transactions?.data?.saleOrderTotal / limit); + const pageCount = Math.ceil(transactions?.data?.saleOrderTotal / limitNew); let pageQuery = _.omit(query, ['limit', 'offset', 'context']); pageQuery = _.pickBy( pageQuery, @@ -137,6 +137,58 @@ const Transactions = ({ context = '' }) => { 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), + 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, @@ -144,7 +196,7 @@ const Transactions = ({ context = '' }) => { site: siteFilter || (auth?.webRole === null && auth?.site ? auth.site : null), }; - const queryString = toQuery(query) + const queryString = toQuery(query); const data = await transactionsApi({ query: queryString }); return data; }; @@ -155,6 +207,25 @@ const Transactions = ({ context = '' }) => { exportToExcel(dataToExport?.saleOrders, siteFilter); }; + const handleDownload = (format) => { + handleExport(format); + setIsOpen(false); + }; + + const handleExport = (format) => { + if (format === 'csv') { + handleExportCSV(); + } else if (format === 'xlsx') { + handleExportExcel(); + } + }; + + const startItem = 1 + (pageNew - 1) * limitNew; + const endItem = Math.min( + limitNew * pageNew, + transactions?.data?.saleOrderTotal + ); + useEffect(() => { fetchSite(); }, []); @@ -328,14 +399,54 @@ const Transactions = ({ context = '' }) => { <Menu /> </div> <div className='w-9/12 p-4 bg-white border border-gray_r-6 rounded'> - <div className='flex mb-6 items-center justify-between'> + <div className='flex mb-6 items-center justify-between '> <h1 className='text-title-sm font-semibold'> Daftar Transaksi{' '} {transactions?.data?.saleOrders - ? `(${transactions?.data?.saleOrders.length})` + ? `(${transactions?.data?.saleOrderTotal})` : ''} </h1> - <div className='grid grid-cols-2 gap-2'> + <div className='relative inline-block text-left'> + <button + onClick={() => setIsOpen(!isOpen)} + type='button' + className='btn-light bg-slate-50 mt-3 w-full gap-2 items-center flex flex-row !text-gray_r-11 px-4 py-3 mb-2' + > + <p>Export</p> + <motion.div + animate={{ rotate: isOpen ? 180 : 0 }} + transition={{ duration: 0.2, ease: 'easeInOut' }} + > + <ChevronDownIcon className='w-5' /> + </motion.div> + </button> + + {isOpen && ( + <motion.div + initial={{ opacity: 0, y: -10 }} + animate={{ opacity: 1, y: 0 }} + exit={{ opacity: 0, y: -10 }} + transition={{ duration: 0.2, ease: 'easeInOut' }} + className='absolute mt-2 w-fit py-1 bg-white border border-gray-300 rounded-md shadow-lg' + > + <button + onClick={() => handleDownload('csv')} + className='block w-full px-4 py-2 text-left hover:bg-gray-200 text-nowrap' + > + Download CSV + </button> + <button + onClick={() => handleDownload('xlsx')} + className='block w-full px-4 py-2 text-left hover:bg-gray-200 text-nowrap' + > + Download XLSX + </button> + </motion.div> + )} + </div> + </div> + <div className='flex flex-row items-center justify-between mb-2'> + <div className='flex flex-col gap-2 pb-2'> {listSites?.length > 0 ? ( <select value={siteFilter} @@ -349,7 +460,9 @@ const Transactions = ({ context = '' }) => { </option> ))} </select> - ) : (<div></div>)} + ) : ( + <div></div> + )} <form className='flex gap-x-1' onSubmit={handleSubmit}> <input @@ -367,14 +480,138 @@ const Transactions = ({ context = '' }) => { </button> </form> </div> + <div className='flex flex-row gap-4 items-center justify-center'> + <p> + Menampilkan {startItem}- + {endItem ? endItem : transactions?.data?.saleOrderTotal} dari{' '} + {transactions?.data?.saleOrderTotal} + </p> + <select + id='limitSelect' + value={limitNew} + onChange={(e) => { + setLimitNew(Number(e.target.value)); + setPageNew(1); + }} + className='border p-2' + > + <option value={10}>10</option> + <option value={15}>15</option> + <option value={20}>20</option> + </select> + </div> + </div> + <div className='flex'> + {!transactions.isLoading && + (!transactions?.data?.saleOrders || + transactions?.data?.saleOrders?.length == 0) && ( + <div className='justify-center p-4'> + <p className='text-gray-500 text-center '> + Tidak Ada Transaksi + </p> + </div> + )} + + {transactions && transactions.data?.saleOrders?.length > 0 && ( + <div className='flex flex-col gap-4 w-full'> + {transactions.data.saleOrders.map((saleOrder, index) => ( + <div + key={index} + className='border p-2 hover:border-red-500 w-full rounded-sm' + > + {/* <Link + href={`/my/quotations/${saleOrder?.id}`} + className='hover:border-red-500 block w-full' + > */} + <div className='flex flex-row justify-between items-center py-2'> + <div className='flex justify-center gap-3'> + <TransactionStatusBadge status={saleOrder.status} /> + <p className='text-red-500'>{saleOrder.name}</p> + <p> + Salesperson:{' '} + { + <span className='font-semibold'> + {saleOrder.sales} + </span> + } + </p> + </div> + <div> + Tanggal Pesanan:{' '} + <span className='font-semibold'> + {saleOrder.dateOrder.split(' ')[0] || '-'} + </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-4/5 flex-col gap-2'> + <div className='flex gap-2'> + <div> + <Image + src={saleOrder.products[0]?.parent?.image} + alt={saleOrder.products[0]?.name} + className='object-contain object-center border border-gray_r-6 h-32 w-full rounded-md' + /> + </div> + <div className='flex flex-col gap-3 justify-start'> + <p className='flex flex-row gap-2'> + <span className='text-sm'>Nomor PO:</span> + <span className='text-sm text-red-500 font-semibold'> + {saleOrder.purchaseOrderName || '-'} + </span> + </p> + <p className='line-clamp-2 leading-6 tracking-wide opacity-90 !text-gray_r-12 font-semibold text-nowrap'> + {saleOrder.products[0]?.parent?.name} + </p> + <p className='opacity-85 !text-gray_r-12'> + {saleOrder.products[0]?.quantity} x{' '} + {currencyFormat( + saleOrder.products[0]?.price?.priceDiscount + )} + </p> + <div className='flex flex-row justify-start items-center'> + {saleOrder.products?.length > 1 && ( + <div className='flex flex-row gap-1 justify-start items-center'> + {saleOrder.products + .slice(1) + .map((product, index) => ( + <Image + key={index} // Tambahkan key untuk setiap elemen dalam map() + src={product?.parent?.image} + alt={product?.name} + className='object-contain object-center border border-gray_r-6 h-8 w-8 rounded-md' + /> + ))} + <Link + href={`/my/quotations/${saleOrder?.id}`} + className='text-sm text-red-500 text-nowrap' + > + Lihat semua produk + </Link> + </div> + )} + </div> + </div> + </div> + <div className='flex flex-row w-full text-nowrap gap-2'> + <span className='text-sm'> + pesanan dibuat oleh: + </span> + <p className='text-sm font-semibold'> + {saleOrder.address.customer?.name || '-'} + </p> + </div> + </div> + <div className='w-[1px] h-24 bg-gray-300'></div> + <div className='w-1/5'>Total harga</div> + </div> + {/* </Link> */} + </div> + ))} + </div> + )} </div> - <button - onClick={handleExportExcel} - type='button' - className='btn-solid-red px-3 py-2 mr-auto mb-2' - > - <span>Download</span> - </button> <table className='table-data'> <thead> <tr> @@ -382,9 +619,7 @@ const Transactions = ({ context = '' }) => { <th>No. PO</th> <th>Tanggal</th> <th>Created By</th> - {auth?.feature?.soApproval && ( - <th>Site</th> - )} + {auth?.feature?.soApproval && <th>Site</th>} <th className='!text-left'>Salesperson</th> <th className='!text-left'>Total</th> <th>Status</th> @@ -439,8 +674,9 @@ const Transactions = ({ context = '' }) => { <Pagination pageCount={pageCount} - currentPage={parseInt(page)} - url={router.pathname + (pageQuery ? `?${pageQuery}` : '')} + currentPage={parseInt(pageNew)} + // url={router.pathname + (pageQuery ? `?${pageQuery}` : '')} + url={`/my/transactions?${toQuery(_.omit(query, ['page']))}`} className='mt-2 mb-2' /> </div> |
