diff options
| author | Rafi Zadanly <zadanlyr@gmail.com> | 2023-10-21 10:06:54 +0700 |
|---|---|---|
| committer | Rafi Zadanly <zadanlyr@gmail.com> | 2023-10-21 10:06:54 +0700 |
| commit | a001da95b9c03167656aec8a573cf60c12164b3f (patch) | |
| tree | 54c8432bf9b1ec21648c6fae9faf94066c96987f | |
| parent | 5c1f114b9e9feb256a49bfd60ed60c72823bca7c (diff) | |
| parent | bf33b9a9493aeab84e72647fad384bed43feabd5 (diff) | |
Merge branch 'master' into refactor/all
| -rw-r--r-- | src/core/utils/formatValue.js | 10 | ||||
| -rw-r--r-- | src/lib/checkout/components/Checkout.jsx | 14 | ||||
| -rw-r--r-- | src/lib/checkout/components/CheckoutOld.jsx | 4 | ||||
| -rw-r--r-- | src/lib/flashSale/components/FlashSale.jsx | 2 | ||||
| -rw-r--r-- | src/lib/product/components/Product/ProductDesktop.jsx | 27 | ||||
| -rw-r--r-- | src/lib/product/components/Product/ProductMobile.jsx | 11 | ||||
| -rw-r--r-- | src/lib/product/components/ProductCard.jsx | 39 | ||||
| -rw-r--r-- | src/lib/product/components/ProductFilter.jsx | 6 | ||||
| -rw-r--r-- | src/lib/product/components/ProductSearch.jsx | 12 | ||||
| -rw-r--r-- | src/lib/variant/components/VariantCard.jsx | 2 | ||||
| -rw-r--r-- | src/pages/api/shop/search.js | 15 | ||||
| -rw-r--r-- | src/pages/google_merchant/products/[page].js | 45 | ||||
| -rw-r--r-- | src/utils/solrMapping.js | 6 |
13 files changed, 125 insertions, 68 deletions
diff --git a/src/core/utils/formatValue.js b/src/core/utils/formatValue.js new file mode 100644 index 00000000..f2c17769 --- /dev/null +++ b/src/core/utils/formatValue.js @@ -0,0 +1,10 @@ +const sellingProductFormat = (value) => { + if (value > 1000) { + const formattedValue = (value / 1000).toFixed(0).replace('.', ',') + return formattedValue + 'rb+'; + } else { + return value.toString() + } +} + +export { sellingProductFormat } diff --git a/src/lib/checkout/components/Checkout.jsx b/src/lib/checkout/components/Checkout.jsx index 511fcdba..9a799010 100644 --- a/src/lib/checkout/components/Checkout.jsx +++ b/src/lib/checkout/components/Checkout.jsx @@ -168,6 +168,16 @@ const Checkout = () => { setExpedisi(dataExpedisi) } loadExpedisi() + + const handlePopState = () => { + router.push('/shop/cart') + } + + window.onpopstate = handlePopState + + return () => { + window.onpopstate = null + } // voucher() }, []) @@ -1071,7 +1081,7 @@ const Checkout = () => { <div className='font-normal mt-1'> {product.price.priceDiscount > 0 ? currencyFormat(product?.price?.priceDiscount) - : 'Call For Price'} + : 'Call for Inquiry'} </div> )} </td> @@ -1092,7 +1102,7 @@ const Checkout = () => { })} className='underline' > - Call For Price{' '} + Call for Inquiry{' '} </a> )} </div> diff --git a/src/lib/checkout/components/CheckoutOld.jsx b/src/lib/checkout/components/CheckoutOld.jsx index 6852059e..d57fbd66 100644 --- a/src/lib/checkout/components/CheckoutOld.jsx +++ b/src/lib/checkout/components/CheckoutOld.jsx @@ -486,7 +486,7 @@ const Checkout = () => { <div className='font-normal mt-1'> {product.price.priceDiscount > 0 ? currencyFormat(product?.price?.priceDiscount) - : 'Call For Price'} + : 'Call for Inquiry'} </div> </td> <td> @@ -501,7 +501,7 @@ const Checkout = () => { })} className='underline' > - Call For Price{' '} + Call for Inquiry{' '} </a> )} </div> diff --git a/src/lib/flashSale/components/FlashSale.jsx b/src/lib/flashSale/components/FlashSale.jsx index 87545d8d..3d5c4e0e 100644 --- a/src/lib/flashSale/components/FlashSale.jsx +++ b/src/lib/flashSale/components/FlashSale.jsx @@ -63,7 +63,7 @@ const FlashSaleProduct = ({ flashSaleId }) => { useEffect(() => { const loadProducts = async () => { const dataProducts = await productSearchApi({ - query: `fq=flashsale_id_i:${flashSaleId}&fq=flashsale_price_f:[1 TO *]&limit=500`, + query: `fq=flashsale_id_i:${flashSaleId}&fq=flashsale_price_f:[1 TO *]&limit=500&orderBy=flashsale-price-asc`, operation: 'AND' }) setProducts(dataProducts.response) diff --git a/src/lib/product/components/Product/ProductDesktop.jsx b/src/lib/product/components/Product/ProductDesktop.jsx index 701750b2..a8cca416 100644 --- a/src/lib/product/components/Product/ProductDesktop.jsx +++ b/src/lib/product/components/Product/ProductDesktop.jsx @@ -25,6 +25,7 @@ import { useProductCartContext } from '@/contexts/ProductCartContext' import { Box, Skeleton, Tooltip } from '@chakra-ui/react' import { Info } from 'lucide-react' import Breadcrumb from './Breadcrumb' +import { sellingProductFormat } from '@/core/utils/formatValue' const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { const router = useRouter() @@ -44,21 +45,9 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { const { setRefreshCart, refreshCart } = useProductCartContext() - const getLowestPrice = useCallback(() => { - const prices = product.variants.map((variant) => ({ - price: variant.price, - isFlashsale: variant.isFlashsale - })) - const lowest = prices.reduce((lowest, price) => { - return price.price < lowest.price ? price : lowest - }, prices[0]) - return lowest - }, [product]) - useEffect(() => { - const lowest = getLowestPrice() - setLowestPrice(lowest) - }, [getLowestPrice]) + setLowestPrice({ price: product?.lowestPrice }) + }, [product]) useEffect(() => { const getBackgound = async () => { @@ -418,7 +407,7 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { </div> </div> - <div className='w-[25%]'> + <div className='w-[30%]'> {product.variants.length > 1 && product.lowestPrice.priceDiscount > 0 && ( <div className='text-gray_r-12/80'>Harga mulai dari: </div> )} @@ -447,7 +436,11 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { )} </div> )} */} - + {product?.qtySold > 0 && ( + <div className='text-gray_r-9'> + {sellingProductFormat(product?.qtySold) + ' Terjual'} + </div> + )} {lowestPrice?.isFlashsale && lowestPrice?.price.discountPercentage > 0 ? ( <> <div className='flex gap-x-1 items-center mt-2'> @@ -632,7 +625,7 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { rel='noopener noreferrer' target='_blank' > - Call for price + Call for Inquiry </a> )} </div> diff --git a/src/lib/product/components/Product/ProductMobile.jsx b/src/lib/product/components/Product/ProductMobile.jsx index 70ac1cbc..ef2c0002 100644 --- a/src/lib/product/components/Product/ProductMobile.jsx +++ b/src/lib/product/components/Product/ProductMobile.jsx @@ -21,6 +21,7 @@ import ImageNext from 'next/image' import CountDown2 from '@/core/components/elements/CountDown/CountDown2' import Breadcrumb from './Breadcrumb' import useAuth from '@/core/hooks/useAuth' +import { sellingProductFormat } from '@/core/utils/formatValue' const ProductMobile = ({ product, wishlist, toggleWishlist }) => { const router = useRouter() @@ -62,7 +63,8 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { price: getLowestPrice(), stock: product.stockTotal, weight: product.weight, - hasProgram: false + hasProgram: false, + qtySold: product.qtySold }) const variantOptions = product.variants?.map((variant) => { @@ -105,7 +107,8 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { stock: variant.stock, weight: variant.weight, hasProgram: variant.hasProgram, - isFlashsale: variant.isFlashsale + isFlashsale: variant.isFlashsale, + qtySold: variant.qtySold } setActiveVariant(newActiveVariant) @@ -245,7 +248,9 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { </button> </div> <h1 className='leading-6 font-medium mb-3'>{activeVariant?.name}</h1> - + {product?.qtySold > 0 && ( + <div className='text-gray_r-9'>{sellingProductFormat(activeVariant?.qtySold) + ' Terjual'}</div> + )} {product.variants.length > 1 && activeVariant.price.priceDiscount > 0 && !selectedVariant && ( diff --git a/src/lib/product/components/ProductCard.jsx b/src/lib/product/components/ProductCard.jsx index 3b96ac32..9500a3fd 100644 --- a/src/lib/product/components/ProductCard.jsx +++ b/src/lib/product/components/ProductCard.jsx @@ -1,6 +1,7 @@ import Image from '@/core/components/elements/Image/Image' import Link from '@/core/components/elements/Link/Link' import currencyFormat from '@/core/utils/currencyFormat' +import { sellingProductFormat } from '@/core/utils/formatValue' import { createSlug } from '@/core/utils/slug' import whatsappUrl from '@/core/utils/whatsappUrl' import ImageNext from 'next/image' @@ -82,9 +83,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { )} <Link href={createSlug('/shop/product/', product?.name, product?.id)} - className={`mb-3 !text-gray_r-12 leading-6 block ${ - simpleTitle ? 'line-clamp-2 h-12' : 'line-clamp-3 h-[64px]' - }`} + className={`mb-2 !text-gray_r-12 leading-6 block line-clamp-3 h-[64px]`} title={product?.name} > {product?.name} @@ -103,7 +102,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { {product?.lowestPrice.priceDiscount > 0 ? ( currencyFormat(product?.lowestPrice.priceDiscount) ) : ( - <a href={callForPriceWhatsapp}>Call for price</a> + <a href={callForPriceWhatsapp}>Call for Inquiry</a> )} </div> </> @@ -118,17 +117,16 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { </div> </> ) : ( - <a href={callForPriceWhatsapp}>Call for price</a> + <a href={callForPriceWhatsapp}>Call for Inquiry</a> )} </div> )} - {product?.stockTotal > 0 && ( - <div className='flex gap-x-1'> - <div className='badge-solid-red'>Ready Stock</div> - <div className='badge-gray'>{product?.stockTotal > 5 ? '> 5' : '< 5'}</div> - </div> - )} + <div className='flex w-full items-center gap-x-1 '> + {product?.stockTotal > 0 && <div className='badge-solid-red'>Ready Stock</div>} + {/* <div className='badge-gray'>{product?.stockTotal > 5 ? '> 5' : '< 5'}</div> */} + {product?.qtySold > 0 && <div className='text-gray_r-9 text-[11px]'>{sellingProductFormat(product?.qtySold) + ' Terjual'}</div>} + </div> </div> </div> ) @@ -186,9 +184,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { )} <Link href={createSlug('/shop/product/', product?.name, product?.id)} - className={`mb-3 !text-gray_r-12 leading-6 ${ - simpleTitle ? 'line-clamp-2' : 'line-clamp-3' - }`} + className={`mb-3 !text-gray_r-12 leading-6 line-clamp-3`} > {product?.name} </Link> @@ -209,7 +205,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { {product?.lowestPrice?.priceDiscount > 0 ? ( currencyFormat(product?.lowestPrice?.priceDiscount) ) : ( - <a href={callForPriceWhatsapp}>Call for price</a> + <a href={callForPriceWhatsapp}>Call for Inquiry</a> )} </div> </> @@ -224,17 +220,16 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { </div> </> ) : ( - <a href={callForPriceWhatsapp}>Call for price</a> + <a href={callForPriceWhatsapp}>Call for Inquiry</a> )} </div> )} - {product?.stockTotal > 0 && ( - <div className='flex gap-x-1'> - <div className='badge-solid-red'>Ready Stock</div> - <div className='badge-gray'>{product?.stockTotal > 5 ? '> 5' : '< 5'}</div> - </div> - )} + <div className='flex w-full items-center gap-x-1 '> + {product?.stockTotal > 0 && <div className='badge-solid-red'>Ready Stock</div>} + {/* <div className='badge-gray'>{product?.stockTotal > 5 ? '> 5' : '< 5'}</div> */} + {product?.qtySold > 0 && <div className='text-gray_r-9 text-[11px]'>{sellingProductFormat(product?.qtySold) + ' Terjual'}</div>} + </div> </div> </div> ) diff --git a/src/lib/product/components/ProductFilter.jsx b/src/lib/product/components/ProductFilter.jsx index 14eef0ba..d52fcb90 100644 --- a/src/lib/product/components/ProductFilter.jsx +++ b/src/lib/product/components/ProductFilter.jsx @@ -15,7 +15,7 @@ const orderOptions = [ const ProductFilter = ({ active, close, brands, categories, prefixUrl, defaultBrand = null }) => { const router = useRouter() const { query } = router - const [order, setOrder] = useState(query?.orderBy) + const [order, setOrder] = useState(query?.orderBy || 'popular') const [brand, setBrand] = useState(query?.brand) const [category, setCategory] = useState(query?.category) const [priceFrom, setPriceFrom] = useState(query?.priceFrom) @@ -184,7 +184,7 @@ const ProductFilter = ({ active, close, brands, categories, prefixUrl, defaultBr ))} </div> </div> - <div> + {/* <div> <label>Ketersedian Stok</label> <div className='mt-2'> <Checkbox @@ -196,7 +196,7 @@ const ProductFilter = ({ active, close, brands, categories, prefixUrl, defaultBr Ketersedian Stok </Checkbox> </div> - </div> + </div> */} <button type='button' className='btn-solid-red w-full mt-2' onClick={handleSubmit}> Terapkan Filter </button> diff --git a/src/lib/product/components/ProductSearch.jsx b/src/lib/product/components/ProductSearch.jsx index fd75d587..9d59b305 100644 --- a/src/lib/product/components/ProductSearch.jsx +++ b/src/lib/product/components/ProductSearch.jsx @@ -23,8 +23,9 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null }) => { const { page = 1 } = query const [q, setQ] = useState(query?.q || '*') const [limit, setLimit] = useState(query?.limit || 30) + const [orderBy, setOrderBy] = useState(router.query?.orderBy || 'popular') if (defaultBrand) query.brand = defaultBrand.toLowerCase() - const { productSearch } = useProductSearch({ query: { ...query, q, limit } }) + const { productSearch } = useProductSearch({ query: { ...query, q, limit, orderBy } }) const [products, setProducts] = useState(null) const [spellings, setSpellings] = useState(null) const [bannerPromotionHeader, setBannerPromotionHeader] = useState(null) @@ -70,11 +71,11 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null }) => { const brands = [] for ( let i = 0; - i < productSearch.data?.facetCounts?.facetFields?.manufactureName.length; + i < productSearch.data?.facetCounts?.facetFields?.manufactureNameS.length; i += 2 ) { - const brand = productSearch.data?.facetCounts?.facetFields?.manufactureName[i] - const qty = productSearch.data?.facetCounts?.facetFields?.manufactureName[i + 1] + const brand = productSearch.data?.facetCounts?.facetFields?.manufactureNameS[i] + const qty = productSearch.data?.facetCounts?.facetFields?.manufactureNameS[i + 1] if (qty > 0) { brands.push({ brand, qty }) } @@ -303,10 +304,9 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null }) => { <select name='urutan' className='form-input mt-2' - value={router.query?.orderBy || ''} + value={orderBy} onChange={(e) => handleOrderBy(e)} > - <option value=''>Urutkan</option> {orderOptions.map((option, index) => ( <option key={index} value={option.value}> {' '} diff --git a/src/lib/variant/components/VariantCard.jsx b/src/lib/variant/components/VariantCard.jsx index 6c0ab974..9f1b5733 100644 --- a/src/lib/variant/components/VariantCard.jsx +++ b/src/lib/variant/components/VariantCard.jsx @@ -68,7 +68,7 @@ const VariantCard = ({ product, openOnClick = true, buyMore = false }) => { })} className='underline text-danger-500' > - Call For Price{' '} + Call for Inquiry{' '} </a> )} </p> diff --git a/src/pages/api/shop/search.js b/src/pages/api/shop/search.js index fd7a215a..b4d67c5d 100644 --- a/src/pages/api/shop/search.js +++ b/src/pages/api/shop/search.js @@ -13,20 +13,20 @@ export default async function handler(req, res) { orderBy = '', operation = 'AND', fq = '', - limit = 0, + limit = 30, stock = '' } = req.query let paramOrderBy = '' switch (orderBy) { case 'price-asc': - paramOrderBy += 'price_discount_f ASC' + paramOrderBy += 'price_tier1_v2_f ASC' break case 'price-desc': - paramOrderBy += 'price_discount_f DESC' + paramOrderBy += 'price_tier1_v2_f DESC' break case 'popular': - paramOrderBy += 'search_rank_i DESC' + paramOrderBy += 'product_rating_f DESC, search_rank_i DESC,' break case 'popular-weekly': paramOrderBy += 'search_rank_weekly_i DESC' @@ -34,6 +34,9 @@ export default async function handler(req, res) { case 'stock': paramOrderBy += 'stock_total_f DESC' break + case 'flashsale-price-asc': + paramOrderBy += 'flashsale_price_f ASC' + break default: paramOrderBy += 'product_rating_f DESC, price_discount_f DESC' break @@ -41,7 +44,7 @@ export default async function handler(req, res) { let offset = (page - 1) * limit let parameter = [ - 'facet.field=manufacture_name', + 'facet.field=manufacture_name_s', 'facet.field=category_name', 'facet=true', 'indent=true', @@ -49,7 +52,7 @@ export default async function handler(req, res) { `q.op=${operation}`, `q=${escapeSolrQuery(q)}`, 'qf=name_s', - `start=${offset}`, + `start=${parseInt(offset)}`, `rows=${limit}`, `sort=${paramOrderBy}`, `fq=-publish_b:false` diff --git a/src/pages/google_merchant/products/[page].js b/src/pages/google_merchant/products/[page].js index d6309090..c8b4079b 100644 --- a/src/pages/google_merchant/products/[page].js +++ b/src/pages/google_merchant/products/[page].js @@ -2,6 +2,7 @@ import { createSlug } from '@/core/utils/slug' import toTitleCase from '@/core/utils/toTitleCase' import productSearchApi from '@/lib/product/api/productSearchApi' import variantSearchApi from '@/lib/product/api/variantSearchApi' +import axios from 'axios' import _ from 'lodash-contrib' import { create } from 'xmlbuilder' @@ -21,8 +22,11 @@ export async function getServerSideProps({ res, query }) { } const products = await variantSearchApi({ query: _.toQuery(queries) }) + const brandsData = {} + const categoriesData = {} + const productItems = [] - products.response.products.forEach((product) => { + for (const product of products.response.products) { const productUrl = createSlug('/shop/product/variant/', product.name, product.id, true) const productId = product.code != '' ? product.code : product.id const regexHtmlTags = /(<([^>]+)>)/gi @@ -34,6 +38,27 @@ export async function getServerSideProps({ res, query }) { product.description = defaultProductDescription } + let categoryName = null + + let brandId = product.manufacture?.id ?? null + let categoryId = null + + if (brandId && brandId in brandsData) { + categoryId = brandsData[brandId].category_ids?.[0] ?? null + } else { + const solrBrand = await getBrandById(brandId) + brandsData[brandId] = solrBrand + categoryId = solrBrand?.category_ids?.[0] ?? null + } + + if (categoryId && categoryId in categoriesData) { + categoryName = categoriesData[categoryId].name_s ?? null + } else { + const solrCategory = await getCategoryById(categoryId) + categoriesData[categoryId] = solrCategory + categoryName = solrCategory?.name_s ?? null + } + const availability = 'in_stock' const item = { @@ -47,20 +72,24 @@ export async function getServerSideProps({ res, query }) { 'g:brand': { '#text': product.manufacture?.name || '' }, 'g:price': { '#text': `${Math.round(product.lowestPrice.price * 1.11)} IDR` } } - + if (product.stockTotal == 0) { item['g:custom_label_0'] = { '#text': 'Stok Tidak Tersedia' } } else { item['g:custom_label_1'] = { '#text': 'Stok Tersedia' } } + if (categoryName) { + item['g:custom_label_2'] = { '#text': categoryName } + } + if (product.lowestPrice.discountPercentage > 0) { item['g:sale_price'] = { '#text': `${Math.round(product.lowestPrice.priceDiscount * 1.11)} IDR` } } productItems.push(item) - }) + } const googleMerchant = { rss: { @@ -82,6 +111,16 @@ export async function getServerSideProps({ res, query }) { return { props: {} } } +const getBrandById = async (id) => { + const brand = await axios(`${process.env.SOLR_HOST}/solr/brands/select?q=id:${id}`) + return brand.data.response.docs[0] ?? null +} + +const getCategoryById = async (id) => { + const category = await axios(`${process.env.SOLR_HOST}/solr/categories/select?q=id:${id}`) + return category.data.response.docs[0] ?? null +} + export default function GoogleMerchantPage() { return null } diff --git a/src/utils/solrMapping.js b/src/utils/solrMapping.js index 61816cf8..41d24b53 100644 --- a/src/utils/solrMapping.js +++ b/src/utils/solrMapping.js @@ -32,7 +32,8 @@ export const productMappingSolr = (products, pricelist) => { remainingTime: flashsaleTime(product?.flashsale_end_date_s)?.remainingTime, name: product?.product?.flashsale_name_s, tag: product?.flashsale_tag_s || 'FLASH SALE' - } + }, + qtySold : product?.qty_sold_f || 0 } if (product.manufacture_id_i && product.manufacture_name_s) { @@ -85,7 +86,8 @@ export const variantsMappingSolr = (parent, products, pricelist) => { stockTotal: product.stock_total_f || 0, weight: product.weight_f || 0, manufacture: {}, - parent: {} + parent: {}, + qtySold : product?.qty_sold_f || 0 } if (product.manufacture_id_i && product.manufacture_name_s) { |
