diff options
| -rw-r--r-- | src/core/components/elements/Product/cartProductsList.jsx | 5 | ||||
| -rw-r--r-- | src/lib/brand/components/Brand.jsx | 125 | ||||
| -rw-r--r-- | src/lib/brand/components/Breadcrumb.jsx | 40 | ||||
| -rw-r--r-- | src/lib/category/components/Breadcrumb.jsx | 56 | ||||
| -rw-r--r-- | src/lib/category/components/Category.jsx | 2 | ||||
| -rw-r--r-- | src/lib/checkout/components/Checkout.jsx | 79 | ||||
| -rw-r--r-- | src/lib/product/components/Product/Breadcrumb.jsx | 69 | ||||
| -rw-r--r-- | src/lib/product/components/Product/Product.jsx | 38 | ||||
| -rw-r--r-- | src/lib/product/components/Product/ProductDesktop.jsx | 73 | ||||
| -rw-r--r-- | src/lib/product/components/Product/ProductMobile.jsx | 2 | ||||
| -rw-r--r-- | src/lib/product/components/ProductSearch.jsx | 4 | ||||
| -rw-r--r-- | src/lib/promotinProgram/components/PromotionType.jsx | 2 | ||||
| -rw-r--r-- | src/lib/variant/components/VariantCard.jsx | 2 | ||||
| -rw-r--r-- | src/pages/shop/brands/[slug].jsx | 3 | ||||
| -rw-r--r-- | src/pages/shop/cart.jsx | 15 | ||||
| -rw-r--r-- | src/pages/shop/category/[slug].jsx | 4 | ||||
| -rw-r--r-- | src/pages/shop/product/[slug].jsx | 7 | ||||
| -rw-r--r-- | src/pages/shop/search.jsx | 20 |
18 files changed, 393 insertions, 153 deletions
diff --git a/src/core/components/elements/Product/cartProductsList.jsx b/src/core/components/elements/Product/cartProductsList.jsx index 32df992b..9662cd3b 100644 --- a/src/core/components/elements/Product/cartProductsList.jsx +++ b/src/core/components/elements/Product/cartProductsList.jsx @@ -15,7 +15,6 @@ const CardProdcuctsList = ({ updateQuantity = () => {}, setDeleteConfirmation = () => {} }) => { - return ( <table className='table-cart'> <thead> @@ -144,7 +143,9 @@ const CardProdcuctsList = ({ {product?.code}{' '} {product?.attributes.length > 0 ? `| ${product?.attributes.join(', ')}` : ''} </div> - <div className='text-gray_r-11 mt-2'>Berat item : {product?.weight} Kg</div> + <div className='text-gray_r-11 mt-2'> + Berat item : {product?.weight} Kg x {product?.quantity} Barang + </div> </div> </td> <td className='relative'> diff --git a/src/lib/brand/components/Brand.jsx b/src/lib/brand/components/Brand.jsx index 3c411969..4afbcb3e 100644 --- a/src/lib/brand/components/Brand.jsx +++ b/src/lib/brand/components/Brand.jsx @@ -6,10 +6,10 @@ import { Pagination, Autoplay } from 'swiper' import 'swiper/css' import 'swiper/css/pagination' import 'swiper/css/autoplay' -import Divider from '@/core/components/elements/Divider/Divider' -import ImageSkeleton from '@/core/components/elements/Skeleton/ImageSkeleton' import MobileView from '@/core/components/views/MobileView' import DesktopView from '@/core/components/views/DesktopView' +import { Skeleton } from '@chakra-ui/react' +import classNames from 'classnames' const swiperBanner = { pagination: { dynamicBullets: true }, @@ -28,65 +28,77 @@ const Brand = ({ id }) => { <MobileView> <> <div> - {brand.isLoading && <ImageSkeleton />} - {brand.data?.banners?.length == 0 && ( - <Image - src='/images/default-banner-brand.jpg' - alt='Brand - Indoteknik' - width={1024} - height={512} - className='w-full h-auto' - /> - )} - {brand.data && ( - <> - <Swiper - slidesPerView={1} - pagination={swiperBanner.pagination} - modules={swiperBanner.modules} - autoplay={swiperBanner.autoplay} - className='border-b border-gray_r-6' - > - {brand.data?.banners?.map((banner, index) => ( - <SwiperSlide key={index}> + <Skeleton + isLoaded={!brand.isLoading} + aspectRatio='4/2' + className={classNames({ + 'mb-6': brand.isLoading + })} + > + {brand.data?.banners?.length == 0 && ( + <Image + src='/images/default-banner-brand.jpg' + alt='Brand - Indoteknik' + width={1024} + height={512} + className='w-full h-auto' + /> + )} + + {brand.data && ( + <> + <Swiper + slidesPerView={1} + pagination={swiperBanner.pagination} + modules={swiperBanner.modules} + autoplay={swiperBanner.autoplay} + className='border-b border-gray_r-6' + > + {brand.data?.banners?.map((banner, index) => ( + <SwiperSlide key={index}> + <Image + src={banner} + alt={`Brand ${brand.data?.name} - Indoteknik`} + width={1024} + height={512} + className='w-full h-auto' + /> + </SwiperSlide> + ))} + </Swiper> + <div className='p-4'> + <div className='text-caption-1 text-gray_r-11 mb-2'>Produk dari brand:</div> + {brand?.data?.logo && ( <Image - src={banner} - alt={`Brand ${brand.data?.name} - Indoteknik`} - width={1024} - height={512} - className='w-full h-auto' + src={brand?.data?.logo} + alt={brand?.data?.name} + className='w-32 p-2 border borde-gray_r-6 rounded' + width={256} + height={128} /> - </SwiperSlide> - ))} - </Swiper> - <div className='p-4'> - <div className='text-caption-1 text-gray_r-11 mb-2'>Produk dari brand:</div> - {brand?.data?.logo && ( - <Image - src={brand?.data?.logo} - alt={brand?.data?.name} - className='w-32 p-2 border borde-gray_r-6 rounded' - width={256} - height={128} - /> - )} - {!brand?.data?.logo && ( - <div className='bg-danger-500 text-white text-center text-caption-1 py-2 px-4 rounded w-fit'> - {brand?.data?.name} - </div> - )} - </div> - </> - )} + )} + {!brand?.data?.logo && ( + <div className='bg-danger-500 text-white text-center text-caption-1 py-2 px-4 rounded w-fit'> + {brand?.data?.name} + </div> + )} + </div> + </> + )} + </Skeleton> </div> - <Divider /> </> </MobileView> <DesktopView> - <div className='container mx-auto mt-10 mb-3'> - <div className='min-h-[150px]'> - {brand.isLoading && <ImageSkeleton />} + <div className='container mx-auto'> + <Skeleton + isLoaded={!brand.isLoading} + aspectRatio='4/1' + className={classNames({ + 'mb-6': brand.isLoading + })} + > {brand.data?.banners?.length == 0 && ( <Image src='/images/default-banner-brand.jpg' @@ -96,6 +108,7 @@ const Brand = ({ id }) => { className='w-full h-auto' /> )} + {brand.data && ( <> <Swiper @@ -117,6 +130,7 @@ const Brand = ({ id }) => { </SwiperSlide> ))} </Swiper> + <div className='p-4'> <div className='text-caption-1 text-gray_r-11 mb-2'>Produk dari brand:</div> {brand?.data?.logo && ( @@ -136,8 +150,7 @@ const Brand = ({ id }) => { </div> </> )} - </div> - <Divider /> + </Skeleton> </div> </DesktopView> </> diff --git a/src/lib/brand/components/Breadcrumb.jsx b/src/lib/brand/components/Breadcrumb.jsx new file mode 100644 index 00000000..0fec2dad --- /dev/null +++ b/src/lib/brand/components/Breadcrumb.jsx @@ -0,0 +1,40 @@ +import { Breadcrumb as ChakraBreadcrumb, BreadcrumbItem, BreadcrumbLink } from '@chakra-ui/react' +import Link from 'next/link' +import React from 'react' + +/** + * Renders a breadcrumb component with links to navigate through different pages. + * + * @param {Object} props - The props object containing the brand name. + * @param {string} props.brandName - The name of the brand to display in the breadcrumb. + * @return {JSX.Element} The rendered breadcrumb component. + */ +const Breadcrumb = ({ brandName }) => { + return ( + <div className='container mx-auto py-4 md:py-6'> + <ChakraBreadcrumb> + <BreadcrumbItem> + <BreadcrumbLink as={Link} href='/' className='!text-danger-500 whitespace-nowrap'> + Home + </BreadcrumbLink> + </BreadcrumbItem> + + <BreadcrumbItem> + <BreadcrumbLink + as={Link} + href='/shop/brands' + className='!text-danger-500 whitespace-nowrap' + > + Brands + </BreadcrumbLink> + </BreadcrumbItem> + + <BreadcrumbItem isCurrentPage> + <BreadcrumbLink className='whitespace-nowrap'>{brandName}</BreadcrumbLink> + </BreadcrumbItem> + </ChakraBreadcrumb> + </div> + ) +} + +export default Breadcrumb diff --git a/src/lib/category/components/Breadcrumb.jsx b/src/lib/category/components/Breadcrumb.jsx new file mode 100644 index 00000000..127904ee --- /dev/null +++ b/src/lib/category/components/Breadcrumb.jsx @@ -0,0 +1,56 @@ +import odooApi from '@/core/api/odooApi' +import { createSlug } from '@/core/utils/slug' +import { + Breadcrumb as ChakraBreadcrumb, + BreadcrumbItem, + BreadcrumbLink, + Skeleton +} from '@chakra-ui/react' +import Link from 'next/link' +import React from 'react' +import { useQuery } from 'react-query' + +/** + * Render a breadcrumb component. + * + * @param {object} categoryId - The ID of the category. + * @return {JSX.Element} The breadcrumb component. + */ +const Breadcrumb = ({ categoryId }) => { + const breadcrumbs = useQuery( + `category-breadcrumbs/${categoryId}`, + async () => await odooApi('GET', `/api/v1/category/${categoryId}/category-breadcrumb`) + ) + + return ( + <div className='container mx-auto py-4 md:py-6'> + <Skeleton isLoaded={!breadcrumbs.isLoading} className='w-2/3'> + <ChakraBreadcrumb> + <BreadcrumbItem> + <BreadcrumbLink as={Link} href='/' className='!text-danger-500 whitespace-nowrap'> + Home + </BreadcrumbLink> + </BreadcrumbItem> + + {breadcrumbs.data?.map((category, index) => ( + <BreadcrumbItem key={index} isCurrentPage={index === breadcrumbs.data.length - 1}> + {index === breadcrumbs.data.length - 1 ? ( + <BreadcrumbLink className='whitespace-nowrap'>{category.name}</BreadcrumbLink> + ) : ( + <BreadcrumbLink + as={Link} + href={createSlug('/shop/category/', category.name, category.id)} + className='!text-danger-500 whitespace-nowrap' + > + {category.name} + </BreadcrumbLink> + )} + </BreadcrumbItem> + ))} + </ChakraBreadcrumb> + </Skeleton> + </div> + ) +} + +export default Breadcrumb diff --git a/src/lib/category/components/Category.jsx b/src/lib/category/components/Category.jsx index af696d42..e6ea5acf 100644 --- a/src/lib/category/components/Category.jsx +++ b/src/lib/category/components/Category.jsx @@ -30,7 +30,7 @@ const Category = () => { return ( <DesktopView> <div className='category-mega-box'> - {categories.map((category) => ( + {categories?.map((category) => ( <div key={category.id}> <Link href={createSlug('/shop/category/', category.name, category.id)} diff --git a/src/lib/checkout/components/Checkout.jsx b/src/lib/checkout/components/Checkout.jsx index e2c1a85b..35236e79 100644 --- a/src/lib/checkout/components/Checkout.jsx +++ b/src/lib/checkout/components/Checkout.jsx @@ -33,6 +33,8 @@ import { useQuery } from 'react-query' import { gtagPurchase } from '@/core/utils/googleTag' import { findVoucher, getVoucher } from '../api/getVoucher' import CardProdcuctsList from '@/core/components/elements/Product/cartProductsList' +import { Spinner } from '@chakra-ui/react' +import { AnimatePresence, motion } from 'framer-motion' const SELF_PICKUP_ID = 32 @@ -110,6 +112,7 @@ const Checkout = () => { const [buttonTerapkan, SetButtonTerapkan] = useState(false) const [checkoutValidation, setCheckoutValidation] = useState(false) const [loadingVoucher, setLoadingVoucher] = useState(true) + const [loadingRajaOngkir, setLoadingRajaOngkir] = useState(false) const expedisiValidation = useRef(null) @@ -211,6 +214,7 @@ const Checkout = () => { useEffect(() => { setCheckoutValidation(false) const loadServiceRajaOngkir = async () => { + setLoadingRajaOngkir(true) const body = { origin: 2127, destination: selectedAddress.shipping.rajaongkirCityId, @@ -221,6 +225,7 @@ const Checkout = () => { } setBiayaKirim(0) const dataService = await axios('/api/rajaongkir-service?body=' + JSON.stringify(body)) + setLoadingRajaOngkir(false) setListServiceExpedisi(dataService.data[0].costs) if (dataService.data[0].costs[0]) { setBiayaKirim(dataService.data[0].costs[0]?.cost[0].value) @@ -259,9 +264,8 @@ const Checkout = () => { useEffect(() => { if (selectedExpedisi) { let serviceType = selectedExpedisi.split(',') - if (serviceType[0] === 0) { - setSelectedExpedisi(0) - } + if (serviceType[0] === 0) return + setselectedCarrier(serviceType[0]) setselectedCarrierId(serviceType[1]) setListServiceExpedisi([]) @@ -723,6 +727,7 @@ const Checkout = () => { checkWeigth={checkWeigth} checkoutValidation={checkoutValidation} expedisiValidation={expedisiValidation} + loadingRajaOngkir={loadingRajaOngkir} /> <Divider /> <SectionListService @@ -980,6 +985,7 @@ const Checkout = () => { checkWeigth={checkWeigth} checkoutValidation={checkoutValidation} expedisiValidation={expedisiValidation} + loadingRajaOngkir={loadingRajaOngkir} /> <Divider /> <SectionListService @@ -990,7 +996,7 @@ const Checkout = () => { <div className='p-4'> <div className='font-medium'>Detail Pesanan</div> <CardProdcuctsList isLoading={isLoading} products={products} /> - + {/* <table className='table-checkout'> <thead> <tr> @@ -1158,7 +1164,9 @@ const Checkout = () => { <div className='sticky top-48 border border-gray_r-6 bg-white rounded p-4'> <div className='flex justify-between items-center'> <div className='font-medium'>Ringkasan Pesanan</div> - <div className='text-gray_r-11 text-caption-1'>{products?.length} Barang</div> + <div className='text-gray_r-11 text-caption-1'> + {products?.length} Barang - {cartCheckout?.totalWeight.kg} Kg + </div> </div> <hr className='my-4 border-gray_r-6' /> @@ -1403,32 +1411,51 @@ const SectionExpedisi = ({ setSelectedExpedisi, checkWeigth, checkoutValidation, - expedisiValidation + expedisiValidation, + loadingRajaOngkir }) => address?.rajaongkirCityId > 0 && ( <div className='p-4' ref={expedisiValidation}> <div className='flex justify-between items-center'> - <div className='font-medium'>Pilih Expedisi : </div> + <div className='font-medium'>Pilih Ekspedisi: </div> <div className='w-[250px]'> - <select - className={`form-input ${checkoutValidation ? 'border-red-500 shake' : ''}`} - onChange={(e) => setSelectedExpedisi(e.target.value)} - required - > - <option value='0,0'>Pilih Pengiriman</option> - <option value='1,32'>SELF PICKUP</option> - {checkWeigth != true && - listExpedisi.map((expedisi) => ( - <option - disabled={checkWeigth} - value={expedisi.label + ',' + expedisi.carrierId} - key={expedisi.value} + <div className='flex items-center gap-x-4'> + <select + className={`form-input ${checkoutValidation ? 'border-red-500 shake' : ''}`} + onChange={(e) => setSelectedExpedisi(e.target.value)} + required + > + <option value='0,0'>Pilih Pengiriman</option> + <option value='1,32'>SELF PICKUP</option> + {checkWeigth != true && + listExpedisi.map((expedisi) => ( + <option + disabled={checkWeigth} + value={expedisi.label + ',' + expedisi.carrierId} + key={expedisi.value} + > + {' '} + {expedisi.label.toUpperCase()}{' '} + </option> + ))} + </select> + + <AnimatePresence> + {loadingRajaOngkir && ( + <motion.div + initial={{ opacity: 0, width: 0 }} + animate={{ opacity: 1, width: '28px' }} + exit={{ opacity: 0, width: 0 }} + transition={{ + duration: 0.25 + }} + className='overflow-hidden' > - {' '} - {expedisi.label.toUpperCase()}{' '} - </option> - ))} - </select> + <Spinner thickness='3px' speed='0.5s' color='red.500' /> + </motion.div> + )} + </AnimatePresence> + </div> {checkoutValidation && ( <span className='text-sm text-red-500'>*silahkan pilih expedisi</span> )} @@ -1459,7 +1486,7 @@ const SectionListService = ({ listserviceExpedisi, setSelectedServiceType }) => <> <div className='p-4'> <div className='flex justify-between items-center'> - <div className='font-medium'>Service Type Expedisi : </div> + <div className='font-medium'>Tipe Layanan Ekspedisi: </div> <div> <select className='form-input' onChange={(e) => setSelectedServiceType(e.target.value)}> {listserviceExpedisi.map((service) => ( diff --git a/src/lib/product/components/Product/Breadcrumb.jsx b/src/lib/product/components/Product/Breadcrumb.jsx new file mode 100644 index 00000000..0554dba5 --- /dev/null +++ b/src/lib/product/components/Product/Breadcrumb.jsx @@ -0,0 +1,69 @@ +import odooApi from '@/core/api/odooApi' +import { createSlug } from '@/core/utils/slug' +import { + Breadcrumb as ChakraBreadcrumb, + BreadcrumbItem, + BreadcrumbLink, + Skeleton +} from '@chakra-ui/react' +import classNames from 'classnames' +import Link from 'next/link' +import { useQuery } from 'react-query' + +/** + * Renders a breadcrumb component based on the provided `productId`. + * + * @param {Object} props - The properties passed to the component. + * @param {number} props.productId - The ID of the product. + * @param {string} props.productName - The ID of the product. + * @return {ReactElement} The rendered breadcrumb component. + */ +const Breadcrumb = ({ productId, productName }) => { + const categories = useQuery( + `detail/categories/${productId}`, + async () => await odooApi('GET', `/api/v1/product/${productId}/category-breadcrumb`), + { + enabled: !!productId + } + ) + + return ( + <Skeleton + isLoaded={!categories.isLoading} + className={classNames({ + 'w-2/3': categories.isLoading, + 'w-full': !categories.isLoading + })} + > + <ChakraBreadcrumb + mb={10} + overflowX={'auto'} + className='text-caption-2 md:text-body-2 p-4 md:p-0' + > + <BreadcrumbItem> + <BreadcrumbLink as={Link} href='/' className='!text-danger-500 whitespace-nowrap'> + Home + </BreadcrumbLink> + </BreadcrumbItem> + + {categories.data?.map((category) => ( + <BreadcrumbItem key={category.id}> + <BreadcrumbLink + as={Link} + href={createSlug('/shop/category/', category.name, category.id)} + className='!text-danger-500 whitespace-nowrap' + > + {category.name} + </BreadcrumbLink> + </BreadcrumbItem> + ))} + + <BreadcrumbItem isCurrentPage> + <BreadcrumbLink className='whitespace-nowrap'>{productName}</BreadcrumbLink> + </BreadcrumbItem> + </ChakraBreadcrumb> + </Skeleton> + ) +} + +export default Breadcrumb diff --git a/src/lib/product/components/Product/Product.jsx b/src/lib/product/components/Product/Product.jsx index 54490c26..6e983c2e 100644 --- a/src/lib/product/components/Product/Product.jsx +++ b/src/lib/product/components/Product/Product.jsx @@ -36,29 +36,21 @@ const Product = ({ product, isVariant = false }) => { } }, [product, isVariant]) - if (isVariant == true) { - return ( - <> - <ProductDesktopVariant - product={product} - wishlist={wishlist} - toggleWishlist={toggleWishlist} - /> - <ProductMobileVariant - product={product} - wishlist={wishlist} - toggleWishlist={toggleWishlist} - /> - </> - ) - } else { - return ( - <> - <ProductMobile product={product} wishlist={wishlist} toggleWishlist={toggleWishlist} /> - <ProductDesktop products={product} wishlist={wishlist} toggleWishlist={toggleWishlist} /> - </> - ) - } + return isVariant == true ? ( + <> + <ProductDesktopVariant + product={product} + wishlist={wishlist} + toggleWishlist={toggleWishlist} + /> + <ProductMobileVariant product={product} wishlist={wishlist} toggleWishlist={toggleWishlist} /> + </> + ) : ( + <> + <ProductMobile product={product} wishlist={wishlist} toggleWishlist={toggleWishlist} /> + <ProductDesktop products={product} wishlist={wishlist} toggleWishlist={toggleWishlist} /> + </> + ) } export default Product diff --git a/src/lib/product/components/Product/ProductDesktop.jsx b/src/lib/product/components/Product/ProductDesktop.jsx index 47e98c1a..855c9f75 100644 --- a/src/lib/product/components/Product/ProductDesktop.jsx +++ b/src/lib/product/components/Product/ProductDesktop.jsx @@ -24,6 +24,7 @@ import ColumnsSLA from './ColumnsSLA' import { useProductCartContext } from '@/contexts/ProductCartContext' import { Box, Skeleton, Tooltip } from '@chakra-ui/react' import { Info } from 'lucide-react' +import Breadcrumb from './Breadcrumb' const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { const router = useRouter() @@ -199,47 +200,49 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { return ( <DesktopView> <div className='container mx-auto pt-10'> + <Breadcrumb productId={product.id} productName={product.name} /> <div className='flex'> <div className='w-full flex flex-wrap'> <div className='w-5/12'> <div className='relative mb-2'> - {product?.flashSale?.remainingTime > 0 && lowestPrice?.price.discountPercentage > 0 && ( - <div className={`absolute bottom-0 w-full`}> - <div className='absolute bottom-0 w-full h-full'> - <ImageNext - src={backgorundFlashSale || '/images/GAMBAR-BG-FLASH-SALE.jpg'} - width={1000} - height={100} - /> - </div> - <div className='relative'> - <div className='flex gap-x-2 items-center p-2'> - <div className='bg-yellow-400 rounded-full p-1 h-9 w-20 flex items-center justify-center '> - <span className='text-lg font-bold'> - {Math.floor(product.lowestPrice.discountPercentage)}% - </span> - </div> - <div - className={`bg-red-600 border border-solid border-yellow-400 rounded-full h-9 p-2 flex w-[50%] items-center justify-center gap-x-4`} - > - <ImageNext - src='/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg' - width={17} - height={10} - /> - <span className='text-white text-lg font-semibold'> - {product?.flashSale?.tag != 'false' || product?.flashSale?.tag - ? product?.flashSale?.tag - : 'FLASH SALE'} - </span> - </div> - <div> - <CountDown2 initialTime={product.flashSale.remainingTime} /> + {product?.flashSale?.remainingTime > 0 && + lowestPrice?.price.discountPercentage > 0 && ( + <div className={`absolute bottom-0 w-full`}> + <div className='absolute bottom-0 w-full h-full'> + <ImageNext + src={backgorundFlashSale || '/images/GAMBAR-BG-FLASH-SALE.jpg'} + width={1000} + height={100} + /> + </div> + <div className='relative'> + <div className='flex gap-x-2 items-center p-2'> + <div className='bg-yellow-400 rounded-full p-1 h-9 w-20 flex items-center justify-center '> + <span className='text-lg font-bold'> + {Math.floor(product.lowestPrice.discountPercentage)}% + </span> + </div> + <div + className={`bg-red-600 border border-solid border-yellow-400 rounded-full h-9 p-2 flex w-[50%] items-center justify-center gap-x-4`} + > + <ImageNext + src='/images/ICON_FLASH_SALE_WEBSITE_INDOTEKNIK.svg' + width={17} + height={10} + /> + <span className='text-white text-lg font-semibold'> + {product?.flashSale?.tag != 'false' || product?.flashSale?.tag + ? product?.flashSale?.tag + : 'FLASH SALE'} + </span> + </div> + <div> + <CountDown2 initialTime={product.flashSale.remainingTime} /> + </div> </div> </div> </div> - </div> - )} + )} <Image src={product.image} alt={product.name} @@ -390,7 +393,7 @@ const ProductDesktop = ({ products, wishlist, toggleWishlist }) => { ))} </div> <div className='flex'> - <div className='w-3/4 leading-7 product__description'> + <div className='w-3/4 leading-8 product__description'> <TabContent active={informationTab == 'description'}> <span dangerouslySetInnerHTML={{ diff --git a/src/lib/product/components/Product/ProductMobile.jsx b/src/lib/product/components/Product/ProductMobile.jsx index ffa75f72..402490f7 100644 --- a/src/lib/product/components/Product/ProductMobile.jsx +++ b/src/lib/product/components/Product/ProductMobile.jsx @@ -19,6 +19,7 @@ import { gtagAddToCart } from '@/core/utils/googleTag' import odooApi from '@/core/api/odooApi' import ImageNext from 'next/image' import CountDown2 from '@/core/components/elements/CountDown/CountDown2' +import Breadcrumb from './Breadcrumb' const ProductMobile = ({ product, wishlist, toggleWishlist }) => { const router = useRouter() @@ -160,6 +161,7 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => { return ( <MobileView> + <Breadcrumb productId={product.id} productName={product.name} /> <div className='relative'> {product?.flashSale?.remainingTime > 0 && activeVariant?.price.discountPercentage > 0 && ( <div className={`absolute bottom-0 w-full`}> diff --git a/src/lib/product/components/ProductSearch.jsx b/src/lib/product/components/ProductSearch.jsx index 1072b2ae..3c954548 100644 --- a/src/lib/product/components/ProductSearch.jsx +++ b/src/lib/product/components/ProductSearch.jsx @@ -158,7 +158,7 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null }) => { <> <MobileView> {productSearch.isLoading && <ProductSearchSkeleton />} - <div className='p-4'> + <div className='p-4 pt-0'> <h1 className='mb-2 font-semibold text-h-sm'>Produk</h1> <div className='mb-2 leading-6 text-gray_r-11'> @@ -238,7 +238,7 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null }) => { </MobileView> <DesktopView> - <div className='container mx-auto mt-10 flex mb-3'> + <div className='container mx-auto flex mb-3'> <div className='w-3/12'> <ProductFilterDesktop brands={brands || []} diff --git a/src/lib/promotinProgram/components/PromotionType.jsx b/src/lib/promotinProgram/components/PromotionType.jsx index ad7185e3..51f2622a 100644 --- a/src/lib/promotinProgram/components/PromotionType.jsx +++ b/src/lib/promotinProgram/components/PromotionType.jsx @@ -24,7 +24,7 @@ const PromotionType = ({ const id = variantId const listProgram = async () => { const programs = await getPromotionProgram({ id }) - if (programs.length > 0) { + if (programs?.length > 0) { setPromotionList(programs) setActiveTitle(programs?.[0].type.value) } diff --git a/src/lib/variant/components/VariantCard.jsx b/src/lib/variant/components/VariantCard.jsx index 64b6d146..6c0ab974 100644 --- a/src/lib/variant/components/VariantCard.jsx +++ b/src/lib/variant/components/VariantCard.jsx @@ -40,7 +40,7 @@ const VariantCard = ({ product, openOnClick = true, buyMore = false }) => { {product.attributes.length > 0 ? ` ・ ${product.attributes.join(', ')}` : ''} </p> <p className='text-caption-2 text-gray_r-11 mt-1'> - Berat Item : {product?.weight} Kg + Berat Item : {product?.weight} Kg x {product?.quantity} Barang </p> <div className='flex flex-wrap gap-x-1 items-center mt-auto'> {product.hasFlashsale && ( diff --git a/src/pages/shop/brands/[slug].jsx b/src/pages/shop/brands/[slug].jsx index 88f19bc0..d75475b7 100644 --- a/src/pages/shop/brands/[slug].jsx +++ b/src/pages/shop/brands/[slug].jsx @@ -3,6 +3,7 @@ import { getIdFromSlug, getNameFromSlug } from '@/core/utils/slug' import { useRouter } from 'next/router' import _ from 'lodash' import Seo from '@/core/components/Seo' +import Breadcrumb from '@/lib/brand/components/Breadcrumb' const BasicLayout = dynamic(() => import('@/core/components/layouts/BasicLayout')) const ProductSearch = dynamic(() => import('@/lib/product/components/ProductSearch')) @@ -26,6 +27,8 @@ export default function BrandDetail() { ]} /> + <Breadcrumb brandName={brandName} /> + <Brand id={getIdFromSlug(slug)} /> {!_.isEmpty(router.query) && ( <ProductSearch diff --git a/src/pages/shop/cart.jsx b/src/pages/shop/cart.jsx index a7f2037b..2da58c96 100644 --- a/src/pages/shop/cart.jsx +++ b/src/pages/shop/cart.jsx @@ -3,7 +3,9 @@ import BasicLayout from '@/core/components/layouts/BasicLayout' import DesktopView from '@/core/components/views/DesktopView' import MobileView from '@/core/components/views/MobileView' import IsAuth from '@/lib/auth/components/IsAuth' +import { Breadcrumb, BreadcrumbItem, BreadcrumbLink } from '@chakra-ui/react' import dynamic from 'next/dynamic' +import Link from 'next/link' const AppLayout = dynamic(() => import('@/core/components/layouts/AppLayout')) const CartComponent = dynamic(() => import('@/lib/cart/components/Cart')) @@ -22,6 +24,19 @@ export default function Cart() { <DesktopView> <BasicLayout> + <div className='container mx-auto py-4 md:py-6 pb-0'> + <Breadcrumb> + <BreadcrumbItem> + <BreadcrumbLink as={Link} href='/' className='!text-danger-500 whitespace-nowrap'> + Home + </BreadcrumbLink> + </BreadcrumbItem> + + <BreadcrumbItem isCurrentPage> + <BreadcrumbLink className='whitespace-nowrap'>Keranjang</BreadcrumbLink> + </BreadcrumbItem> + </Breadcrumb> + </div> <CartComponent /> </BasicLayout> </DesktopView> diff --git a/src/pages/shop/category/[slug].jsx b/src/pages/shop/category/[slug].jsx index dbc17c06..e3650235 100644 --- a/src/pages/shop/category/[slug].jsx +++ b/src/pages/shop/category/[slug].jsx @@ -3,6 +3,7 @@ import { getIdFromSlug, getNameFromSlug } from '@/core/utils/slug' import { useRouter } from 'next/router' import _ from 'lodash' import Seo from '@/core/components/Seo' +import Breadcrumb from '@/lib/category/components/Breadcrumb' const BasicLayout = dynamic(() => import('@/core/components/layouts/BasicLayout')) const ProductSearch = dynamic(() => import('@/lib/product/components/ProductSearch')) @@ -16,6 +17,7 @@ export default function CategoryDetail() { const query = { fq: `manufacture_id_i:${categoryId}` } + return ( <BasicLayout> <Seo @@ -29,6 +31,8 @@ export default function CategoryDetail() { ]} /> + <Breadcrumb categoryId={categoryId} /> + {!_.isEmpty(router.query) && ( <ProductSearch query={query} prefixUrl={`/shop/category/${slug}`} /> )} diff --git a/src/pages/shop/product/[slug].jsx b/src/pages/shop/product/[slug].jsx index 63fb2e7e..4c17ab37 100644 --- a/src/pages/shop/product/[slug].jsx +++ b/src/pages/shop/product/[slug].jsx @@ -29,12 +29,6 @@ export async function getServerSideProps(context) { // let productSolr = null if (product?.length == 1) { product = product[0] - const regexHtmlTags = /(<([^>]+)>)/gi - const regexHtmlTagsExceptP = /<\/?(?!p\b)[^>]*>/g - product.description = product.description - .replace(regexHtmlTagsExceptP, ' ') - .replace(regexHtmlTags, ' ') - .trim() } else { product = null } @@ -45,6 +39,7 @@ export async function getServerSideProps(context) { } export default function ProductDetail({ product }) { + console.log(product); const router = useRouter() const { setProduct } = useProductContext() diff --git a/src/pages/shop/search.jsx b/src/pages/shop/search.jsx index 907465b7..db3b449a 100644 --- a/src/pages/shop/search.jsx +++ b/src/pages/shop/search.jsx @@ -2,6 +2,8 @@ import dynamic from 'next/dynamic' import { useRouter } from 'next/router' import _ from 'lodash-contrib' import Seo from '@/core/components/Seo' +import { Breadcrumb, BreadcrumbItem, BreadcrumbLink } from '@chakra-ui/react' +import Link from 'next/link' const BasicLayout = dynamic(() => import('@/core/components/layouts/BasicLayout')) const ProductSearch = dynamic(() => import('@/lib/product/components/ProductSearch')) @@ -13,6 +15,24 @@ export default function Search() { <BasicLayout> <Seo title={`Cari produk ${router.query.q || ''} di Indoteknik.com`} /> + <div className='container mx-auto py-4 md:py-6'> + {router.query?.q && ( + <Breadcrumb> + <BreadcrumbItem> + <BreadcrumbLink as={Link} href='/' className='!text-danger-500 whitespace-nowrap'> + Home + </BreadcrumbLink> + </BreadcrumbItem> + + <BreadcrumbItem isCurrentPage> + <BreadcrumbLink className='whitespace-nowrap'> + Cari: {router.query.q || ''} + </BreadcrumbLink> + </BreadcrumbItem> + </Breadcrumb> + )} + </div> + {!_.isEmpty(router.query) && <ProductSearch query={router.query} prefixUrl='/shop/search' />} </BasicLayout> ) |
