diff options
| author | IT Fixcomart <it@fixcomart.co.id> | 2024-10-07 06:08:39 +0000 |
|---|---|---|
| committer | IT Fixcomart <it@fixcomart.co.id> | 2024-10-07 06:08:39 +0000 |
| commit | 154f70e23f9aaced5b210b4f27161f70999ae9cb (patch) | |
| tree | 5de6b0e48df5267f0bd0fd99397157e7e57ee9fb /src | |
| parent | 79e1000887b6326475251624575655bb65c296db (diff) | |
| parent | 41a12de37fff3f716de77bb18c50073336f69ab4 (diff) | |
Merged in iman/flashsaleNonDisplay (pull request #345)
Iman/flashsaleNonDisplay
Approved-by: trisusilo
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/flashSale/components/FlashSale.jsx | 60 | ||||
| -rw-r--r-- | src/lib/flashSale/components/FlashSaleNonDisplay.jsx | 68 | ||||
| -rw-r--r-- | src/lib/product/components/ProductFilter.jsx | 127 | ||||
| -rw-r--r-- | src/lib/product/components/ProductFilterDesktop.jsx | 165 | ||||
| -rw-r--r-- | src/lib/product/components/ProductSearch.jsx | 20 | ||||
| -rw-r--r-- | src/pages/api/shop/search.js | 35 |
6 files changed, 313 insertions, 162 deletions
diff --git a/src/lib/flashSale/components/FlashSale.jsx b/src/lib/flashSale/components/FlashSale.jsx index 5be6d4e3..89c46de4 100644 --- a/src/lib/flashSale/components/FlashSale.jsx +++ b/src/lib/flashSale/components/FlashSale.jsx @@ -26,38 +26,40 @@ const FlashSale = () => { } return ( - flashSales?.length > 0 && ( - <div className='px-4 sm:px-0 grid grid-cols-1 gap-y-8'> - {flashSales.map((flashSale, index) => ( - <div key={index}> - <div className='flex gap-x-3 mb-4 justify-between sm:justify-start'> - <div className='font-medium sm:text-h-lg mt-1.5'> - {flashSale.name} + <div className='sm:mt-4'> + {flashSales?.length > 0 && ( + <div className='px-4 sm:px-0 grid grid-cols-1 gap-y-8 sm:mt-4'> + {flashSales.map((flashSale, index) => ( + <div key={index}> + <div className='flex gap-x-3 mb-4 justify-between sm:justify-start'> + <div className='font-medium sm:text-h-lg mt-1.5'> + {flashSale.name} + </div> + <CountDown initialTime={flashSale.duration} /> </div> - <CountDown initialTime={flashSale.duration} /> - </div> - <div className='relative'> - <Image - src={flashSale.banner} - alt={flashSale.name} - width={1080} - height={192} - className='w-full rounded mb-4 hidden sm:block' - /> - <Image - src={flashSale.bannerMobile} - alt={flashSale.name} - width={256} - height={48} - className='w-full rounded mb-4 block sm:hidden' - /> - <FlashSaleProduct flashSaleId={flashSale.pricelistId} /> + <div className='relative'> + <Image + src={flashSale.banner} + alt={flashSale.name} + width={1080} + height={192} + className='w-full rounded mb-4 hidden sm:block' + /> + <Image + src={flashSale.bannerMobile} + alt={flashSale.name} + width={256} + height={48} + className='w-full rounded mb-4 block sm:hidden' + /> + <FlashSaleProduct flashSaleId={flashSale.pricelistId} /> + </div> </div> - </div> - ))} - </div> - ) + ))} + </div> + )} + </div> ); }; diff --git a/src/lib/flashSale/components/FlashSaleNonDisplay.jsx b/src/lib/flashSale/components/FlashSaleNonDisplay.jsx new file mode 100644 index 00000000..c91de2be --- /dev/null +++ b/src/lib/flashSale/components/FlashSaleNonDisplay.jsx @@ -0,0 +1,68 @@ +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'; +import Link from 'next/link'; +import { useRouter } from 'next/router'; +const FlashSaleNonDisplay = () => { + const [flashSales, setFlashSales] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const router = useRouter(); + useEffect(() => { + const loadFlashSales = async () => { + const dataFlashSales = await flashSaleApi(); + setFlashSales(dataFlashSales); + setIsLoading(false); + }; + loadFlashSales(); + }, []); + const handleSubmit = () => { + router.push(`/shop/search?penawaran=${flashSales[0]?.pricelistId}`); + }; + if (isLoading) { + return <FlashSaleSkeleton />; + } + + return ( + flashSales?.length > 0 && ( + <div className='px-4 sm:px-0 grid grid-cols-1 gap-y-8'> + {flashSales.map((flashSale, index) => ( + <div key={index}> + <div className='flex items-center mb-4 justify-between '> + <div className='font-medium sm:text-h-lg mt-1.5'> + Penawaran Terbatas + </div> + <div + onClick={handleSubmit} + className='!text-red-500 font-semibold cursor-pointer' + > + Lihat Semua + </div> + </div> + <div className='relative'> + <FlashSaleProduct flashSaleId={flashSale.pricelistId} /> + </div> + </div> + ))} + </div> + ) + ); +}; +const FlashSaleProduct = ({ flashSaleId }) => { + const [products, setProducts] = useState(null); + useEffect(() => { + const loadProducts = async () => { + const dataProducts = await productSearchApi({ + query: `fq=-flashsale_id_i:${flashSaleId}&fq=flashsale_price_f:[1 TO *]&limit=25&orderBy=flashsale-discount-desc`, + operation: 'AND', + }); + setProducts(dataProducts.response); + }; + loadProducts(); + }, [flashSaleId]); + return <ProductSlider products={products} />; +}; +export default FlashSaleNonDisplay; diff --git a/src/lib/product/components/ProductFilter.jsx b/src/lib/product/components/ProductFilter.jsx index d52fcb90..947550b7 100644 --- a/src/lib/product/components/ProductFilter.jsx +++ b/src/lib/product/components/ProductFilter.jsx @@ -1,88 +1,96 @@ -import BottomPopup from '@/core/components/elements/Popup/BottomPopup' -import { useRouter } from 'next/router' -import { useState } from 'react' -import _ from 'lodash' -import { toQuery } from 'lodash-contrib' -import { Checkbox } from '@chakra-ui/react' +import BottomPopup from '@/core/components/elements/Popup/BottomPopup'; +import { useRouter } from 'next/router'; +import { useState } from 'react'; +import _ from 'lodash'; +import { toQuery } from 'lodash-contrib'; +import { Checkbox } from '@chakra-ui/react'; const orderOptions = [ { value: 'price-asc', label: 'Harga Terendah' }, { value: 'price-desc', label: 'Harga Tertinggi' }, { value: 'popular', label: 'Populer' }, - { value: 'stock', label: 'Ready Stock' } -] + { value: 'stock', label: 'Ready Stock' }, +]; -const ProductFilter = ({ active, close, brands, categories, prefixUrl, defaultBrand = null }) => { - const router = useRouter() - const { query } = router - 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) - const [priceTo, setPriceTo] = useState(query?.priceTo) +const ProductFilter = ({ + active, + close, + brands, + categories, + prefixUrl, + defaultBrand = null, +}) => { + const router = useRouter(); + const { query } = router; + 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); + const [priceTo, setPriceTo] = useState(query?.priceTo); - const [stock, setStock] = useState(query?.stock) + const [stock, setStock] = useState(query?.stock); - const [activeRange, setActiveRange] = useState(null) + const [activeRange, setActiveRange] = useState(null); const priceRange = [ { priceFrom: 100000, - priceTo: 200000 + priceTo: 200000, }, { priceFrom: 200000, - priceTo: 300000 + priceTo: 300000, }, { priceFrom: 300000, - priceTo: 400000 + priceTo: 400000, }, { priceFrom: 400000, - priceTo: 500000 - } - ] + priceTo: 500000, + }, + ]; const handlePriceFromChange = async (priceFromr, priceTor, index) => { - await setPriceFrom(priceFromr) - await setPriceTo(priceTor) - setActiveRange(index) - } + await setPriceFrom(priceFromr); + await setPriceTo(priceTor); + setActiveRange(index); + }; const handleReadyStockChange = (event) => { - const value = event.target.value - const isChecked = event.target.checked + const value = event.target.value; + const isChecked = event.target.checked; if (isChecked) { - setStock(value) + setStock(value); } else { - setStock(null) + setStock(null); } - } + }; const handleSubmit = () => { let params = { + penawaran: router.query.penawaran, q: router.query.q, orderBy: order, brand, category, priceFrom, priceTo, - stock: stock - } - params = _.pickBy(params, _.identity) - params = toQuery(params) - router.push(`${prefixUrl}?${params}`) - } + stock: stock, + }; + params = _.pickBy(params, _.identity); + params = toQuery(params); + router.push(`${prefixUrl}?${params}`); + }; const formatCurrency = (value) => { if (value >= 1000) { - const thousands = Math.floor(value / 1000) // Menghitung ribuan - return `Rp${thousands}k` + const thousands = Math.floor(value / 1000); // Menghitung ribuan + return `Rp${thousands}k`; } else { - return `Rp${value}` + return `Rp${value}`; } - } + }; return ( <BottomPopup active={active} close={close} title='Filter Produk'> @@ -101,7 +109,10 @@ const ProductFilter = ({ active, close, brands, categories, prefixUrl, defaultBr <option value=''>Pilih Brand...</option> {brands.map((brand, index) => ( <option value={brand.brand} key={index}> - {brand.brand} <span className='text-sm text-gray-200'>({brand.qty})</span> + {brand.brand}{' '} + <span className='text-sm text-gray-200'> + ({brand.qty}) + </span> </option> ))} </> @@ -125,7 +136,10 @@ const ProductFilter = ({ active, close, brands, categories, prefixUrl, defaultBr <option value=''>Pilih Kategori...</option> {categories.map((category, index) => ( <option value={category.name} key={index}> - {category.name} <span className='text-sm text-gray-200'>({category.qty})</span> + {category.name}{' '} + <span className='text-sm text-gray-200'> + ({category.qty}) + </span> </option> ))} </> @@ -141,7 +155,9 @@ const ProductFilter = ({ active, close, brands, categories, prefixUrl, defaultBr <button key={orderOption.value} className={`btn-light px-3 font-normal flex-shrink-0 ${ - order == orderOption.value ? 'bg-warning-500' : 'bg-transparent' + order == orderOption.value + ? 'bg-warning-500' + : 'bg-transparent' }`} onClick={() => setOrder(orderOption.value)} > @@ -173,13 +189,16 @@ const ProductFilter = ({ active, close, brands, categories, prefixUrl, defaultBr {priceRange.map((price, i) => ( <button key={i} - onClick={() => handlePriceFromChange(price.priceFrom, price.priceTo, i)} + onClick={() => + handlePriceFromChange(price.priceFrom, price.priceTo, i) + } className={`w-full border ${ i === activeRange ? 'border-red-600' : 'border-gray-400' } py-2 p-3 rounded-full text-sm whitespace-nowrap`} > - {formatCurrency(price.priceFrom)} - {formatCurrency(price.priceTo)} + {formatCurrency(price.priceFrom)} -{' '} + {formatCurrency(price.priceTo)} </button> ))} </div> @@ -197,12 +216,16 @@ const ProductFilter = ({ active, close, brands, categories, prefixUrl, defaultBr </Checkbox> </div> </div> */} - <button type='button' className='btn-solid-red w-full mt-2' onClick={handleSubmit}> + <button + type='button' + className='btn-solid-red w-full mt-2' + onClick={handleSubmit} + > Terapkan Filter </button> </div> </BottomPopup> - ) -} + ); +}; -export default ProductFilter +export default ProductFilter; diff --git a/src/lib/product/components/ProductFilterDesktop.jsx b/src/lib/product/components/ProductFilterDesktop.jsx index 73fecab5..d2ecb4d9 100644 --- a/src/lib/product/components/ProductFilterDesktop.jsx +++ b/src/lib/product/components/ProductFilterDesktop.jsx @@ -1,7 +1,7 @@ -import { useRouter } from 'next/router' -import { useEffect, useState } from 'react' -import _ from 'lodash' -import { toQuery } from 'lodash-contrib' +import { useRouter } from 'next/router'; +import { useEffect, useState } from 'react'; +import _ from 'lodash'; +import { toQuery } from 'lodash-contrib'; import { Accordion, AccordionButton, @@ -15,110 +15,119 @@ import { InputGroup, InputLeftAddon, Stack, - VStack -} from '@chakra-ui/react' -import Image from '@/core/components/elements/Image/Image' -import { formatCurrency } from '@/core/utils/formatValue' + VStack, +} from '@chakra-ui/react'; +import Image from '@/core/components/elements/Image/Image'; +import { formatCurrency } from '@/core/utils/formatValue'; -const ProductFilterDesktop = ({ brands, categories, prefixUrl, defaultBrand = null }) => { - - - const router = useRouter() - const { query } = router - const [order, setOrder] = useState(query?.orderBy) - const [brandValues, setBrand] = useState(query?.brand?.split(',') || []) - const [categoryValues, setCategory] = useState(query?.category?.split(',') || []) - const [priceFrom, setPriceFrom] = useState(query?.priceFrom) - const [priceTo, setPriceTo] = useState(query?.priceTo) - const [stock, setStock] = useState(query?.stock) - const [activeRange, setActiveRange] = useState(null) - const [activeIndeces, setActiveIndeces] = useState([]) +const ProductFilterDesktop = ({ + brands, + categories, + prefixUrl, + defaultBrand = null, +}) => { + const router = useRouter(); + const { query } = router; + const [order, setOrder] = useState(query?.orderBy); + const [brandValues, setBrand] = useState(query?.brand?.split(',') || []); + const [categoryValues, setCategory] = useState( + query?.category?.split(',') || [] + ); + const [priceFrom, setPriceFrom] = useState(query?.priceFrom); + const [priceTo, setPriceTo] = useState(query?.priceTo); + const [stock, setStock] = useState(query?.stock); + const [activeRange, setActiveRange] = useState(null); + const [activeIndeces, setActiveIndeces] = useState([]); const priceRange = [ { priceFrom: 100000, - priceTo: 200000 + priceTo: 200000, }, { priceFrom: 200000, - priceTo: 300000 + priceTo: 300000, }, { priceFrom: 300000, - priceTo: 400000 + priceTo: 400000, }, { priceFrom: 400000, - priceTo: 500000 - } - ] + priceTo: 500000, + }, + ]; const indexRange = priceRange.findIndex((range) => { - return range.priceFrom === parseInt(priceFrom) && range.priceTo == parseInt(priceTo) - }) + return ( + range.priceFrom === parseInt(priceFrom) && + range.priceTo == parseInt(priceTo) + ); + }); const handleCategoriesChange = (event) => { - const value = event.target.value - const isChecked = event.target.checked + const value = event.target.value; + const isChecked = event.target.checked; if (isChecked) { - setCategory([...categoryValues, value]) + setCategory([...categoryValues, value]); } else { - setCategory(categoryValues.filter((val) => val !== value)) + setCategory(categoryValues.filter((val) => val !== value)); } - } + }; const handleBrandsChange = (event) => { - const value = event.target.value - const isChecked = event.target.checked + const value = event.target.value; + const isChecked = event.target.checked; if (isChecked) { - setBrand([...brandValues, value]) + setBrand([...brandValues, value]); } else { - setBrand(brandValues.filter((val) => val !== value)) + setBrand(brandValues.filter((val) => val !== value)); } - } + }; const handleReadyStockChange = (event) => { - const value = event.target.value - const isChecked = event.target.checked + const value = event.target.value; + const isChecked = event.target.checked; if (isChecked) { - setStock(value) + setStock(value); } else { - setStock(null) + setStock(null); } - } + }; const handlePriceFromChange = async (priceFromr, priceTor, index) => { - await setPriceFrom(priceFromr) - await setPriceTo(priceTor) - setActiveRange(index) - } + await setPriceFrom(priceFromr); + await setPriceTo(priceTor); + setActiveRange(index); + }; const handleSubmit = () => { let params = { + penawaran: router.query.penawaran, q: router.query.q, orderBy: order, brand: brandValues.join(','), category: categoryValues.join(','), priceFrom, priceTo, - stock: stock - } - params = _.pickBy(params, _.identity) - params = toQuery(params) + stock: stock, + }; + params = _.pickBy(params, _.identity); + params = toQuery(params); - const slug = Array.isArray(router.query.slug) ? router.query.slug[0] : router.query.slug; + const slug = Array.isArray(router.query.slug) + ? router.query.slug[0] + : router.query.slug; if (slug) { - if(prefixUrl.includes('category') || prefixUrl.includes('lob')){ - router.push(`${prefixUrl}?${params}`) - }else{ - router.push(`${prefixUrl}/${slug}?${params}`) + if (prefixUrl.includes('category') || prefixUrl.includes('lob')) { + router.push(`${prefixUrl}?${params}`); + } else { + router.push(`${prefixUrl}/${slug}?${params}`); } } else { - router.push(`${prefixUrl}?${params}`) + router.push(`${prefixUrl}?${params}`); } - } - - + }; /*const handleIndexAccordion = async () => { if (brandValues) { @@ -136,9 +145,8 @@ const ProductFilterDesktop = ({ brands, categories, prefixUrl, defaultBrand = nu }*/ useEffect(() => { - setActiveRange(indexRange) - }, []) - + setActiveRange(indexRange); + }, []); return ( <> @@ -165,13 +173,17 @@ const ProductFilterDesktop = ({ brands, categories, prefixUrl, defaultBrand = nu > <div className='flex items-center gap-2'> <span>{brand.brand} </span> - <span className='text-sm text-gray-600'>({brand.qty})</span> + <span className='text-sm text-gray-600'> + ({brand.qty}) + </span> </div> </Checkbox> </div> )) ) : ( - <div className='flex items-center gap-2'>Brands tidak tersedia</div> + <div className='flex items-center gap-2'> + Brands tidak tersedia + </div> )} </Stack> </AccordionPanel> @@ -199,13 +211,17 @@ const ProductFilterDesktop = ({ brands, categories, prefixUrl, defaultBrand = nu > <div className='flex items-center gap-2'> <span>{category.name} </span> - <span className='text-sm text-gray-600'>({category.qty})</span> + <span className='text-sm text-gray-600'> + ({category.qty}) + </span> </div> </Checkbox> </div> )) ) : ( - <div className='flex items-center gap-2'>Kategori tidak tersedia</div> + <div className='flex items-center gap-2'> + Kategori tidak tersedia + </div> )} </Stack> </AccordionPanel> @@ -243,13 +259,16 @@ const ProductFilterDesktop = ({ brands, categories, prefixUrl, defaultBrand = nu {priceRange.map((price, i) => ( <button key={i} - onClick={() => handlePriceFromChange(price.priceFrom, price.priceTo, i)} + onClick={() => + handlePriceFromChange(price.priceFrom, price.priceTo, i) + } className={`w-full border ${ i === activeRange ? 'border-red-600' : 'border-gray-400' } py-2 p-3 rounded-full text-sm whitespace-nowrap`} > - {formatCurrency(price.priceFrom)} - {formatCurrency(price.priceTo)} + {formatCurrency(price.priceFrom)} -{' '} + {formatCurrency(price.priceTo)} </button> ))} </div> @@ -282,7 +301,7 @@ const ProductFilterDesktop = ({ brands, categories, prefixUrl, defaultBrand = nu Terapkan </Button> </> - ) -} + ); +}; -export default ProductFilterDesktop +export default ProductFilterDesktop; diff --git a/src/lib/product/components/ProductSearch.jsx b/src/lib/product/components/ProductSearch.jsx index 26114acf..f7b044aa 100644 --- a/src/lib/product/components/ProductSearch.jsx +++ b/src/lib/product/components/ProductSearch.jsx @@ -79,6 +79,24 @@ const ProductSearch = ({ } }, [categoryId]); + useEffect(() => { + const checkIfPenawaran = async () => { + if (router.asPath.includes('penawaran')) { + query = { + ...query, + fq: [ + `-flashsale_id_i:${router.query.penawaran}`, + `flashsale_price_f:[1 TO *]`, + ], + orderBy: 'flashsale-discount-desc', + }; + setFinalQuery(query); + setOrderBy('flashsale-discount-desc'); + } + }; + checkIfPenawaran(); + }, [router.query]); + const collectIds = (category) => { const ids = []; function recurse(cat) { @@ -337,6 +355,7 @@ const ProductSearch = ({ const handleDeleteFilter = async (source, value) => { let params = { + penawaran: router.query.penawaran, q: router.query.q, orderBy: orderBy, brand: brandValues.join(','), @@ -364,6 +383,7 @@ const ProductSearch = ({ break; case 'delete': params = { + penawaran: router.query.penawaran, q: router.query.q, orderBy: orderBy, }; diff --git a/src/pages/api/shop/search.js b/src/pages/api/shop/search.js index 6269d3ed..49a5fe69 100644 --- a/src/pages/api/shop/search.js +++ b/src/pages/api/shop/search.js @@ -23,6 +23,9 @@ export default async function handler(req, res) { let paramOrderBy = ''; switch (orderBy) { + case 'flashsale-discount-desc': + paramOrderBy += 'flashsale_discount_f DESC'; + break; case 'price-asc': paramOrderBy += 'price_tier1_v2_f ASC'; break; @@ -68,15 +71,23 @@ export default async function handler(req, res) { let checkQ = q.trim().split(/[\s\+\-\!\(\)\{\}\[\]\^"~\*\?:\\\/]+/); let newQ = escapeSolrQuery(q); - const formattedQuery = `(${newQ.split(' ').map(term => `${term}*`).join(' ') })`; - const mm = checkQ.length > 2 ? checkQ.length > 5 ? '55%' : '85%' : `${checkQ.length}`; + const formattedQuery = `(${newQ + .split(' ') + .map((term) => `${term}*`) + .join(' ')})`; + const mm = + checkQ.length > 2 + ? checkQ.length > 5 + ? '55%' + : '85%' + : `${checkQ.length}`; const filterQueries = [ '-publish_b:false', 'product_rating_f:[8 TO *]', - 'price_tier1_v2_f:[1 TO *]' + 'price_tier1_v2_f:[1 TO *]', ]; - + const fq_ = filterQueries.join('AND '); let offset = (page - 1) * limit; @@ -87,7 +98,13 @@ export default async function handler(req, res) { 'indent=true', `facet.query=${escapeSolrQuery(q)}`, `q.op=OR`, - `q=${source == 'similar' || checkQ.length < 3 ? checkQ.length < 2 ? newQ : newQ + '*' : formattedQuery }`, + `q=${ + source == 'similar' || checkQ.length < 3 + ? checkQ.length < 2 + ? newQ + : newQ + '*' + : formattedQuery + }`, `defType=edismax`, 'qf=name_s description_clean_t category_name manufacture_name_s variants_code_t variants_name_t category_id_ids default_code_s', `start=${parseInt(offset)}`, @@ -135,12 +152,14 @@ export default async function handler(req, res) { if (typeof fq === 'string') parameter.push(`fq=${encodeURIComponent(fq)}`); // Multi fq in url params if (Array.isArray(fq)) - parameter = parameter.concat(fq.map((val) => `fq=${encodeURIComponent(val)}`)); - + parameter = parameter.concat( + fq.map((val) => `fq=${encodeURIComponent(val)}`) + ); + let result = await axios( process.env.SOLR_HOST + '/solr/product/select?' + parameter.join('&') ); - + try { result.data.response.products = productMappingSolr( result.data.response.docs, |
