summaryrefslogtreecommitdiff
path: root/src/lib/transaction
diff options
context:
space:
mode:
authorIT Fixcomart <it@fixcomart.co.id>2023-03-01 09:18:52 +0000
committerIT Fixcomart <it@fixcomart.co.id>2023-03-01 09:18:52 +0000
commita7abbf4ddc70068620e9f44b74dc162ce2e16ee2 (patch)
tree74f66253717515d364ce74bd8275015c1f829cbc /src/lib/transaction
parent90e1edab9b6a8ccc09a49fed3addbec2cbc4e4c3 (diff)
parenta1b9b647a6c4bda1f5db63879639d44543f9557e (diff)
Merged in refactor (pull request #1)
Refactor
Diffstat (limited to 'src/lib/transaction')
-rw-r--r--src/lib/transaction/api/cancelTransactionApi.js13
-rw-r--r--src/lib/transaction/api/checkoutPoApi.js13
-rw-r--r--src/lib/transaction/api/transactionApi.js10
-rw-r--r--src/lib/transaction/api/transactionsApi.js13
-rw-r--r--src/lib/transaction/api/uploadPoApi.js14
-rw-r--r--src/lib/transaction/components/Transaction.jsx352
-rw-r--r--src/lib/transaction/components/TransactionStatusBadge.jsx41
-rw-r--r--src/lib/transaction/components/Transactions.jsx216
-rw-r--r--src/lib/transaction/hooks/useTransaction.js13
-rw-r--r--src/lib/transaction/hooks/useTransactions.js15
-rw-r--r--src/lib/transaction/utils/transactions.js15
11 files changed, 715 insertions, 0 deletions
diff --git a/src/lib/transaction/api/cancelTransactionApi.js b/src/lib/transaction/api/cancelTransactionApi.js
new file mode 100644
index 00000000..1bba2bde
--- /dev/null
+++ b/src/lib/transaction/api/cancelTransactionApi.js
@@ -0,0 +1,13 @@
+import odooApi from '@/core/api/odooApi'
+import { getAuth } from '@/core/utils/auth'
+
+const cancelTransactionApi = async ({ transaction }) => {
+ const auth = getAuth()
+ const dataCancelTransaction = await odooApi(
+ 'POST',
+ `/api/v1/partner/${auth.partnerId}/sale_order/${transaction.id}/cancel`
+ )
+ return dataCancelTransaction
+}
+
+export default cancelTransactionApi
diff --git a/src/lib/transaction/api/checkoutPoApi.js b/src/lib/transaction/api/checkoutPoApi.js
new file mode 100644
index 00000000..04421368
--- /dev/null
+++ b/src/lib/transaction/api/checkoutPoApi.js
@@ -0,0 +1,13 @@
+import odooApi from '@/core/api/odooApi'
+import { getAuth } from '@/core/utils/auth'
+
+const checkoutPoApi = async ({ id }) => {
+ const auth = getAuth()
+ const dataCheckout = await odooApi(
+ 'POST',
+ `/api/v1/partner/${auth?.partnerId}/sale_order/${id}/checkout`
+ )
+ return dataCheckout
+}
+
+export default checkoutPoApi
diff --git a/src/lib/transaction/api/transactionApi.js b/src/lib/transaction/api/transactionApi.js
new file mode 100644
index 00000000..e7c4c23f
--- /dev/null
+++ b/src/lib/transaction/api/transactionApi.js
@@ -0,0 +1,10 @@
+import odooApi from '@/core/api/odooApi'
+import { getAuth } from '@/core/utils/auth'
+
+const transactionApi = async ({ id }) => {
+ const auth = getAuth()
+ const dataTransaction = await odooApi('GET', `/api/v1/partner/${auth.partnerId}/sale_order/${id}`)
+ return dataTransaction
+}
+
+export default transactionApi
diff --git a/src/lib/transaction/api/transactionsApi.js b/src/lib/transaction/api/transactionsApi.js
new file mode 100644
index 00000000..f4e36e6f
--- /dev/null
+++ b/src/lib/transaction/api/transactionsApi.js
@@ -0,0 +1,13 @@
+import odooApi from '@/core/api/odooApi'
+import { getAuth } from '@/core/utils/auth'
+
+const transactionsApi = async ({ query }) => {
+ const auth = getAuth()
+ const dataTransactions = await odooApi(
+ 'GET',
+ `/api/v1/partner/${auth.partnerId}/sale_order?${query}`
+ )
+ return dataTransactions
+}
+
+export default transactionsApi
diff --git a/src/lib/transaction/api/uploadPoApi.js b/src/lib/transaction/api/uploadPoApi.js
new file mode 100644
index 00000000..7feeff66
--- /dev/null
+++ b/src/lib/transaction/api/uploadPoApi.js
@@ -0,0 +1,14 @@
+import odooApi from '@/core/api/odooApi'
+import { getAuth } from '@/core/utils/auth'
+
+const uploadPoApi = async ({ id, data }) => {
+ const auth = getAuth()
+ const dataUploadPo = await odooApi(
+ 'POST',
+ `/api/v1/partner/${auth.partnerId}/sale_order/${id}/upload_po`,
+ data
+ )
+ return dataUploadPo
+}
+
+export default uploadPoApi
diff --git a/src/lib/transaction/components/Transaction.jsx b/src/lib/transaction/components/Transaction.jsx
new file mode 100644
index 00000000..7da33551
--- /dev/null
+++ b/src/lib/transaction/components/Transaction.jsx
@@ -0,0 +1,352 @@
+import Spinner from '@/core/components/elements/Spinner/Spinner'
+import useTransaction from '../hooks/useTransaction'
+import TransactionStatusBadge from './TransactionStatusBadge'
+import Divider from '@/core/components/elements/Divider/Divider'
+import { useRef, useState } from 'react'
+import { downloadPurchaseOrder, downloadQuotation } from '../utils/transactions'
+import BottomPopup from '@/core/components/elements/Popup/BottomPopup'
+import uploadPoApi from '../api/uploadPoApi'
+import { toast } from 'react-hot-toast'
+import getFileBase64 from '@/core/utils/getFileBase64'
+import currencyFormat from '@/core/utils/currencyFormat'
+import VariantGroupCard from '@/lib/variant/components/VariantGroupCard'
+import { ChevronDownIcon, ChevronRightIcon, ChevronUpIcon } from '@heroicons/react/24/outline'
+import Link from '@/core/components/elements/Link/Link'
+import Alert from '@/core/components/elements/Alert/Alert'
+import checkoutPoApi from '../api/checkoutPoApi'
+import cancelTransactionApi from '../api/cancelTransactionApi'
+
+const Transaction = ({ id }) => {
+ const { transaction } = useTransaction({ id })
+
+ const poNumber = useRef('')
+ const poFile = useRef('')
+ const [uploadPo, setUploadPo] = useState(false)
+ const openUploadPo = () => setUploadPo(true)
+ const closeUploadPo = () => setUploadPo(false)
+ const submitUploadPo = async () => {
+ const file = poFile.current.files[0]
+ const name = poNumber.current.value
+ if (typeof file === 'undefined' || !name) {
+ toast.error('Nomor dan Dokumen PO harus diisi', { position: 'bottom-center' })
+ return
+ }
+ if (file.size > 5000000) {
+ toast.error('Maksimal ukuran file adalah 5MB', { position: 'bottom-center' })
+ return
+ }
+ const data = { name, file: await getFileBase64(file) }
+ const isUploaded = await uploadPoApi({ id, data })
+ if (isUploaded) {
+ toast.success('Berhasil upload PO')
+ transaction.refetch()
+ closeUploadPo()
+ return
+ }
+ toast.error('Terjadi kesalahan internal, coba lagi nanti atau hubungi kami')
+ }
+
+ const [cancelTransaction, setCancelTransaction] = useState(false)
+ const openCancelTransaction = () => setCancelTransaction(true)
+ const closeCancelTransaction = () => setCancelTransaction(false)
+ const submitCancelTransaction = async () => {
+ const isCancelled = await cancelTransactionApi({ transaction: transaction.data })
+ if (isCancelled) {
+ toast.success('Berhasil batalkan transaksi')
+ transaction.refetch()
+ }
+ closeCancelTransaction()
+ }
+
+ const checkout = async () => {
+ if (!transaction.data?.purchaseOrderFile) {
+ toast.error('Mohon upload dokumen PO anda sebelum melanjutkan pesanan')
+ return
+ }
+ await checkoutPoApi({ id })
+ toast.success('Berhasil melanjutkan pesanan')
+ transaction.refetch()
+ }
+
+ if (transaction.isLoading) {
+ return (
+ <div className='flex justify-center my-6'>
+ <Spinner className='w-6 text-gray_r-12/50 fill-gray_r-12' />
+ </div>
+ )
+ }
+
+ return (
+ transaction.data?.name && (
+ <>
+ <div className='flex flex-col gap-y-4 p-4'>
+ <DescriptionRow label='Status Transaksi'>
+ <div className='flex justify-end'>
+ <TransactionStatusBadge status={transaction.data?.status} />
+ </div>
+ </DescriptionRow>
+ <DescriptionRow label='No Transaksi'>{transaction.data?.name}</DescriptionRow>
+ <DescriptionRow label='Ketentuan Pembayaran'>
+ {transaction.data?.paymentTerm}
+ </DescriptionRow>
+ <DescriptionRow label='Nama Sales'>{transaction.data?.sales}</DescriptionRow>
+ <DescriptionRow label='Waktu Transaksi'>{transaction.data?.dateOrder}</DescriptionRow>
+ </div>
+
+ <Divider />
+
+ <div className='p-4 flex flex-col gap-y-4'>
+ <DescriptionRow label='Purchase Order'>
+ {transaction.data?.purchaseOrderName || '-'}
+ </DescriptionRow>
+ <div className='flex items-center'>
+ <p className='text-gray_r-11 leading-none'>Dokumen PO</p>
+ <button
+ type='button'
+ className='btn-light py-1.5 px-3 ml-auto'
+ onClick={
+ transaction.data?.purchaseOrderFile
+ ? () => downloadPurchaseOrder(transaction.data)
+ : openUploadPo
+ }
+ >
+ {transaction.data?.purchaseOrderFile ? 'Download' : 'Upload'}
+ </button>
+ </div>
+ </div>
+
+ <Divider />
+
+ <div className='font-medium p-4'>Detail Produk</div>
+
+ <div className='p-4 pt-0 flex flex-col gap-y-3'>
+ <VariantGroupCard
+ variants={transaction.data?.products}
+ buyMore
+ />
+ <div className='flex justify-between mt-3 font-medium'>
+ <p>Total Belanja</p>
+ <p>{currencyFormat(transaction.data?.amountTotal)}</p>
+ </div>
+ </div>
+
+ <Divider />
+
+ <SectionAddress address={transaction.data?.address} />
+
+ <Divider />
+
+ <div className='p-4'>
+ <p className='font-medium'>Invoice</p>
+ <div className='flex flex-col gap-y-3 mt-4'>
+ {transaction.data?.invoices?.map((invoice, index) => (
+ <Link
+ href={`/my/invoice/${invoice.id}`}
+ key={index}
+ >
+ <div className='shadow rounded-md p-4 text-gray_r-12 font-normal flex justify-between'>
+ <div>
+ <p className='mb-2'>{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>
+ ))}
+ {transaction.data?.invoices?.length === 0 && (
+ <Alert
+ type='info'
+ className='text-center'
+ >
+ Belum ada Invoice
+ </Alert>
+ )}
+ </div>
+ </div>
+
+ <Divider />
+
+ <div className='p-4 pt-0'>
+ {transaction.data?.status == 'draft' && (
+ <button
+ className='btn-yellow w-full mt-4'
+ onClick={checkout}
+ >
+ Lanjutkan Transaksi
+ </button>
+ )}
+ <button
+ className='btn-light w-full mt-4'
+ disabled={transaction.data?.status != 'draft'}
+ onClick={downloadQuotation}
+ >
+ Download Quotation
+ </button>
+ {transaction.data?.status != 'draft' && (
+ <button
+ className='btn-light w-full mt-4'
+ disabled={transaction.data?.status != 'waiting'}
+ onClick={openCancelTransaction}
+ >
+ Batalkan Transaksi
+ </button>
+ )}
+ </div>
+
+ <BottomPopup
+ active={cancelTransaction}
+ close={closeCancelTransaction}
+ title='Batalkan Transaksi'
+ >
+ <div className='leading-7 text-gray_r-12/80'>
+ Apakah anda yakin membatalkan transaksi{' '}
+ <span className='underline'>{transaction.data?.name}</span>?
+ </div>
+ <div className='flex mt-6 gap-x-4'>
+ <button
+ className='btn-solid-red flex-1'
+ type='button'
+ onClick={submitCancelTransaction}
+ >
+ Ya, Batalkan
+ </button>
+ <button
+ className='btn-light flex-1'
+ type='button'
+ onClick={closeCancelTransaction}
+ >
+ Batal
+ </button>
+ </div>
+ </BottomPopup>
+
+ <BottomPopup
+ title='Upload PO'
+ close={closeUploadPo}
+ active={uploadPo}
+ >
+ <div>
+ <label>Nomor PO</label>
+ <input
+ type='text'
+ className='form-input mt-3'
+ ref={poNumber}
+ />
+ </div>
+ <div className='mt-4'>
+ <label>Dokumen PO</label>
+ <input
+ type='file'
+ className='form-input mt-3 py-2'
+ ref={poFile}
+ />
+ </div>
+ <div className='grid grid-cols-2 gap-x-3 mt-6'>
+ <button
+ type='button'
+ className='btn-light w-full'
+ onClick={closeUploadPo}
+ >
+ Batal
+ </button>
+ <button
+ type='button'
+ className='btn-solid-red w-full'
+ onClick={submitUploadPo}
+ >
+ Upload
+ </button>
+ </div>
+ </BottomPopup>
+ </>
+ )
+ )
+}
+
+const SectionAddress = ({ address }) => {
+ const [section, setSection] = useState({
+ customer: false,
+ invoice: false,
+ shipping: false
+ })
+ const toggleSection = (name) => {
+ setSection({ ...section, [name]: !section[name] })
+ }
+
+ return (
+ <>
+ <SectionButton
+ label='Detail Pelanggan'
+ active={section.customer}
+ toggle={() => toggleSection('customer')}
+ />
+
+ {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} />}
+ </>
+ )
+}
+
+const SectionButton = ({ label, active, toggle }) => (
+ <button
+ className='p-4 font-medium flex justify-between w-full'
+ onClick={toggle}
+ >
+ <span>{label}</span>
+ {active ? <ChevronUpIcon className='w-5' /> : <ChevronDownIcon className='w-5' />}
+ </button>
+)
+
+const SectionContent = ({ address }) => {
+ let fullAddress = []
+ if (address?.street) fullAddress.push(address.street)
+ if (address?.subDistrict?.name) fullAddress.push(address.subDistrict.name)
+ if (address?.district?.name) fullAddress.push(address.district.name)
+ if (address?.city?.name) fullAddress.push(address.city.name)
+ fullAddress = fullAddress.join(', ')
+
+ return (
+ <div className='flex flex-col gap-y-4 p-4 border-t border-gray_r-6'>
+ <DescriptionRow label='Nama'>{address.name}</DescriptionRow>
+ <DescriptionRow label='Email'>{address.email || '-'}</DescriptionRow>
+ <DescriptionRow label='No Telepon'>{address.mobile || '-'}</DescriptionRow>
+ <DescriptionRow label='Alamat'>{fullAddress}</DescriptionRow>
+ </div>
+ )
+}
+
+const DescriptionRow = ({ children, label }) => (
+ <div className='grid grid-cols-2'>
+ <span className='text-gray_r-11'>{label}</span>
+ <span className='text-right'>{children}</span>
+ </div>
+)
+
+export default Transaction
diff --git a/src/lib/transaction/components/TransactionStatusBadge.jsx b/src/lib/transaction/components/TransactionStatusBadge.jsx
new file mode 100644
index 00000000..7372e4da
--- /dev/null
+++ b/src/lib/transaction/components/TransactionStatusBadge.jsx
@@ -0,0 +1,41 @@
+const TransactionStatusBadge = ({ status }) => {
+ let badgeProps = {
+ className: ['h-fit'],
+ text: ''
+ }
+ switch (status) {
+ case 'cancel':
+ badgeProps.className.push('badge-solid-red')
+ badgeProps.text = 'Pesanan batal'
+ break
+ case 'draft':
+ badgeProps.className.push('badge-red')
+ badgeProps.text = 'Pending quotation'
+ break
+ case 'waiting':
+ badgeProps.className.push('badge-yellow')
+ badgeProps.text = 'Pesanan diterima'
+ break
+ case 'sale':
+ badgeProps.className.push('badge-yellow')
+ badgeProps.text = 'Pesanan diproses'
+ break
+ case 'shipping':
+ badgeProps.className.push('badge-green')
+ badgeProps.text = 'Pesanan dikirim'
+ break
+ case 'partial_shipping':
+ badgeProps.className.push('badge-green')
+ badgeProps.text = 'Dikirim sebagian'
+ break
+ case 'done':
+ badgeProps.className.push('badge-solid-green')
+ badgeProps.text = 'Pesanan selesai'
+ break
+ }
+ badgeProps.className = badgeProps.className.join(' ')
+
+ return <div className={badgeProps.className}>{badgeProps.text}</div>
+}
+
+export default TransactionStatusBadge
diff --git a/src/lib/transaction/components/Transactions.jsx b/src/lib/transaction/components/Transactions.jsx
new file mode 100644
index 00000000..ccbdede2
--- /dev/null
+++ b/src/lib/transaction/components/Transactions.jsx
@@ -0,0 +1,216 @@
+import { useRouter } from 'next/router'
+import { useState } from 'react'
+import { toast } from 'react-hot-toast'
+import { EllipsisVerticalIcon, MagnifyingGlassIcon } from '@heroicons/react/24/outline'
+
+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'
+
+const Transactions = () => {
+ const router = useRouter()
+ const { q = '', page = 1 } = router.query
+
+ const limit = 10
+
+ const query = {
+ name: q,
+ offset: (page - 1) * limit,
+ limit
+ }
+ const { transactions } = useTransactions({ query })
+
+ const [inputQuery, setInputQuery] = useState(q)
+ const [toOthers, setToOthers] = useState(null)
+ const [toCancel, setToCancel] = useState(null)
+
+ 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 / limit)
+ let pageQuery = _.omit(query, ['limit', 'offset'])
+ pageQuery = _.pickBy(pageQuery, _.identity)
+ pageQuery = toQuery(pageQuery)
+
+ const handleSubmit = (e) => {
+ e.preventDefault()
+ router.push(`/my/transactions?q=${inputQuery}`)
+ }
+
+ return (
+ <div className='p-4 flex flex-col gap-y-4'>
+ <form
+ className='flex gap-x-3'
+ onSubmit={handleSubmit}
+ >
+ <input
+ type='text'
+ className='form-input'
+ placeholder='Cari Transaksi...'
+ value={inputQuery}
+ onChange={(e) => setInputQuery(e.target.value)}
+ />
+ <button
+ className='btn-light bg-transparent px-3'
+ type='submit'
+ >
+ <MagnifyingGlassIcon className='w-6' />
+ </button>
+ </form>
+
+ {transactions.isLoading && (
+ <div className='flex justify-center my-4'>
+ <Spinner className='w-6 text-gray_r-12/50 fill-gray_r-12' />
+ </div>
+ )}
+
+ {!transactions.isLoading && transactions.data?.saleOrders?.length === 0 && (
+ <Alert
+ type='info'
+ className='text-center'
+ >
+ Tidak ada data transaksi
+ </Alert>
+ )}
+
+ {transactions.data?.saleOrders?.map((saleOrder, index) => (
+ <div
+ className='p-4 shadow border border-gray_r-3 rounded-md'
+ key={index}
+ >
+ <div className='grid grid-cols-2'>
+ <Link href={`/my/transaction/${saleOrder.id}`}>
+ <span className='text-caption-2 text-gray_r-11'>No. Transaksi</span>
+ <h2 className='text-red_r-11 mt-1'>{saleOrder.name}</h2>
+ </Link>
+ <div className='flex gap-x-1 justify-end'>
+ <TransactionStatusBadge status={saleOrder.status} />
+ <EllipsisVerticalIcon
+ className='w-5 h-5'
+ onClick={() => setToOthers(saleOrder)}
+ />
+ </div>
+ </div>
+ <Link href={`/my/transaction/${saleOrder.id}`}>
+ <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>
+ </div>
+ </Link>
+ </div>
+ ))}
+
+ <Pagination
+ pageCount={pageCount}
+ currentPage={parseInt(page)}
+ url={`/my/transactions${pageQuery}`}
+ className='mt-2 mb-2'
+ />
+
+ <BottomPopup
+ title='Lainnya'
+ active={toOthers}
+ close={() => setToOthers(null)}
+ >
+ <div className='flex flex-col gap-y-4 mt-2'>
+ <button
+ className='text-left disabled:opacity-60'
+ disabled={!toOthers?.purchaseOrderFile}
+ onClick={() => {
+ downloadPurchaseOrder(toOthers)
+ setToOthers(null)
+ }}
+ >
+ 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>
+
+ <BottomPopup
+ active={toCancel}
+ close={() => setToCancel(null)}
+ title='Batalkan Transaksi'
+ >
+ <div className='leading-7 text-gray_r-12/80'>
+ Apakah anda yakin membatalkan transaksi{' '}
+ <span className='underline'>{toCancel?.name}</span>?
+ </div>
+ <div className='flex mt-6 gap-x-4'>
+ <button
+ className='btn-solid-red flex-1'
+ type='button'
+ onClick={submitCancelTransaction}
+ >
+ Ya, Batalkan
+ </button>
+ <button
+ className='btn-light flex-1'
+ type='button'
+ onClick={() => setToCancel(null)}
+ >
+ Batal
+ </button>
+ </div>
+ </BottomPopup>
+ </div>
+ )
+}
+
+export default Transactions
diff --git a/src/lib/transaction/hooks/useTransaction.js b/src/lib/transaction/hooks/useTransaction.js
new file mode 100644
index 00000000..6dda0573
--- /dev/null
+++ b/src/lib/transaction/hooks/useTransaction.js
@@ -0,0 +1,13 @@
+import { useQuery } from 'react-query'
+import transactionApi from '../api/transactionApi'
+
+const useTransaction = ({ id }) => {
+ const fetchTransaction = async () => await transactionApi({ id })
+ const { data, isLoading, refetch } = useQuery(`transaction-${id}`, fetchTransaction)
+
+ return {
+ transaction: { data, isLoading, refetch }
+ }
+}
+
+export default useTransaction
diff --git a/src/lib/transaction/hooks/useTransactions.js b/src/lib/transaction/hooks/useTransactions.js
new file mode 100644
index 00000000..5b40a05a
--- /dev/null
+++ b/src/lib/transaction/hooks/useTransactions.js
@@ -0,0 +1,15 @@
+import { useQuery } from 'react-query'
+import transactionsApi from '../api/transactionsApi'
+import _ from 'lodash-contrib'
+
+const useTransactions = ({ query }) => {
+ const queryString = _.toQuery(query)
+ const fetchTransactions = async () => await transactionsApi({ query: queryString })
+ const { data, isLoading, refetch } = useQuery(`transactions-${queryString}`, fetchTransactions)
+
+ return {
+ transactions: { data, isLoading, refetch }
+ }
+}
+
+export default useTransactions
diff --git a/src/lib/transaction/utils/transactions.js b/src/lib/transaction/utils/transactions.js
new file mode 100644
index 00000000..4c7522be
--- /dev/null
+++ b/src/lib/transaction/utils/transactions.js
@@ -0,0 +1,15 @@
+import { getAuth } from '@/core/utils/auth'
+
+const downloadPurchaseOrder = (transaction) => {
+ const auth = getAuth()
+ const url = `${process.env.ODOO_HOST}/api/v1/partner/${auth.partnerId}/sale_order/${transaction.id}/download_po/${transaction.token}`
+ window.open(url, 'download')
+}
+
+const downloadQuotation = (transaction) => {
+ const auth = getAuth()
+ const url = `${process.env.ODOO_HOST}/api/v1/partner/${auth.partnerId}/sale_order/${transaction.id}/download/${transaction.token}`
+ window.open(url, 'download')
+}
+
+export { downloadPurchaseOrder, downloadQuotation }