diff options
| author | it-fixcomart <it@fixcomart.co.id> | 2024-11-29 10:37:21 +0700 |
|---|---|---|
| committer | it-fixcomart <it@fixcomart.co.id> | 2024-11-29 10:37:21 +0700 |
| commit | c996c4e0631aa3d51acc83f4cc23418e91f3158f (patch) | |
| tree | 63d31733edd048ea50780d966ff0a1d5a4fa4866 /src | |
| parent | f7a13c357a6d44f9fa6e0c034292ab782249731e (diff) | |
| parent | 37cd18a88f0f104e81c1b6f33f9bab7b2a0b2dfb (diff) | |
Merge branch 'new-release' into CR/website-improvement
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/flashSale/components/FlashSale.jsx | 36 | ||||
| -rw-r--r-- | src/lib/product/components/Product/ProductMobileVariant.jsx | 154 | ||||
| -rw-r--r-- | src/lib/transaction/api/checkoutPoApi.js | 19 | ||||
| -rw-r--r-- | src/lib/transaction/components/Transaction.jsx | 119 | ||||
| -rw-r--r-- | src/lib/treckingAwb/component/Manifest.jsx | 123 | ||||
| -rw-r--r-- | src/pages/api/flashsale-header.js | 40 | ||||
| -rw-r--r-- | src/pages/api/page-content.js | 1 | ||||
| -rw-r--r-- | src/pages/api/search-flashsale.js | 45 | ||||
| -rw-r--r-- | src/pages/api/shop/product-detail.js | 2 | ||||
| -rw-r--r-- | src/pages/api/shop/search.js | 9 | ||||
| -rw-r--r-- | src/pages/index.jsx | 237 | ||||
| -rw-r--r-- | src/utils/solrMapping.js | 5 |
12 files changed, 495 insertions, 295 deletions
diff --git a/src/lib/flashSale/components/FlashSale.jsx b/src/lib/flashSale/components/FlashSale.jsx index 8be1d7a6..6d90cad7 100644 --- a/src/lib/flashSale/components/FlashSale.jsx +++ b/src/lib/flashSale/components/FlashSale.jsx @@ -2,10 +2,8 @@ import Image from 'next/image'; import { useEffect, useState } from 'react'; import CountDown from '@/core/components/elements/CountDown/CountDown'; -import productSearchApi from '@/lib/product/api/productSearchApi'; import ProductSlider from '@/lib/product/components/ProductSlider'; -import flashSaleApi from '../api/flashSaleApi'; import { FlashSaleSkeleton } from '../skeleton/FlashSaleSkeleton'; const FlashSale = () => { @@ -14,10 +12,14 @@ const FlashSale = () => { useEffect(() => { const loadFlashSales = async () => { - const dataFlashSales = await flashSaleApi(); - setFlashSales(dataFlashSales); + const res = await fetch('/api/flashsale-header'); + const { data } = await res.json(); + if (data) { + setFlashSales(data); + } setIsLoading(false); }; + loadFlashSales(); }, []); @@ -53,7 +55,10 @@ const FlashSale = () => { height={48} className='w-full rounded mb-4 block sm:hidden' /> - <FlashSaleProduct flashSaleId={flashSale.pricelistId} /> + <FlashSaleProduct + flashSaleId={flashSale.pricelistId} + duration={flashSale.duration} + /> </div> </div> ))} @@ -63,19 +68,24 @@ const FlashSale = () => { ); }; -const FlashSaleProduct = ({ flashSaleId }) => { +const FlashSaleProduct = ({ flashSaleId, duration }) => { const [products, setProducts] = useState(null); - useEffect(() => { + const data_search = new URLSearchParams({ + query: `fq=flashsale_id_i:${flashSaleId}&fq=flashsale_price_f:[1 TO *]&limit=500&orderBy=flashsale-price-asc&source=similar`, + operation: 'AND', + duration: `${duration}`, + }); const loadProducts = async () => { - const dataProducts = await productSearchApi({ - query: `fq=flashsale_id_i:${flashSaleId}&fq=flashsale_price_f:[1 TO *]&limit=500&orderBy=flashsale-price-asc&source=similar`, - operation: 'AND', - }); - setProducts(dataProducts.response); + const res = await fetch( + `/api/search-flashsale?${data_search.toString()}` + ); + const { data } = await res.json(); + setProducts(data.response); }; + loadProducts(); - }, [flashSaleId]); + }, []); return <ProductSlider products={products} />; }; diff --git a/src/lib/product/components/Product/ProductMobileVariant.jsx b/src/lib/product/components/Product/ProductMobileVariant.jsx index b87bcbc8..de5c3f10 100644 --- a/src/lib/product/components/Product/ProductMobileVariant.jsx +++ b/src/lib/product/components/Product/ProductMobileVariant.jsx @@ -168,44 +168,14 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => { return ( <MobileView> - <Image - src={product.image + '?variant=True'} - alt={product.name} - className='h-72 object-contain object-center w-full border-b border-gray_r-4' - /> - - <div className='p-4'> - <div className='flex items-end mb-2'> - {product.manufacture?.name ? ( - <Link - href={createSlug( - '/shop/brands/', - product.manufacture?.name, - product.manufacture?.id - )} - > - {product.manufacture?.name} - </Link> - ) : ( - <div>-</div> - )} - <button type='button' className='ml-auto' onClick={toggleWishlist}> - {wishlist.data?.productTotal > 0 ? ( - <HeartIcon className='w-6 fill-danger-500 text-danger-500' /> - ) : ( - <HeartIcon className='w-6' /> - )} - </button> - </div> - <h1 className='font-medium text-h-lg leading-8 md:text-title-md md:leading-10 mb-3'> - {activeVariant?.name} - </h1> - + <div + className={`px-4 block md:sticky md:top-[150px] md:py-6 fixed bottom-0 left-0 right-0 bg-white p-2 z-10 pb-6 pt-6 rounded-lg shadow-[rgba(0,0,4,0.1)_0px_-4px_4px_0px] `} + > {activeVariant.isFlashSale && activeVariant?.price?.discountPercentage > 0 ? ( <> <div className='flex gap-x-1 items-center'> - <div className='badge-solid-red'> + <div className='bg-danger-500 px-2 py-1.5 rounded text-white text-caption-2'> {activeVariant?.price?.discountPercentage}% </div> <div className='text-gray_r-11 line-through text-caption-1'> @@ -223,7 +193,7 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => { </div> </> ) : ( - <h3 className='text-danger-500 font-semibold mt-1'> + <div className='text-danger-500 font-semibold mt-1 text-3xl'> {activeVariant?.price?.price > 0 ? ( <> {currencyFormat(activeVariant?.price?.price)} @@ -253,54 +223,84 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => { </a> </span> )} - </h3> + </div> )} + <div className=''> + <div className='mt-4 mb-2'>Jumlah</div> + <div className='flex gap-x-3'> + <div className='w-2/12'> + <input + name='quantity' + type='number' + className='form-input' + value={quantity} + onChange={(e) => setQuantity(e.target.value)} + /> + </div> + <button + type='button' + className='btn-yellow flex-1' + onClick={handleClickCart} + > + Keranjang + </button> + <button + type='button' + className='btn-solid-red flex-1' + onClick={handleClickBuy} + > + Beli + </button> + </div> + <Button + onClick={() => handleButton(product.id)} + color={'red'} + colorScheme='white' + className='w-full border-2 p-2 gap-1 mt-2 hover:bg-slate-100 flex items-center' + > + <ImageNext + src='/images/writing.png' + alt='penawaran instan' + className='' + width={25} + height={25} + /> + Penawaran Harga Instan + </Button> + </div> </div> - - <Divider /> + <Image + src={product.image + '?variant=True'} + alt={product.name} + className='h-72 object-contain object-center w-full border-b border-gray_r-4' + /> <div className='p-4'> - <div className='mt-4 mb-2'>Jumlah</div> - <div className='flex gap-x-3'> - <div className='w-2/12'> - <input - name='quantity' - type='number' - className='form-input' - value={quantity} - onChange={(e) => setQuantity(e.target.value)} - /> - </div> - <button - type='button' - className='btn-yellow flex-1' - onClick={handleClickCart} - > - Keranjang - </button> - <button - type='button' - className='btn-solid-red flex-1' - onClick={handleClickBuy} - > - Beli + <div className='flex items-end mb-2'> + {product.manufacture?.name ? ( + <Link + href={createSlug( + '/shop/brands/', + product.manufacture?.name, + product.manufacture?.id + )} + > + {product.manufacture?.name} + </Link> + ) : ( + <div>-</div> + )} + <button type='button' className='ml-auto' onClick={toggleWishlist}> + {wishlist.data?.productTotal > 0 ? ( + <HeartIcon className='w-6 fill-danger-500 text-danger-500' /> + ) : ( + <HeartIcon className='w-6' /> + )} </button> </div> - <Button - onClick={() => handleButton(product.id)} - color={'red'} - colorScheme='white' - className='w-full border-2 p-2 gap-1 mt-2 hover:bg-slate-100 flex items-center' - > - <ImageNext - src='/images/writing.png' - alt='penawaran instan' - className='' - width={25} - height={25} - /> - Penawaran Harga Instan - </Button> + <h1 className='font-medium text-h-lg leading-8 md:text-title-md md:leading-10 mb-3'> + {activeVariant?.name} + </h1> </div> <Divider /> diff --git a/src/lib/transaction/api/checkoutPoApi.js b/src/lib/transaction/api/checkoutPoApi.js index 04421368..3376e773 100644 --- a/src/lib/transaction/api/checkoutPoApi.js +++ b/src/lib/transaction/api/checkoutPoApi.js @@ -1,13 +1,14 @@ -import odooApi from '@/core/api/odooApi' -import { getAuth } from '@/core/utils/auth' +import odooApi from '@/core/api/odooApi'; +import { getAuth } from '@/core/utils/auth'; -const checkoutPoApi = async ({ id }) => { - const auth = getAuth() +const checkoutPoApi = async ({ id, status }) => { + const auth = getAuth(); const dataCheckout = await odooApi( 'POST', - `/api/v1/partner/${auth?.partnerId}/sale_order/${id}/checkout` - ) - return dataCheckout -} + `/api/v1/partner/${auth?.partnerId}/sale_order/${id}/checkout`, + { status } + ); + return dataCheckout; +}; -export default checkoutPoApi +export default checkoutPoApi; diff --git a/src/lib/transaction/components/Transaction.jsx b/src/lib/transaction/components/Transaction.jsx index 93887f4d..f5dc507a 100644 --- a/src/lib/transaction/components/Transaction.jsx +++ b/src/lib/transaction/components/Transaction.jsx @@ -38,7 +38,9 @@ import aprpoveApi from '../api/approveApi'; import rejectApi from '../api/rejectApi'; import rejectProductApi from '../api/rejectProductApi'; import { useRouter } from 'next/router'; - +import { gtagPurchase } from '@/core/utils/googleTag'; +import { deleteItemCart } from '@/core/utils/cart'; +import axios from 'axios'; const Transaction = ({ id }) => { const router = useRouter(); const [isModalOpen, setIsModalOpen] = useState(false); @@ -47,7 +49,7 @@ const Transaction = ({ id }) => { const auth = useAuth(); const { transaction } = useTransaction({ id }); const statusApprovalWeb = transaction.data?.approvalStep; - + const [isLoading, setIsLoading] = useState(false); const { queryAirwayBill } = useAirwayBill({ orderId: id }); const [airwayBillPopup, setAirwayBillPopup] = useState(null); @@ -83,8 +85,26 @@ const Transaction = ({ id }) => { }; const [cancelTransaction, setCancelTransaction] = useState(false); + const [continueNoPo, setContinueNoPo] = useState(false); + const [continueTransaction, setContinueTransaction] = useState(false); const openCancelTransaction = () => setCancelTransaction(true); + const openContinueTransaction = () => { + if (auth.partnerTempo) { + checkout(); + } else { + if (!transaction.data?.purchaseOrderFile) { + setContinueTransaction(true); + } else { + checkoutNoPO(); + } + } + }; + // const ContinueTransaction = () => { + // setContinueNoPo(true); + // checkoutNoPO(); + // }; const closeCancelTransaction = () => setCancelTransaction(false); + const closeContinueTransaction = () => setContinueTransaction(false); const [rejectTransaction, setRejectTransaction] = useState(false); @@ -100,15 +120,70 @@ const Transaction = ({ id }) => { } closeCancelTransaction(); }; - const checkout = async () => { if (!transaction.data?.purchaseOrderFile) { toast.error('Mohon upload dokumen PO anda sebelum melanjutkan pesanan'); return; } - await checkoutPoApi({ id }); + await checkoutPoApi({ id, status: true }); + toast.success('Berhasil melanjutkan pesanan'); + transaction.refetch(); + }; + + const checkoutNoPO = async () => { + setIsLoading(true); + gtagPurchase( + transaction.data.products, + transaction.data.deliveryAmount, + transaction.data.name + ); + + gtag('event', 'conversion', { + send_to: 'AW-954540379/nDymCL3BhaQYENvClMcD', + value: + transaction.data?.amountTotal + + Math.round(parseInt(transaction.data.deliveryAmount * 1.1) / 1000) * + 1000, + currency: 'IDR', + transaction_id: transaction.data.id, + }); + + for (const product of transaction.data.products) + deleteItemCart({ productId: product.id }); + if (transaction.data?.amountTotal > 0) { + const payment = await axios.post( + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/midtrans-payment?transactionId=${transaction.data.id}` + ); + setIsLoading(false); + window.location.href = payment.data.redirectUrl; + } else { + window.location.href = `${ + process.env.NEXT_PUBLIC_SELF_HOST + }/shop/checkout/success?order_id=${transaction.data.name.replace( + /\//g, + '-' + )}`; + } toast.success('Berhasil melanjutkan pesanan'); transaction.refetch(); + + /* const midtrans = async () => { + for (const product of products) deleteItemCart({ productId: product.id }); + if (grandTotal > 0) { + const payment = await axios.post( + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/midtrans-payment?transactionId=${isCheckouted.id}` + ); + setIsLoading(false); + window.location.href = payment.data.redirectUrl; + } else { + window.location.href = `${ + process.env.NEXT_PUBLIC_SELF_HOST + }/shop/checkout/success?order_id=${isCheckouted.name.replace( + /\//g, + '-' + )}`; + } + };*/ }; const handleApproval = async () => { @@ -207,6 +282,32 @@ const Transaction = ({ id }) => { transaction.data?.name && ( <> <BottomPopup + active={continueTransaction} + close={closeContinueTransaction} + title='Lanjutkan Transaksi' + > + <div className='leading-7 text-gray_r-12/80'> + Apakah anda yakin melanjutkan tanpa upload PO?{' '} + <span className='underline'>{transaction.data?.name}</span>? + </div> + <div className='flex justify-end mt-6 gap-x-4'> + <button + className='btn-solid-red w-full md:w-fit' + type='button' + onClick={checkoutNoPO} + > + Ya, Lanjutkan + </button> + <button + className='btn-light w-full md:w-fit' + type='button' + onClick={closeContinueTransaction} + > + Batal + </button> + </div> + </BottomPopup> + <BottomPopup active={cancelTransaction} close={closeCancelTransaction} title='Batalkan Transaksi' @@ -468,7 +569,10 @@ const Transaction = ({ id }) => { )} {transaction.data?.status == 'draft' && !auth?.feature?.soApproval && ( - <button className='btn-yellow w-full mt-4' onClick={checkout}> + <button + className='btn-yellow w-full mt-4' + onClick={openContinueTransaction} + > Lanjutkan Transaksi </button> )} @@ -562,7 +666,10 @@ const Transaction = ({ id }) => { )} {transaction.data?.status == 'draft' && !auth?.feature.soApproval && ( - <button className='btn-yellow' onClick={checkout}> + <button + className='btn-yellow' + onClick={openContinueTransaction} + > Lanjutkan Transaksi </button> )} diff --git a/src/lib/treckingAwb/component/Manifest.jsx b/src/lib/treckingAwb/component/Manifest.jsx index fbc95702..87e01e38 100644 --- a/src/lib/treckingAwb/component/Manifest.jsx +++ b/src/lib/treckingAwb/component/Manifest.jsx @@ -1,16 +1,15 @@ -import odooApi from '@/core/api/odooApi' -import BottomPopup from '@/core/components/elements/Popup/BottomPopup' -import LogoSpinner from '@/core/components/elements/Spinner/LogoSpinner' -import { getAuth } from '@/core/utils/auth' -import { useEffect, useState } from 'react' -import { toast } from 'react-hot-toast' -import ImageNext from 'next/image' -import { list } from 'postcss' +import odooApi from '@/core/api/odooApi'; +import BottomPopup from '@/core/components/elements/Popup/BottomPopup'; +import LogoSpinner from '@/core/components/elements/Spinner/LogoSpinner'; +import { getAuth } from '@/core/utils/auth'; +import { useEffect, useState } from 'react'; +import { toast } from 'react-hot-toast'; +import ImageNext from 'next/image'; +import { list } from 'postcss'; const Manifest = ({ idAWB, closePopup }) => { - const [manifests, setManifests] = useState(null) - const [isLoading, setIsLoading] = useState(false) - + const [manifests, setManifests] = useState(null); + const [isLoading, setIsLoading] = useState(false); const formatCustomDate = (date) => { const months = [ 'Jan', @@ -24,61 +23,60 @@ const Manifest = ({ idAWB, closePopup }) => { 'Sep', 'Oct', 'Nov', - 'Dec' - ] + 'Dec', + ]; - const parts = date.split(' ') // Pisahkan tanggal dan waktu - const [datePart, timePart] = parts - const [yyyy, mm, dd] = datePart.split('-') - const [hh, min] = timePart.split(':') + const parts = date.split(' '); // Pisahkan tanggal dan waktu + const [datePart, timePart] = parts; + const [yyyy, mm, dd] = datePart.split('-'); + const [hh, min] = timePart.split(':'); - const monthAbbreviation = months[parseInt(mm, 10) - 1] + const monthAbbreviation = months[parseInt(mm, 10) - 1]; - return `${dd} ${monthAbbreviation} ${hh}:${min}` - } + return `${dd} ${monthAbbreviation} ${hh}:${min}`; + }; const getManifest = async () => { - setIsLoading(true) - const auth = getAuth() - let list - if(auth){ + setIsLoading(true); + const auth = getAuth(); + let list; + if (auth) { list = await odooApi( 'GET', `/api/v1/partner/${auth.partnerId}/stock-picking/${idAWB}/tracking` - ) - }else{ - list = await odooApi( - 'GET', - `/api/v1/stock-picking/${idAWB}/tracking` - ) + ); + } else { + list = await odooApi('GET', `/api/v1/stock-picking/${idAWB}/tracking`); } - setManifests(list) - setIsLoading(false) - } + setManifests(list); + setIsLoading(false); + }; useEffect(() => { if (idAWB) { - getManifest() + getManifest(); } else { - setManifests(null) + setManifests(null); } - }, [idAWB]) + }, [idAWB]); - const [copied, setCopied] = useState(false) + const [copied, setCopied] = useState(false); const handleCopyClick = () => { - const textToCopy = manifests?.waybillNumber - navigator.clipboard.writeText(textToCopy) - setCopied(true) - toast.success('No Resi Berhasil di Copy') - setTimeout(() => setCopied(false), 2000) // Reset copied state after 2 seconds - } + const textToCopy = manifests?.waybillNumber; + navigator.clipboard.writeText(textToCopy); + setCopied(true); + toast.success('No Resi Berhasil di Copy'); + setTimeout(() => setCopied(false), 2000); // Reset copied state after 2 seconds + }; return ( <> {isLoading && ( <BottomPopup active={true} close=''> - <div className='leading-7 text-gray_r-12/80 flex justify-center'>Mohon Tunggu</div> + <div className='leading-7 text-gray_r-12/80 flex justify-center'> + Mohon Tunggu + </div> <div className='container flex justify-center my-4'> <LogoSpinner width={48} height={48} /> </div> @@ -111,11 +109,14 @@ const Manifest = ({ idAWB, closePopup }) => { </div> <div className=''> <h1 className='text-body-1'> - Estimasi tiba pada <span className='text-gray_r-11 text-sm'>({manifests?.eta})</span> + Estimasi tiba pada{' '} + <span className='text-gray_r-11 text-sm'>({manifests?.eta})</span> </h1> <h1 className='text-sm mt-2 mb-3'> Dikirim Menggunakan{' '} - <span className='text-red-500 font-semibold'>{manifests?.deliveryOrder.carrier}</span> + <span className='text-red-500 font-semibold'> + {manifests?.deliveryOrder.carrier} + </span> </h1> {manifests?.waybillNumber && ( <div className='flex justify-between items-center'> @@ -154,10 +155,16 @@ const Manifest = ({ idAWB, closePopup }) => { {manifests.delivered == true && index == 0 ? ( <div class={`absolute w-6 h-6 rounded-full mt-1.5 -left-3 border ${ - index == 0 ? 'bg-green-100 border-green-100' : 'bg-gray_r-7 border-white' + index == 0 + ? 'bg-green-100 border-green-100' + : 'bg-gray_r-7 border-white' }`} > - <ImageNext src='/images/open-box(1).svg' width={30} height={20} /> + <ImageNext + src='/images/open-box(1).svg' + width={30} + height={20} + /> </div> ) : ( <div @@ -167,7 +174,9 @@ const Manifest = ({ idAWB, closePopup }) => { {manifests.delivered != true && ( <div class={`absolute w-3 h-3 rounded-full mt-1.5 -left-1.5 border ${ - index == 0 ? 'bg-green-600 border-green-600' : 'bg-gray_r-7 border-white' + index == 0 + ? 'bg-green-600 border-green-600' + : 'bg-gray_r-7 border-white' } `} /> )} @@ -176,9 +185,15 @@ const Manifest = ({ idAWB, closePopup }) => { {formatCustomDate(manifest.datetime)} </time> {manifests.delivered == true && index == 0 && ( - <p class={`leading-6 font-semibold text-sm text-green-600 `}>Sudah Sampai</p> + <p + class={`leading-6 font-semibold text-sm text-green-600 `} + > + Sudah Sampai + </p> )} - <p class={`leading-6 text-[12px] text-gray_r-11`}>{manifest.description}</p> + <p class={`leading-6 text-[12px] text-gray_r-11`}> + {manifest.description} + </p> </li> </> ))} @@ -187,7 +202,7 @@ const Manifest = ({ idAWB, closePopup }) => { </BottomPopup> )} </> - ) -} + ); +}; -export default Manifest +export default Manifest; diff --git a/src/pages/api/flashsale-header.js b/src/pages/api/flashsale-header.js new file mode 100644 index 00000000..31f8efdd --- /dev/null +++ b/src/pages/api/flashsale-header.js @@ -0,0 +1,40 @@ +import odooApi from '@/core/api/odooApi'; +import { createClient } from 'redis'; + +const client = createClient(); + +client.on('error', (err) => console.error('Redis Client Error', err)); + +const connectRedis = async () => { + if (!client.isOpen) { + await client.connect(); + } +}; + +export default async function handler(req, res) { + try { + await connectRedis(); + const cacheKey = `flashsale_header`; + // await client.del(cacheKey); + let cachedData = await client.get(cacheKey); + + if (cachedData) { + const data = JSON.parse(cachedData); + return res.status(200).json({ data }); + } else { + const flashSale = await odooApi('GET', `/api/v1/flashsale/header`); + + await client.set( + cacheKey, + JSON.stringify(flashSale), + 'EX', + flashSale.duration + ); + cachedData = await client.get(cacheKey); + return res.status(200).json({ data: cachedData }); + } + } catch (error) { + console.error('Error interacting with Redis or fetching data:', error); + return res.status(500).json({ error: 'Internal Server Error' }); + } +} diff --git a/src/pages/api/page-content.js b/src/pages/api/page-content.js index 3cb8a237..a6514505 100644 --- a/src/pages/api/page-content.js +++ b/src/pages/api/page-content.js @@ -22,7 +22,6 @@ export default async function handler(req, res) { if (!cachedData) { const items = await getPageContent({ path }); - console.log('items', items); await client.set( `page-content:${path}`, JSON.stringify(items), diff --git a/src/pages/api/search-flashsale.js b/src/pages/api/search-flashsale.js new file mode 100644 index 00000000..d9e56c83 --- /dev/null +++ b/src/pages/api/search-flashsale.js @@ -0,0 +1,45 @@ +import odooApi from '@/core/api/odooApi'; +import { createClient } from 'redis'; +import _ from 'lodash-contrib'; +import axios from 'axios'; + +const client = createClient(); + +client.on('error', (err) => console.error('Redis Client Error', err)); + +const connectRedis = async () => { + if (!client.isOpen) { + await client.connect(); + } +}; + +export default async function handler(req, res) { + const { query, operation, duration } = req.query; + try { + await connectRedis(); + const cacheKey = `flashsale_product`; + // await client.del(cacheKey); + let cachedData = await client.get(cacheKey); + + if (cachedData) { + const data = JSON.parse(cachedData); + return res.status(200).json({ data }); + } else { + const dataProductSearch = await axios( + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/search?${query}&operation=${operation}]` + ); + + await client.set( + cacheKey, + JSON.stringify(dataProductSearch.data), + 'EX', + duration + ); + cachedData = await client.get(cacheKey); + return res.status(200).json({ data: cachedData }); + } + } catch (error) { + console.error('Error interacting with Redis or fetching data:', error); + return res.status(500).json({ error: 'Internal Server Error' }); + } +} diff --git a/src/pages/api/shop/product-detail.js b/src/pages/api/shop/product-detail.js index 247f2a04..faa96028 100644 --- a/src/pages/api/shop/product-detail.js +++ b/src/pages/api/shop/product-detail.js @@ -8,7 +8,7 @@ export default async function handler(req, res) { ) let productVariants = await axios( process.env.SOLR_HOST + - `/solr/variants/select?q=template_id_i:${req.query.id}&q.op=OR&indent=true&rows=100&fq=-publish_b:false` + `/solr/variants/select?q=template_id_i:${req.query.id}&q.op=OR&indent=true&rows=100&fq=-publish_b:false AND price_tier1_v2_f:[1 TO *]` ) let auth = req.query.auth === 'false' ? JSON.parse(req.query.auth) : req.query.auth let result = productMappingSolr(productTemplate.data.response.docs, auth || false) diff --git a/src/pages/api/shop/search.js b/src/pages/api/shop/search.js index 65927bbc..63ec7ca0 100644 --- a/src/pages/api/shop/search.js +++ b/src/pages/api/shop/search.js @@ -19,12 +19,7 @@ export default async function handler(req, res) { source = '', } = req.query; - - - console.log('fq new', fq); - let { stock = '' } = req.query; - let paramOrderBy = ''; switch (orderBy) { case 'flashsale-discount-desc': @@ -93,7 +88,6 @@ export default async function handler(req, res) { 'price_tier1_v2_f:[1 TO *]', ]; - if (fq && source != 'similar' && typeof fq != 'string') { // filterQueries.push(fq); fq.push(...filterQueries); @@ -103,7 +97,7 @@ export default async function handler(req, res) { let keywords = newQ; if (source === 'similar' || checkQ.length < 3) { if (checkQ.length < 2 || checkQ[1].length < 2) { - keywords = newQ ; + keywords = newQ; } else { keywords = newQ + '*'; } @@ -170,7 +164,6 @@ export default async function handler(req, res) { parameter = parameter.concat( fq.map((val) => `fq=${encodeURIComponent(val)}`) ); - let result = await axios( process.env.SOLR_HOST + '/solr/product/select?' + parameter.join('&') ); diff --git a/src/pages/index.jsx b/src/pages/index.jsx index cc4d68db..2ec1231a 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -45,18 +45,19 @@ const FlashSale = dynamic( } ); -const ProgramPromotion = dynamic(() => - import('@/lib/home/components/PromotionProgram'), -{ - loading: () => <BannerPromoSkeleton />, -} +const ProgramPromotion = dynamic( + () => import('@/lib/home/components/PromotionProgram'), + { + loading: () => <BannerPromoSkeleton />, + } ); const BannerSection = dynamic(() => import('@/lib/home/components/BannerSection') -); -const CategoryHomeId = dynamic(() => - import('@/lib/home/components/CategoryHomeId'), {ssr: false} +); +const CategoryHomeId = dynamic( + () => import('@/lib/home/components/CategoryHomeId'), + { ssr: false } ); const CategoryDynamic = dynamic(() => @@ -64,17 +65,18 @@ const CategoryDynamic = dynamic(() => ); const CategoryDynamicMobile = dynamic(() => -import('@/lib/home/components/CategoryDynamicMobile') + import('@/lib/home/components/CategoryDynamicMobile') ); -const CustomerReviews = dynamic(() => - import('@/lib/review/components/CustomerReviews'), {ssr: false} +const CustomerReviews = dynamic( + () => import('@/lib/review/components/CustomerReviews'), + { ssr: false } ); // need to ssr:false -const ServiceList = dynamic(() => import('@/lib/home/components/ServiceList'), {ssr: false}); // need to ssr: false +const ServiceList = dynamic(() => import('@/lib/home/components/ServiceList'), { + ssr: false, +}); // need to ssr: false - - -export default function Home({categoryId}) { +export default function Home({ categoryId }) { const bannerRef = useRef(null); const wrapperRef = useRef(null); @@ -85,123 +87,110 @@ export default function Home({categoryId}) { bannerRef.current?.querySelector(':first-child')?.clientHeight + 'px'; }; - useEffect(() => { - const loadCategories = async () => { - const getCategories = await odooApi('GET', '/api/v1/category/child?partner_id='+{categoryId}) - if(getCategories){ - setDataCategories(getCategories) - } - } - loadCategories() - }, []) - - const [dataCategories, setDataCategories] = useState([]) return ( <> - <BasicLayout> - <Seo - title='Indoteknik.com: B2B Industrial Supply & Solution' - description='Temukan pilihan produk B2B Industri & Alat Teknik untuk Perusahaan, UMKM & Pemerintah dengan lengkap, mudah dan transparan.' - additionalMetaTags={[ - { - name: 'keywords', - content: 'indoteknik, indoteknik.com, toko teknik, toko perkakas, jual genset, jual fogging, jual krisbow, harga krisbow, harga alat safety, harga pompa air', - }, - ]} /> - - <PagePopupIformation /> - - <DesktopView> - <div className='container mx-auto'> - <div - className='flex min-h-[400px] h-[460px]' - ref={wrapperRef} - onLoad={handleOnLoad} - > - <div className='w-2/12'> - <HeroBannerSecondary /> - </div> - <div className='w-7/12 px-1' ref={bannerRef}> - <HeroBanner /> + <BasicLayout> + <Seo + title='Indoteknik.com: B2B Industrial Supply & Solution' + description='Temukan pilihan produk B2B Industri & Alat Teknik untuk Perusahaan, UMKM & Pemerintah dengan lengkap, mudah dan transparan.' + additionalMetaTags={[ + { + name: 'keywords', + content: + 'indoteknik, indoteknik.com, toko teknik, toko perkakas, jual genset, jual fogging, jual krisbow, harga krisbow, harga alat safety, harga pompa air', + }, + ]} + /> + + <PagePopupIformation /> + + <DesktopView> + <div className='container mx-auto'> + <div + className='flex min-h-[400px] h-[460px]' + ref={wrapperRef} + onLoad={handleOnLoad} + > + <div className='w-2/12'> + <HeroBannerSecondary /> + </div> + <div className='w-7/12 px-1' ref={bannerRef}> + <HeroBanner /> + </div> + <div className='w-3/12'> + <DelayRender renderAfter={200}> + <PopularProduct /> + </DelayRender> + </div> </div> - <div className='w-3/12'> - <DelayRender renderAfter={200}> - <PopularProduct /> - </DelayRender> - </div> - </div> - <div className='my-16 flex flex-col gap-y-8'> - <ServiceList /> - <div id='flashsale'> - <PreferredBrand /> + <div className='my-16 flex flex-col gap-y-8'> + <ServiceList /> + <div id='flashsale'> + <PreferredBrand /> + </div> + {!auth?.feature?.soApproval && ( + <> + <DelayRender renderAfter={200}> + <ProgramPromotion /> + </DelayRender> + <DelayRender renderAfter={200}> + <FlashSale /> + </DelayRender> + </> + )} + {/* <PromotinProgram /> */} + <CategoryPilihan /> + <CategoryDynamic /> + <CategoryHomeId /> + <BannerSection /> + <CustomerReviews /> </div> + </div> + </DesktopView> + <MobileView> + <DelayRender renderAfter={200}> + <HeroBanner /> + </DelayRender> + <div className='flex flex-col gap-y-4 my-6'> + <DelayRender renderAfter={400}> + <ServiceList /> + </DelayRender> + <DelayRender renderAfter={400}> + <div id='flashsale'> + <PreferredBrand /> + </div> + </DelayRender> {!auth?.feature?.soApproval && ( <> - <DelayRender renderAfter={200}> - <ProgramPromotion /> - </DelayRender> - <DelayRender renderAfter={200}> - <FlashSale /> - </DelayRender> + <DelayRender renderAfter={400}> + <ProgramPromotion /> + </DelayRender> + <DelayRender renderAfter={600}> + <FlashSale /> + </DelayRender> </> )} - {/* <PromotinProgram /> */} - {dataCategories &&( - <CategoryPilihan categories={dataCategories} /> - )} - <CategoryDynamic /> - <CategoryHomeId /> - <BannerSection /> - <CustomerReviews /> + <DelayRender renderAfter={600}> + {/* <PromotinProgram /> */} + </DelayRender> + <DelayRender renderAfter={600}> + <CategoryPilihan /> + <CategoryDynamicMobile /> + </DelayRender> + <DelayRender renderAfter={800}> + <PopularProduct /> + </DelayRender> + <DelayRender renderAfter={1000}> + <CategoryHomeId /> + <BannerSection /> + </DelayRender> + <DelayRender renderAfter={1200}> + <CustomerReviews /> + </DelayRender> </div> - </div> - </DesktopView> - <MobileView> - <DelayRender renderAfter={200}> - <HeroBanner /> - </DelayRender> - <div className='flex flex-col gap-y-4 my-6'> - <DelayRender renderAfter={400}> - <ServiceList /> - </DelayRender> - <DelayRender renderAfter={400}> - <div id='flashsale'> - <PreferredBrand /> - </div> - </DelayRender> - {!auth?.feature?.soApproval && ( - <> - <DelayRender renderAfter={400}> - <ProgramPromotion /> - </DelayRender> - <DelayRender renderAfter={600}> - <FlashSale /> - </DelayRender> - </> - )} - <DelayRender renderAfter={600}> - {/* <PromotinProgram /> */} - </DelayRender> - <DelayRender renderAfter={600}> - {dataCategories &&( - <CategoryPilihan categories={dataCategories} /> - )} - <CategoryDynamicMobile /> - </DelayRender> - <DelayRender renderAfter={800}> - <PopularProduct /> - </DelayRender> - <DelayRender renderAfter={1000}> - <CategoryHomeId /> - <BannerSection /> - </DelayRender> - <DelayRender renderAfter={1200}> - <CustomerReviews /> - </DelayRender> - </div> - </MobileView> - </BasicLayout> - </> + </MobileView> + </BasicLayout> + </> ); -}
\ No newline at end of file +} diff --git a/src/utils/solrMapping.js b/src/utils/solrMapping.js index 01a8587c..ecd62be2 100644 --- a/src/utils/solrMapping.js +++ b/src/utils/solrMapping.js @@ -75,7 +75,7 @@ export const productMappingSolr = (products, pricelist) => { name: product.manufacture_name_s || '', imagePromotion1: product.image_promotion_1_s || '', imagePromotion2: product.image_promotion_2_s || '', - logo : product.x_logo_manufacture_s || '', + logo: product.x_logo_manufacture_s || '', }; } @@ -129,13 +129,14 @@ export const variantsMappingSolr = (parent, products, pricelist) => { manufacture: {}, parent: {}, qtySold: product?.qty_sold_f || 0, + is_in_bu: product?.is_in_bu_b || false, }; if (product.manufacture_id_i && product.manufacture_name_s) { productMapped.manufacture = { id: product.manufacture_id_i || '', name: product.manufacture_name_s || '', - logo : parent[0]?.x_logo_manufacture_s + logo: parent[0]?.x_logo_manufacture_s, }; } productMapped.parent = { |
