diff options
| author | Rafi Zadanly <zadanlyr@gmail.com> | 2024-01-04 10:05:25 +0700 |
|---|---|---|
| committer | Rafi Zadanly <zadanlyr@gmail.com> | 2024-01-04 10:05:25 +0700 |
| commit | 67398e6f10d6f7729d8f1ace7005ef13d32c5ddd (patch) | |
| tree | 7d47ad6c1a7093e595e22bcecb40016a626162f6 /src/lib/product/components/ProductSearch.jsx | |
| parent | 89f32128f37d99b490de7590e2116a9cfd853f89 (diff) | |
Update promotion program feature
Diffstat (limited to 'src/lib/product/components/ProductSearch.jsx')
| -rw-r--r-- | src/lib/product/components/ProductSearch.jsx | 379 |
1 files changed, 225 insertions, 154 deletions
diff --git a/src/lib/product/components/ProductSearch.jsx b/src/lib/product/components/ProductSearch.jsx index 29bb987e..e2b961f1 100644 --- a/src/lib/product/components/ProductSearch.jsx +++ b/src/lib/product/components/ProductSearch.jsx @@ -1,124 +1,141 @@ -import { useEffect, useMemo, useState } from 'react' -import useProductSearch from '../hooks/useProductSearch' -import ProductCard from './ProductCard' -import Pagination from '@/core/components/elements/Pagination/Pagination' -import { toQuery } from 'lodash-contrib' -import _ from 'lodash' -import ProductSearchSkeleton from './Skeleton/ProductSearchSkeleton' -import ProductFilter from './ProductFilter' -import useActive from '@/core/hooks/useActive' -import MobileView from '@/core/components/views/MobileView' -import DesktopView from '@/core/components/views/DesktopView' -import NextImage from 'next/image' -import ProductFilterDesktop from './ProductFilterDesktop' -import { useRouter } from 'next/router' -import searchSpellApi from '@/core/api/searchSpellApi' -import Link from '@/core/components/elements/Link/Link' -import whatsappUrl from '@/core/utils/whatsappUrl' -import { HStack, Image, Tag, TagCloseButton, TagLabel } from '@chakra-ui/react' -import odooApi from '@/core/api/odooApi' -import { formatCurrency } from '@/core/utils/formatValue' -import axios from 'axios' -import Skeleton from 'react-loading-skeleton' -import { createSlug } from '@/core/utils/slug' - -const ProductSearch = ({ query, prefixUrl, defaultBrand = null, brand = null }) => { - const router = useRouter() - const { page = 1 } = query - const [q, setQ] = useState(query?.q || '*') - const [search, setSearch] = useState(query?.q || '*') - const [limit, setLimit] = useState(query?.limit || 30) - const [orderBy, setOrderBy] = useState(router.query?.orderBy || 'popular') - if (defaultBrand) query.brand = defaultBrand.toLowerCase() +import { useEffect, useMemo, useState } from 'react'; +import useProductSearch from '../hooks/useProductSearch'; +import ProductCard from './ProductCard'; +import Pagination from '@/core/components/elements/Pagination/Pagination'; +import { toQuery } from 'lodash-contrib'; +import _ from 'lodash'; +import ProductSearchSkeleton from './Skeleton/ProductSearchSkeleton'; +import ProductFilter from './ProductFilter'; +import useActive from '@/core/hooks/useActive'; +import MobileView from '@/core/components/views/MobileView'; +import DesktopView from '@/core/components/views/DesktopView'; +import NextImage from 'next/image'; +import ProductFilterDesktop from './ProductFilterDesktop'; +import { useRouter } from 'next/router'; +import searchSpellApi from '@/core/api/searchSpellApi'; +import Link from '@/core/components/elements/Link/Link'; +import whatsappUrl from '@/core/utils/whatsappUrl'; +import { HStack, Image, Tag, TagCloseButton, TagLabel } from '@chakra-ui/react'; +import odooApi from '@/core/api/odooApi'; +import { formatCurrency } from '@/core/utils/formatValue'; +import axios from 'axios'; +import { createSlug } from '@/core/utils/slug'; + +const ProductSearch = ({ + query, + prefixUrl, + defaultBrand = null, + brand = null, +}) => { + const router = useRouter(); + const { page = 1 } = query; + const [q, setQ] = useState(query?.q || '*'); + const [search, setSearch] = 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, orderBy }, - operation: 'AND' - }) - const [products, setProducts] = useState(null) - const [spellings, setSpellings] = useState(null) - const [bannerPromotionHeader, setBannerPromotionHeader] = useState(null) - const [bannerPromotionFooter, setBannerPromotionFooter] = useState(null) - const [isBrand, setIsBrand] = useState(null) - const popup = useActive() - const numRows = [30, 50, 80, 100] + operation: 'AND', + }); + const [products, setProducts] = useState(null); + const [spellings, setSpellings] = useState(null); + const [bannerPromotionHeader, setBannerPromotionHeader] = useState(null); + const [bannerPromotionFooter, setBannerPromotionFooter] = useState(null); + const [isBrand, setIsBrand] = useState(null); + const popup = useActive(); + const numRows = [30, 50, 80, 100]; const [brandValues, setBrand] = useState( - !router.pathname.includes('brands') ? (query.brand ? query.brand.split(',') : []) : [] - ) - const [categoryValues, setCategory] = useState(query?.category?.split(',') || []) - const [priceFrom, setPriceFrom] = useState(query?.priceFrom || null) - const [priceTo, setPriceTo] = useState(query?.priceTo || null) - - const pageCount = Math.ceil(productSearch.data?.response.numFound / limit) - const productStart = productSearch.data?.responseHeader.params.start - const productRows = limit - const productFound = productSearch.data?.response.numFound + !router.pathname.includes('brands') + ? query.brand + ? query.brand.split(',') + : [] + : [] + ); + const [categoryValues, setCategory] = useState( + query?.category?.split(',') || [] + ); + const [priceFrom, setPriceFrom] = useState(query?.priceFrom || null); + const [priceTo, setPriceTo] = useState(query?.priceTo || null); + + const pageCount = Math.ceil(productSearch.data?.response.numFound / limit); + const productStart = productSearch.data?.responseHeader.params.start; + const productRows = limit; + const productFound = productSearch.data?.response.numFound; useEffect(() => { if (productFound == 0 && query.q && !spellings) { searchSpellApi({ query: query.q }).then((response) => { const oddIndexSuggestions = response.data.spellcheck.suggestions.filter( (_, index) => index % 2 === 1 - ) + ); const oddIndexCollations = response.data.spellcheck.collations.filter( (_, index) => index % 2 === 1 - ) + ); const dataSpellings = oddIndexSuggestions.reduce((acc, curr) => { oddIndexCollations.forEach((collation) => { - acc.push(collation.collationQuery) - }) + acc.push(collation.collationQuery); + }); curr.suggestion.forEach((s) => { - if (!acc.includes(s.word)) acc.push(s.word) - }) - return acc - }, []) + if (!acc.includes(s.word)) acc.push(s.word); + }); + return acc; + }, []); if (dataSpellings.length > 0) { - setQ(dataSpellings[0]) + setQ(dataSpellings[0]); } - setSpellings(dataSpellings) - }) + setSpellings(dataSpellings); + }); } - }, [productFound, query, spellings]) + }, [productFound, query, spellings]); useEffect(() => { const checkIfBrand = async () => { const brand = await axios( `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/brands?params=search&q=${search}` - ) - console.log('ini brand', brand) + ); + if (brand.data.length > 0) { - setIsBrand(brand?.data[0]) + setIsBrand(brand?.data[0]); } else { - setIsBrand(null) + setIsBrand(null); } - } + }; if (router.pathname.includes('search')) { - checkIfBrand() + checkIfBrand(); } - }, [q]) + }, [q]); - const brands = [] + const brands = []; for ( let i = 0; i < productSearch.data?.facetCounts?.facetFields?.manufactureNameS.length; i += 2 ) { - const brand = productSearch.data?.facetCounts?.facetFields?.manufactureNameS[i] - const qty = productSearch.data?.facetCounts?.facetFields?.manufactureNameS[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 }) + brands.push({ brand, qty }); } } - const categories = [] - for (let i = 0; i < productSearch.data?.facetCounts?.facetFields?.categoryName.length; i += 2) { - const name = productSearch.data?.facetCounts?.facetFields?.categoryName[i] - const qty = productSearch.data?.facetCounts?.facetFields?.categoryName[i + 1] + const categories = []; + for ( + let i = 0; + i < productSearch.data?.facetCounts?.facetFields?.categoryName.length; + i += 2 + ) { + const name = productSearch.data?.facetCounts?.facetFields?.categoryName[i]; + const qty = + productSearch.data?.facetCounts?.facetFields?.categoryName[i + 1]; if (qty > 0) { - categories.push({ name, qty }) + categories.push({ name, qty }); } } @@ -126,46 +143,54 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null, brand = null }) { 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 handleOrderBy = (e) => { let params = { ...router.query, - orderBy: e.target.value - } - params = _.pickBy(params, _.identity) - params = toQuery(params) - router.push(`${prefixUrl}?${params}`) - } + orderBy: e.target.value, + }; + params = _.pickBy(params, _.identity); + params = toQuery(params); + router.push(`${prefixUrl}?${params}`); + }; const handleLimit = (e) => { let params = { ...router.query, - limit: e.target.value - } - params = _.pickBy(params, _.identity) - params = toQuery(params) - router.push(`${prefixUrl}?${params}`) - } + limit: e.target.value, + }; + params = _.pickBy(params, _.identity); + params = toQuery(params); + router.push(`${prefixUrl}?${params}`); + }; const getBanner = async () => { if (router.pathname.includes('search')) { - const getBannerHeader = await odooApi('GET', '/api/v1/banner?type=promotion-header') - const getBannerFooter = await odooApi('GET', '/api/v1/banner?type=promotion-footer') - var randomIndex = Math.floor(Math.random() * getBannerHeader.length) - var randomIndexFooter = Math.floor(Math.random() * getBannerFooter.length) - setBannerPromotionHeader(getBannerHeader[randomIndex]) - setBannerPromotionFooter(getBannerFooter[randomIndexFooter]) + const getBannerHeader = await odooApi( + 'GET', + '/api/v1/banner?type=promotion-header' + ); + const getBannerFooter = await odooApi( + 'GET', + '/api/v1/banner?type=promotion-footer' + ); + var randomIndex = Math.floor(Math.random() * getBannerHeader.length); + var randomIndexFooter = Math.floor( + Math.random() * getBannerFooter.length + ); + setBannerPromotionHeader(getBannerHeader[randomIndex]); + setBannerPromotionFooter(getBannerFooter[randomIndexFooter]); } - } + }; useEffect(() => { - getBanner() - }, []) + getBanner(); + }, []); useEffect(() => { - setProducts(productSearch.data?.response?.products) - }, [productSearch]) + setProducts(productSearch.data?.response?.products); + }, [productSearch]); const SpellingComponent = useMemo(() => { return ( @@ -182,8 +207,8 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null, brand = null }) </Link> ))} </> - ) - }, [spellings]) + ); + }, [spellings]); const handleDeleteFilter = async (source, value) => { let params = { @@ -192,41 +217,41 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null, brand = null }) brand: brandValues.join(','), category: categoryValues.join(','), priceFrom, - priceTo - } + priceTo, + }; - let brands = brandValues - let catagories = categoryValues + let brands = brandValues; + let catagories = categoryValues; switch (source) { case 'brands': - brands = brandValues.filter((item) => item !== value) - params.brand = brands.join(',') - await setBrand(brands) - break + brands = brandValues.filter((item) => item !== value); + params.brand = brands.join(','); + await setBrand(brands); + break; case 'category': - catagories = categoryValues.filter((item) => item !== value) - params.category = catagories.join(',') - await setCategory(catagories) - break + catagories = categoryValues.filter((item) => item !== value); + params.category = catagories.join(','); + await setCategory(catagories); + break; case 'price': - params.priceFrom = null - params.priceTo = null - break + params.priceFrom = null; + params.priceTo = null; + break; case 'delete': params = { q: router.query.q, - orderBy: orderBy - } - break + orderBy: orderBy, + }; + break; } - handleSubmitFilter(params) - } + handleSubmitFilter(params); + }; const handleSubmitFilter = (params) => { - params = _.pickBy(params, _.identity) - params = toQuery(params) - router.push(`${prefixUrl}?${params}`) - } + params = _.pickBy(params, _.identity); + params = toQuery(params); + router.push(`${prefixUrl}?${params}`); + }; return ( <> @@ -235,8 +260,14 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null, brand = null }) <div className='p-4 pt-0'> {isBrand && isBrand.logo && ( <div className='mb-3'> - <h1 className='mb-2 font-semibold text-h-sm'>Brand Pencarian {q}</h1> - <Image src={isBrand?.logo} alt='' className='object-cover object-center h-[60px]' /> + <h1 className='mb-2 font-semibold text-h-sm'> + Brand Pencarian {q} + </h1> + <Image + src={isBrand?.logo} + alt='' + className='object-cover object-center h-[60px]' + /> </div> )} <h1 className='mb-2 font-semibold text-h-sm'>Produk</h1> @@ -255,7 +286,8 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null, brand = null }) {pageCount > 1 ? ( <> {productStart + 1}- - {parseInt(productStart) + parseInt(productRows) > productFound + {parseInt(productStart) + parseInt(productRows) > + productFound ? productFound : parseInt(productStart) + parseInt(productRows)} dari @@ -267,7 +299,8 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null, brand = null }) produk{' '} {query.q && ( <> - untuk pencarian <span className='font-semibold'>{query.q}</span> + untuk pencarian{' '} + <span className='font-semibold'>{query.q}</span> </> )} </> @@ -279,7 +312,10 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null, brand = null }) {productFound > 0 && ( <div className='flex items-center gap-x-2 mb-5 justify-between'> <div> - <button className='btn-light py-2 px-5 h-[40px]' onClick={popup.activate}> + <button + className='btn-light py-2 px-5 h-[40px]' + onClick={popup.activate} + > Filter </button> </div> @@ -303,7 +339,9 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null, brand = null }) <div className='grid grid-cols-2 gap-3'> {products && - products.map((product) => <ProductCard product={product} key={product.id} />)} + products.map((product) => ( + <ProductCard product={product} key={product.id} /> + ))} </div> <Pagination @@ -329,7 +367,9 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null, brand = null }) <div className='w-3/12'> {brand && ( <div className='p-4'> - <div className='text-caption-1 text-gray_r-11 mb-2'>Produk dari brand:</div> + <div className='text-caption-1 text-gray_r-11 mb-2'> + Produk dari brand: + </div> {brand?.data?.logo && ( <Image src={brand?.data?.logo} @@ -365,12 +405,18 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null, brand = null }) {isBrand && isBrand.logo && ( <div className='mb-3'> - <h1 className='text-2xl mb-2 font-semibold'>Brand Pencarian {q}</h1> + <h1 className='text-2xl mb-2 font-semibold'> + Brand Pencarian {q} + </h1> <Link href={createSlug('/shop/brands/', isBrand.name, isBrand.id)} className='inline' > - <Image src={isBrand?.logo} alt='' className='object-cover object-center h-24' /> + <Image + src={isBrand?.logo} + alt='' + className='object-cover object-center h-24' + /> </Link> </div> )} @@ -391,7 +437,8 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null, brand = null }) {pageCount > 1 ? ( <> {productStart + 1}- - {parseInt(productStart) + parseInt(productRows) > productFound + {parseInt(productStart) + parseInt(productRows) > + productFound ? productFound : parseInt(productStart) + parseInt(productRows)} dari @@ -403,7 +450,8 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null, brand = null }) produk{' '} {query.q && ( <> - untuk pencarian <span className='font-semibold'>{query.q}</span> + untuk pencarian{' '} + <span className='font-semibold'>{query.q}</span> </> )} </> @@ -447,7 +495,9 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null, brand = null }) {productSearch.isLoading && <ProductSearchSkeleton />} <div className='grid grid-cols-5 gap-x-3 gap-y-6'> {products && - products.map((product) => <ProductCard product={product} key={product.id} />)} + products.map((product) => ( + <ProductCard product={product} key={product.id} /> + ))} </div> <div className='flex justify-between items-center mt-6 mb-2'> <div className='pt-2 pb-6 flex items-center gap-x-3'> @@ -464,7 +514,7 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null, brand = null }) href={ query?.q ? whatsappUrl('productSearch', { - name: query.q + name: query.q, }) : whatsappUrl() } @@ -496,40 +546,61 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null, brand = null }) </div> </DesktopView> </> - ) -} + ); +}; -export default ProductSearch +export default ProductSearch; const FilterChoicesComponent = ({ brandValues, categoryValues, priceFrom, priceTo, - handleDeleteFilter + handleDeleteFilter, }) => ( <div className='flex items-center'> <HStack spacing={2} className='flex-wrap'> {brandValues?.map((value, index) => ( - <Tag size='lg' key={index} borderRadius='lg' variant='outline' colorScheme='gray'> + <Tag + size='lg' + key={index} + borderRadius='lg' + variant='outline' + colorScheme='gray' + > <TagLabel>{value}</TagLabel> <TagCloseButton onClick={() => handleDeleteFilter('brands', value)} /> </Tag> ))} {categoryValues?.map((value, index) => ( - <Tag size='lg' key={index} borderRadius='lg' variant='outline' colorScheme='gray'> + <Tag + size='lg' + key={index} + borderRadius='lg' + variant='outline' + colorScheme='gray' + > <TagLabel>{value}</TagLabel> - <TagCloseButton onClick={() => handleDeleteFilter('category', value)} /> + <TagCloseButton + onClick={() => handleDeleteFilter('category', value)} + /> </Tag> ))} {priceFrom && priceTo && ( <Tag size='lg' borderRadius='lg' variant='outline' colorScheme='gray'> - <TagLabel>{formatCurrency(priceFrom) + '-' + formatCurrency(priceTo)}</TagLabel> - <TagCloseButton onClick={() => handleDeleteFilter('price', priceFrom)} /> + <TagLabel> + {formatCurrency(priceFrom) + '-' + formatCurrency(priceTo)} + </TagLabel> + <TagCloseButton + onClick={() => handleDeleteFilter('price', priceFrom)} + /> </Tag> )} - {brandValues?.length > 0 || categoryValues?.length > 0 || priceFrom || priceTo ? ( + {brandValues?.length > 0 || + categoryValues?.length > 0 || + priceFrom || + priceTo ? ( <span> <button className='btn-transparent py-2 px-5 h-[40px] text-red-700' @@ -543,4 +614,4 @@ const FilterChoicesComponent = ({ )} </HStack> </div> -) +); |
