diff options
| -rw-r--r-- | src-migrate/modules/product-detail/components/PriceAction.tsx | 73 | ||||
| -rw-r--r-- | src-migrate/modules/product-detail/components/ProductDetail.tsx | 12 | ||||
| -rw-r--r-- | src-migrate/modules/product-detail/stores/useProductDetail.ts | 6 | ||||
| -rw-r--r-- | src-migrate/pages/shop/cart/cart.module.css | 4 | ||||
| -rw-r--r-- | src-migrate/pages/shop/cart/index.tsx | 41 | ||||
| -rw-r--r-- | src-migrate/types/auth.ts | 4 | ||||
| -rw-r--r-- | src/lib/quotation/components/Quotation.jsx | 62 | ||||
| -rw-r--r-- | src/lib/transaction/api/listSiteApi.js | 10 | ||||
| -rw-r--r-- | src/lib/transaction/components/Transaction.jsx | 84 | ||||
| -rw-r--r-- | src/lib/transaction/components/Transactions.jsx | 328 | ||||
| -rw-r--r-- | src/pages/index.jsx | 27 |
11 files changed, 458 insertions, 193 deletions
diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx index ad04de43..81271f6e 100644 --- a/src-migrate/modules/product-detail/components/PriceAction.tsx +++ b/src-migrate/modules/product-detail/components/PriceAction.tsx @@ -1,15 +1,16 @@ -import style from '../styles/price-action.module.css' +import style from '../styles/price-action.module.css'; -import React, { useEffect } from 'react' -import formatCurrency from '~/libs/formatCurrency' -import { IProductDetail } from '~/types/product' -import { useProductDetail } from '../stores/useProductDetail' -import AddToCart from './AddToCart' -import Link from 'next/link' +import React, { useEffect } from 'react'; +import formatCurrency from '~/libs/formatCurrency'; +import { IProductDetail } from '~/types/product'; +import { useProductDetail } from '../stores/useProductDetail'; +import AddToCart from './AddToCart'; +import Link from 'next/link'; +import { getAuth } from '~/libs/auth'; type Props = { - product: IProductDetail -} + product: IProductDetail; +}; const PriceAction = ({ product }: Props) => { const { @@ -18,8 +19,10 @@ const PriceAction = ({ product }: Props) => { activeVariantId, quantityInput, setQuantityInput, - askAdminUrl - } = useProductDetail() + askAdminUrl, + isApproval, + setIsApproval, + } = useProductDetail(); useEffect(() => { setActive(product.variants[0]) @@ -35,8 +38,13 @@ const PriceAction = ({ product }: Props) => { }, [product, setActive]); + + return ( - <div className='block md:sticky top-[150px] bg-white py-0 md:py-6 z-10' id='price-section'> + <div + className='block md:sticky top-[150px] bg-white py-0 md:py-6 z-10' + id='price-section' + > {!!activePrice && activePrice.price > 0 && ( <> <div className='flex items-end gap-x-2'> @@ -56,8 +64,8 @@ const PriceAction = ({ product }: Props) => { </div> <div className='h-1' /> <div className={style['secondary-text']}> - Termasuk PPN: {' '} - Rp {formatCurrency(Math.round(activePrice.price_discount * 1.11))} + Termasuk PPN: Rp{' '} + {formatCurrency(Math.round(activePrice.price_discount * 1.11))} </div> </> )} @@ -65,7 +73,11 @@ const PriceAction = ({ product }: Props) => { {!!activePrice && activePrice.price === 0 && ( <span> Hubungi kami untuk dapatkan harga terbaik,{' '} - <Link href={askAdminUrl} target='_blank' className={style['contact-us']}> + <Link + href={askAdminUrl} + target='_blank' + className={style['contact-us']} + > klik disini </Link> </span> @@ -74,13 +86,30 @@ const PriceAction = ({ product }: Props) => { <div className='h-4' /> <div className={style['action-wrapper']}> - <label htmlFor="quantity" className='hidden'>Quantity</label> - <input type='number' id='quantity' value={quantityInput} onChange={(e) => setQuantityInput(e.target.value)} className={style['quantity-input']} /> - <AddToCart variantId={activeVariantId} quantity={Number(quantityInput)} /> - <AddToCart source='buy' variantId={activeVariantId} quantity={Number(quantityInput)} /> + <label htmlFor='quantity' className='hidden'> + Quantity + </label> + <input + type='number' + id='quantity' + value={quantityInput} + onChange={(e) => setQuantityInput(e.target.value)} + className={style['quantity-input']} + /> + <AddToCart + variantId={activeVariantId} + quantity={Number(quantityInput)} + /> + {!isApproval && ( + <AddToCart + source='buy' + variantId={activeVariantId} + quantity={Number(quantityInput)} + /> + )} </div> </div> - ) -} + ); +}; -export default PriceAction
\ No newline at end of file +export default PriceAction; diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index bfdf5b43..fad35a7d 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -22,6 +22,7 @@ import PriceAction from './PriceAction' import SimilarBottom from './SimilarBottom' import SimilarSide from './SimilarSide' import VariantList from './VariantList' +import { getAuth } from '~/libs/auth' import { gtagProductDetail } from '@/core/utils/googleTag' @@ -34,7 +35,8 @@ const SELF_HOST = process.env.NEXT_PUBLIC_SELF_HOST const ProductDetail = ({ product }: Props) => { const { isDesktop, isMobile } = useDevice() const router = useRouter() - const { setAskAdminUrl, askAdminUrl, activeVariantId } = useProductDetail() + const auth = getAuth() + const { setAskAdminUrl, askAdminUrl, activeVariantId, setIsApproval, isApproval } = useProductDetail() useEffect(() => { gtagProductDetail(product); @@ -54,6 +56,12 @@ const ProductDetail = ({ product }: Props) => { setAskAdminUrl(createdAskUrl) }, [router.asPath, product.manufacture.name, product.name, setAskAdminUrl]) + useEffect(() => { + if (typeof auth === 'object') { + setIsApproval(auth?.feature?.soApproval); + } + }, []); + return ( <> <div className='md:flex md:flex-wrap'> @@ -121,7 +129,7 @@ const ProductDetail = ({ product }: Props) => { )} <div className='h-4 md:h-10' /> - {!!activeVariantId && <ProductPromoSection productId={activeVariantId} />} + {!!activeVariantId && !isApproval && <ProductPromoSection productId={activeVariantId} />} <div className={style['section-card']}> <h2 className={style['heading']}> diff --git a/src-migrate/modules/product-detail/stores/useProductDetail.ts b/src-migrate/modules/product-detail/stores/useProductDetail.ts index 794f0346..2da8835d 100644 --- a/src-migrate/modules/product-detail/stores/useProductDetail.ts +++ b/src-migrate/modules/product-detail/stores/useProductDetail.ts @@ -6,12 +6,14 @@ type State = { activePrice: IProductVariantDetail['price'] | null; quantityInput: string; askAdminUrl: string; + isApproval : boolean; }; type Action = { setActive: (variant: IProductVariantDetail) => void; setQuantityInput: (value: string) => void; setAskAdminUrl: (url: string) => void; + setIsApproval : (value : boolean) => void; }; export const useProductDetail = create<State & Action>((set, get) => ({ @@ -19,6 +21,7 @@ export const useProductDetail = create<State & Action>((set, get) => ({ activePrice: null, quantityInput: '1', askAdminUrl: '', + isApproval : false, setActive: (variant) => { set({ activeVariantId: variant.id, activePrice: variant.price }); }, @@ -28,4 +31,7 @@ export const useProductDetail = create<State & Action>((set, get) => ({ setAskAdminUrl: (url: string) => { set({ askAdminUrl: url }); }, + setIsApproval : (value : boolean) => { + set({ isApproval : value }) + } })); diff --git a/src-migrate/pages/shop/cart/cart.module.css b/src-migrate/pages/shop/cart/cart.module.css index 98a6ac86..806104be 100644 --- a/src-migrate/pages/shop/cart/cart.module.css +++ b/src-migrate/pages/shop/cart/cart.module.css @@ -29,3 +29,7 @@ .summary-buttons { @apply grid grid-cols-2 gap-x-3 mt-6; } + +.summary-buttons-step-approval { + @apply grid grid-cols-1 gap-y-3 mt-6; +} diff --git a/src-migrate/pages/shop/cart/index.tsx b/src-migrate/pages/shop/cart/index.tsx index 4b4de92b..d89707d2 100644 --- a/src-migrate/pages/shop/cart/index.tsx +++ b/src-migrate/pages/shop/cart/index.tsx @@ -16,13 +16,17 @@ import Image from '~/components/ui/image'; const CartPage = () => { const auth = getAuth(); + const [isStepApproval, setIsStepApproval] = React.useState(false); const { loadCart, cart, summary } = useCartStore(); const useDivvice = useDevice(); useEffect(() => { - if (typeof auth === 'object' && !cart) loadCart(auth.id); + if (typeof auth === 'object' && !cart) { + loadCart(auth.id); + setIsStepApproval(auth?.feature?.soApproval); + } }, [auth, loadCart, cart]); const hasSelectedPromo = useMemo(() => { @@ -106,7 +110,7 @@ const CartPage = () => { <CartSummary {...summary} isLoaded={!!cart} /> )} - <div className={style['summary-buttons']}> + <div className={isStepApproval ? style['summary-buttons-step-approval'] : style['summary-buttons'] }> <Tooltip label={ hasSelectedPromo && @@ -123,23 +127,24 @@ const CartPage = () => { Quotation </Button> </Tooltip> - - <Tooltip - label={clsxm({ - 'Tidak ada item yang dipilih': !hasSelected, - 'Terdapat item yang tidak ada harga': hasSelectNoPrice, - })} - > - <Button - colorScheme='red' - w='full' - isDisabled={!hasSelected || hasSelectNoPrice} - as={Link} - href='/shop/checkout' + {!isStepApproval && ( + <Tooltip + label={clsxm({ + 'Tidak ada item yang dipilih': !hasSelected, + 'Terdapat item yang tidak ada harga': hasSelectNoPrice, + })} > - Checkout - </Button> - </Tooltip> + <Button + colorScheme='red' + w='full' + isDisabled={!hasSelected || hasSelectNoPrice} + as={Link} + href='/shop/checkout' + > + Checkout + </Button> + </Tooltip> + )} </div> </div> </div> diff --git a/src-migrate/types/auth.ts b/src-migrate/types/auth.ts index 02e3623d..e93a475a 100644 --- a/src-migrate/types/auth.ts +++ b/src-migrate/types/auth.ts @@ -15,6 +15,10 @@ export type AuthProps = { company: boolean; pricelist: string | null; token: string; + feature : { + onlyReadyStock : boolean, + soApproval : boolean + } }; export type AuthApiProps = OdooApiRes<AuthProps>; diff --git a/src/lib/quotation/components/Quotation.jsx b/src/lib/quotation/components/Quotation.jsx index 09d55e92..8855c6c4 100644 --- a/src/lib/quotation/components/Quotation.jsx +++ b/src/lib/quotation/components/Quotation.jsx @@ -68,6 +68,8 @@ const Quotation = () => { const [etd, setEtd] = useState(null); const [etdFix, setEtdFix] = useState(null); + const [isApproval, setIsApproval] = useState(false); + const expedisiValidation = useRef(null); const [selectedAddress, setSelectedAddress] = useState({ @@ -86,6 +88,7 @@ const Quotation = () => { }; getAddresses(); + setIsApproval(auth?.feature?.soApproval); }, [auth]); useEffect(() => { @@ -185,6 +188,13 @@ const Quotation = () => { if (etd) setEtdFix(calculateEstimatedArrival(etd)); }, [etd]); + useEffect(() => { + if (isApproval) { + setselectedCarrierId(1); + setselectedExpedisiService('indoteknik'); + } + }, [isApproval]); + // end set up address and carrier useEffect(() => { @@ -235,7 +245,7 @@ const Quotation = () => { const checkout = async () => { // validation checkout - if (selectedExpedisi === 0) { + if (selectedExpedisi === 0 && !isApproval) { setCheckoutValidation(true); if (expedisiValidation.current) { const position = expedisiValidation.current.getBoundingClientRect(); @@ -246,7 +256,7 @@ const Quotation = () => { } return; } - if (selectedCarrier != 1 && biayaKirim == 0) { + if (selectedCarrier != 1 && biayaKirim == 0 && !isApproval) { toast.error('Maaf, layanan tidak tersedia. Mohon pilih expedisi lain.'); return; } @@ -267,7 +277,9 @@ const Quotation = () => { estimated_arrival_days: splitDuration(etd), delivery_service_type: selectedExpedisiService, }; + console.log('data checkout', data); const isSuccess = await checkoutApi({ data }); + console.log('isSuccess', isSuccess); setIsLoading(false); if (isSuccess?.id) { for (const product of products) deleteItemCart({ productId: product.id }); @@ -343,16 +355,21 @@ const Quotation = () => { )} <Divider /> <SectionValidation address={selectedAddress.invoicing} /> - <SectionExpedisi - address={selectedAddress.shipping} - listExpedisi={listExpedisi} - setSelectedExpedisi={setSelectedExpedisi} - checkWeigth={checkWeigth} - checkoutValidation={checkoutValidation} - expedisiValidation={expedisiValidation} - loadingRajaOngkir={loadingRajaOngkir} - /> - <Divider /> + {!isApproval && ( + <> + <SectionExpedisi + address={selectedAddress.shipping} + listExpedisi={listExpedisi} + setSelectedExpedisi={setSelectedExpedisi} + checkWeigth={checkWeigth} + checkoutValidation={checkoutValidation} + expedisiValidation={expedisiValidation} + loadingRajaOngkir={loadingRajaOngkir} + /> + <Divider /> + </> + )} + <SectionListService listserviceExpedisi={listserviceExpedisi} setSelectedServiceType={setSelectedServiceType} @@ -468,15 +485,18 @@ const Quotation = () => { )} <Divider /> <SectionValidation address={selectedAddress.invoicing} /> - <SectionExpedisi - address={selectedAddress.shipping} - listExpedisi={listExpedisi} - setSelectedExpedisi={setSelectedExpedisi} - checkWeigth={checkWeigth} - checkoutValidation={checkoutValidation} - expedisiValidation={expedisiValidation} - loadingRajaOngkir={loadingRajaOngkir} - /> + {!isApproval && ( + <SectionExpedisi + address={selectedAddress.shipping} + listExpedisi={listExpedisi} + setSelectedExpedisi={setSelectedExpedisi} + checkWeigth={checkWeigth} + checkoutValidation={checkoutValidation} + expedisiValidation={expedisiValidation} + loadingRajaOngkir={loadingRajaOngkir} + /> + )} + <Divider /> <SectionListService listserviceExpedisi={listserviceExpedisi} diff --git a/src/lib/transaction/api/listSiteApi.js b/src/lib/transaction/api/listSiteApi.js new file mode 100644 index 00000000..8b7740c5 --- /dev/null +++ b/src/lib/transaction/api/listSiteApi.js @@ -0,0 +1,10 @@ +import odooApi from '@/core/api/odooApi' +import { getAuth } from '@/core/utils/auth' + +const getSite = async () => { + const auth = getAuth() + const dataSite = await odooApi('GET', `/api/v1/partner/${auth?.partnerId}/list/site`) + return dataSite +} + +export default getSite
\ No newline at end of file diff --git a/src/lib/transaction/components/Transaction.jsx b/src/lib/transaction/components/Transaction.jsx index 9962b46e..c6152ca9 100644 --- a/src/lib/transaction/components/Transaction.jsx +++ b/src/lib/transaction/components/Transaction.jsx @@ -50,9 +50,6 @@ const Transaction = ({ id }) => { const [idAWB, setIdAWB] = useState(null); const openUploadPo = () => setUploadPo(true); const closeUploadPo = () => setUploadPo(false); - - - const submitUploadPo = async () => { const file = poFile.current.files[0]; @@ -525,7 +522,7 @@ const Transaction = ({ id }) => { <div>Ketentuan Pembayaran</div> <div>: {transaction?.data?.paymentTerm}</div> - {!auth?.feature?.soApproval && ( + {!auth?.feature?.soApproval ? ( <> <div>Purchase Order</div> <div> @@ -545,6 +542,11 @@ const Transaction = ({ id }) => { </button> </div> </> + ) : ( + <> + <div>Site</div> + <div>: {transaction?.data?.sitePartner}</div> + </> )} </div> </div> @@ -597,7 +599,7 @@ const Transaction = ({ id }) => { <thead> <tr> <th>Nama Produk</th> - <th>Diskon</th> + {/* <th>Diskon</th> */} <th>Jumlah</th> <th>Harga</th> <th>Subtotal</th> @@ -615,39 +617,37 @@ const Transaction = ({ id }) => { )} className='w-[20%] flex-shrink-0' > - - <div className="relative"> - <Image - src={product?.parent?.image} - alt={product?.name} - className='object-contain object-center border border-gray_r-6 h-32 w-full rounded-md' - /> - <div className="absolute top-0 right-4 flex mt-3"> - <div className="gambarB "> - {product.isSni && ( - <ImageNext - src="/images/sni-logo.png" - alt="SNI Logo" - className="w-2 h-4 object-contain object-top sm:h-4" - width={50} - height={50} - /> - )} - </div> - <div className="gambarC "> - {product.isTkdn && ( - <ImageNext - src="/images/TKDN.png" - alt="TKDN" - className="w-5 h-4 object-contain object-top ml-1 sm:h-4" - width={50} - height={50} - /> - )} - </div> - </div> - </div> - + <div className='relative'> + <Image + src={product?.parent?.image} + alt={product?.name} + className='object-contain object-center border border-gray_r-6 h-32 w-full rounded-md' + /> + <div className='absolute top-0 right-4 flex mt-3'> + <div className='gambarB '> + {product.isSni && ( + <ImageNext + src='/images/sni-logo.png' + alt='SNI Logo' + className='w-2 h-4 object-contain object-top sm:h-4' + width={50} + height={50} + /> + )} + </div> + <div className='gambarC '> + {product.isTkdn && ( + <ImageNext + src='/images/TKDN.png' + alt='TKDN' + className='w-5 h-4 object-contain object-top ml-1 sm:h-4' + width={50} + height={50} + /> + )} + </div> + </div> + </div> </Link> <div className='px-2 text-left'> <Link @@ -668,18 +668,18 @@ const Transaction = ({ id }) => { </div> </div> </td> - <td> + {/* <td> {product.price.discountPercentage > 0 ? `${product.price.discountPercentage}%` : ''} - </td> + </td> */} <td>{product.quantity}</td> <td> - {product.price.discountPercentage > 0 && ( + {/* {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> diff --git a/src/lib/transaction/components/Transactions.jsx b/src/lib/transaction/components/Transactions.jsx index be63effd..92bdd276 100644 --- a/src/lib/transaction/components/Transactions.jsx +++ b/src/lib/transaction/components/Transactions.jsx @@ -1,63 +1,163 @@ -import { useRouter } from 'next/router' -import { useState } from 'react' -import { toast } from 'react-hot-toast' -import { EllipsisVerticalIcon, MagnifyingGlassIcon } from '@heroicons/react/24/outline' +import { useRouter } from 'next/router'; +import { useEffect, useState } from 'react'; +import { toast } from 'react-hot-toast'; +import { + EllipsisVerticalIcon, + MagnifyingGlassIcon, +} 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 { + 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'; const Transactions = ({ context = '' }) => { - const router = useRouter() - const { q = '', page = 1 } = router.query + const auth = useAuth(); + const router = useRouter(); + const { q = '', page = 1, site = null } = router.query; - const limit = 15 + const limit = 15; + + const [inputQuery, setInputQuery] = useState(q); + const [toOthers, setToOthers] = useState(null); + const [toCancel, setToCancel] = useState(null); + const [listSites, setListSites] = useState([]); + + const [siteFilter, setSiteFilter] = useState(site); const query = { name: q, offset: (page - 1) * limit, context, - limit - } - const { transactions } = useTransactions({ query }) + limit, + site: + siteFilter || (auth?.webRole === null && auth?.site ? auth.site : null), + }; + + const { transactions } = useTransactions({ query }); - const [inputQuery, setInputQuery] = useState(q) - const [toOthers, setToOthers] = useState(null) - const [toCancel, setToCancel] = useState(null) + const fetchSite = async () => { + const site = await getSite(); + setListSites(site.sites); + }; const submitCancelTransaction = async () => { const isCancelled = await cancelTransactionApi({ - transaction: toCancel - }) + transaction: toCancel, + }); if (isCancelled) { - toast.success('Berhasil batalkan transaksi') - transactions.refetch() + toast.success('Berhasil batalkan transaksi'); + transactions.refetch(); } - setToCancel(null) - } + setToCancel(null); + }; - const pageCount = Math.ceil(transactions?.data?.saleOrderTotal / limit) - let pageQuery = _.omit(query, ['limit', 'offset', 'context']) - pageQuery = _.pickBy(pageQuery, _.identity) - pageQuery = toQuery(pageQuery) + const pageCount = Math.ceil(transactions?.data?.saleOrderTotal / limit); + 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() - router.push(`${router.pathname}?q=${inputQuery}`) - } + 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: 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 getAllData = async () => { + const query = { + name: q, + context, + 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); + }; + + useEffect(() => { + fetchSite(); + }, []); return ( <> <MobileView> @@ -81,17 +181,23 @@ const Transactions = ({ context = '' }) => { </div> )} - {!transactions.isLoading && transactions.data?.saleOrders?.length === 0 && ( - <Alert type='info' className='text-center'> - Tidak ada transaksi - </Alert> - )} + {!transactions.isLoading && + transactions.data?.saleOrders?.length === 0 && ( + <Alert type='info' className='text-center'> + Tidak ada transaksi + </Alert> + )} {transactions.data?.saleOrders?.map((saleOrder, index) => ( - <div className='p-4 shadow border border-gray_r-3 rounded-md' key={index}> + <div + className='p-4 shadow border border-gray_r-3 rounded-md' + key={index} + > <div className='grid grid-cols-2'> <Link href={`${router.pathname}/${saleOrder.id}`}> - <span className='text-caption-2 text-gray_r-11'>No. Transaksi</span> + <span className='text-caption-2 text-gray_r-11'> + No. Transaksi + </span> <h2 className='text-danger-500 mt-1'>{saleOrder.name}</h2> </Link> <div className='flex gap-x-1 justify-end'> @@ -105,13 +211,17 @@ const Transactions = ({ context = '' }) => { <Link href={`${router.pathname}/${saleOrder.id}`}> <div className='grid grid-cols-2 mt-3'> <div> - <span className='text-caption-2 text-gray_r-11'>No. Purchase Order</span> + <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> + <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> @@ -120,10 +230,14 @@ const Transactions = ({ context = '' }) => { <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> + <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> + <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> @@ -140,14 +254,18 @@ const Transactions = ({ context = '' }) => { className='mt-2 mb-2' /> - <BottomPopup title='Lainnya' active={toOthers} close={() => setToOthers(null)}> + <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) + downloadPurchaseOrder(toOthers); + setToOthers(null); }} > Download PO @@ -156,8 +274,8 @@ const Transactions = ({ context = '' }) => { className='text-left disabled:opacity-60' disabled={toOthers?.status != 'draft'} onClick={() => { - downloadQuotation(toOthers) - setToOthers(null) + downloadQuotation(toOthers); + setToOthers(null); }} > Download Quotation @@ -166,8 +284,8 @@ const Transactions = ({ context = '' }) => { className='text-left disabled:opacity-60' disabled={toOthers?.status != 'waiting'} onClick={() => { - setToCancel(toOthers) - setToOthers(null) + setToCancel(toOthers); + setToOthers(null); }} > Batalkan Transaksi @@ -175,7 +293,11 @@ const Transactions = ({ context = '' }) => { </div> </BottomPopup> - <BottomPopup active={toCancel} close={() => setToCancel(null)} title='Batalkan Transaksi'> + <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>? @@ -188,7 +310,11 @@ const Transactions = ({ context = '' }) => { > Ya, Batalkan </button> - <button className='btn-light flex-1' type='button' onClick={() => setToCancel(null)}> + <button + className='btn-light flex-1' + type='button' + onClick={() => setToCancel(null)} + > Batal </button> </div> @@ -205,21 +331,50 @@ const Transactions = ({ context = '' }) => { <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?.saleOrders + ? `(${transactions?.data?.saleOrders.length})` + : ''} </h1> - <form className='flex gap-x-2' 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> + <div className='grid grid-cols-2 gap-2'> + {listSites?.length > 0 ? ( + <select + value={siteFilter} + onChange={handleSiteFilterChange} + className='form-input' + > + <option value=''>Pilih Site</option> + {listSites.map((site) => ( + <option value={site} key={site}> + {site} + </option> + ))} + </select> + ) : (<div></div>)} + + <form className='flex gap-x-1' 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> + </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> @@ -227,6 +382,9 @@ const Transactions = ({ context = '' }) => { <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> @@ -252,13 +410,23 @@ const Transactions = ({ context = '' }) => { {transactions.data?.saleOrders?.map((saleOrder) => ( <tr key={saleOrder.id}> <td> - <Link className='whitespace-nowrap' href={`${router.pathname}/${saleOrder.id}`}>{saleOrder.name}</Link> + <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 className='!text-left'> + {currencyFormat(saleOrder.amountTotal)} + </td> <td> <div className='flex justify-center'> <TransactionStatusBadge status={saleOrder.status} /> @@ -272,14 +440,14 @@ const Transactions = ({ context = '' }) => { <Pagination pageCount={pageCount} currentPage={parseInt(page)} - url={router.pathname + pageQuery} + url={router.pathname + (pageQuery ? `?${pageQuery}` : '')} className='mt-2 mb-2' /> </div> </div> </DesktopView> </> - ) -} + ); +}; -export default Transactions +export default Transactions; diff --git a/src/pages/index.jsx b/src/pages/index.jsx index 3da381b6..8af963fb 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -11,6 +11,8 @@ import { FlashSaleSkeleton } from '@/lib/flashSale/skeleton/FlashSaleSkeleton'; import PreferredBrandSkeleton from '@/lib/home/components/Skeleton/PreferredBrandSkeleton'; import PromotinProgram from '@/lib/promotinProgram/components/HomePage'; import PagePopupIformation from '~/modules/popup-information'; +import useProductDetail from '~/modules/product-detail/stores/useProductDetail'; +import { getAuth } from '~/libs/auth'; const BasicLayout = dynamic(() => import('@/core/components/layouts/BasicLayout') @@ -61,6 +63,8 @@ export default function Home() { const bannerRef = useRef(null); const wrapperRef = useRef(null); + const auth = getAuth(); + const handleOnLoad = () => { wrapperRef.current.style.height = bannerRef.current?.querySelector(':first-child')?.clientHeight + 'px'; @@ -107,8 +111,11 @@ export default function Home() { <div id='flashsale'> <PreferredBrand /> </div> - <ProgramPromotion/> - <FlashSale /> + {!auth?.feature?.soApproval && ( + <> + <ProgramPromotion /> <FlashSale /> + </> + )} <PromotinProgram /> <CategoryHomeId /> <BannerSection /> @@ -130,12 +137,16 @@ export default function Home() { <PreferredBrand /> </div> </DelayRender> - <DelayRender renderAfter={400}> - <ProgramPromotion/> - </DelayRender> - <DelayRender renderAfter={600}> - <FlashSale /> - </DelayRender> + {!auth?.feature?.soApproval && ( + <> + <DelayRender renderAfter={400}> + <ProgramPromotion /> + </DelayRender> + <DelayRender renderAfter={600}> + <FlashSale /> + </DelayRender> + </> + )} <DelayRender renderAfter={600}> <PromotinProgram /> </DelayRender> |
