summaryrefslogtreecommitdiff
path: root/src/lib/cart/components
diff options
context:
space:
mode:
authortrisusilo <tri.susilo@altama.co.id>2023-07-24 03:32:03 +0000
committertrisusilo <tri.susilo@altama.co.id>2023-07-24 03:32:03 +0000
commit4fcb73a336a6025a0ead194b317543efcd4f0e4b (patch)
tree20ea651ea2c02be2bae2c2915ca5ab7ff2f95538 /src/lib/cart/components
parentd61b3ca0213395099e4cea9ace8172d4e622fb52 (diff)
parent984f1b13b408ee9652072392d6312d609b353315 (diff)
Merged in Feature/promotion_programvaoucher (pull request #15)
Feature/promotion programvaoucher
Diffstat (limited to 'src/lib/cart/components')
-rw-r--r--src/lib/cart/components/Cart.jsx691
-rw-r--r--src/lib/cart/components/CartOld.jsx497
2 files changed, 1015 insertions, 173 deletions
diff --git a/src/lib/cart/components/Cart.jsx b/src/lib/cart/components/Cart.jsx
index c02f976f..efbcf76b 100644
--- a/src/lib/cart/components/Cart.jsx
+++ b/src/lib/cart/components/Cart.jsx
@@ -4,7 +4,7 @@ import Image from '@/core/components/elements/Image/Image'
import NextImage from 'next/image'
import currencyFormat from '@/core/utils/currencyFormat'
import { useEffect, useState } from 'react'
-import { deleteItemCart, getItemCart, updateItemCart } from '@/core/utils/cart'
+import { addCart, deleteItemCart, getCartApi, updateItemCart } from '@/core/utils/cart'
import { CheckIcon, TrashIcon } from '@heroicons/react/24/outline'
import { createSlug } from '@/core/utils/slug'
import { useRouter } from 'next/router'
@@ -17,12 +17,54 @@ import DesktopView from '@/core/components/views/DesktopView'
import ProductCard from '@/lib/product/components/ProductCard'
import productSearchApi from '@/lib/product/api/productSearchApi'
import whatsappUrl from '@/core/utils/whatsappUrl'
+import useAuth from '@/core/hooks/useAuth'
+import LogoSpinner from '@/core/components/elements/Spinner/LogoSpinner'
+import { getPromotionProgram } from '@/lib/promotinProgram/api/homepageApi'
+import PromotionType from '@/lib/promotinProgram/components/PromotionType'
import { gtagBeginCheckout } from '@/core/utils/googleTag'
const Cart = () => {
const router = useRouter()
const [products, setProducts] = useState(null)
- const { cart } = useCart({ enabled: !products })
+ const [isLoading, setIsLoading] = useState(true)
+ const auth = useAuth()
+
+ const [cart, setCart] = useState(null)
+
+ useEffect(() => {
+ if (!auth) return
+ }, [auth])
+
+ const getCart = async () => {
+ const listCart = await getCartApi()
+ setCart(listCart)
+ }
+
+ useEffect(() => {
+ getCart()
+ }, [])
+
+ useEffect(() => {
+ if (cart) {
+ const processProducts = async () => {
+ const updatedProducts = await Promise.all(
+ cart.products.map(async (product) => {
+ const id = product.id
+ const program = await getPromotionProgram({ id })
+ const isPromo = program.length > 0 ? 1 : 0
+ return {
+ ...product,
+ isPromo
+ }
+ })
+ )
+ setProducts(updatedProducts)
+ }
+ // processProducts()
+ setProducts(cart.products)
+ setIsLoading(false)
+ }
+ }, [cart])
const [totalPriceBeforeTax, setTotalPriceBeforeTax] = useState(0)
const [totalTaxAmount, setTotalTaxAmount] = useState(0)
@@ -31,21 +73,10 @@ const Cart = () => {
const [deleteConfirmation, setDeleteConfirmation] = useState(null)
const [productRecomendation, setProductRecomendation] = useState(null)
-
- useEffect(() => {
- if (cart.data && !products) {
- const productsWithQuantity = cart.data.map((product) => {
- const productInCart = getItemCart({ productId: product.id })
- if (!productInCart) return
- return {
- ...product,
- quantity: productInCart.quantity,
- selected: productInCart.selected
- }
- })
- setProducts(productsWithQuantity)
- }
- }, [cart, products])
+ const [promotionType, setPromotionType] = useState(null)
+ const [variantId, setVariantId] = useState(null)
+ const [quantity, setQuantity] = useState(null)
+ const [promotionActiveId, setPromotionActiveId] = useState(null)
useEffect(() => {
if (!products) return
@@ -55,13 +86,12 @@ const Cart = () => {
let calculateTotalDiscountAmount = 0
for (const product of products) {
if (product.quantity == '') continue
- updateItemCart({
- productId: product.id,
- quantity: product.quantity,
- selected: product.selected
- })
if (!product.selected) continue
+
+ if (product.canBuy == false) {
+ toggleSelected(product.id)
+ }
let priceBeforeTax = product.price.price / 1.11
calculateTotalPriceBeforeTax += priceBeforeTax * product.quantity
calculateTotalTaxAmount += (product.price.price - priceBeforeTax) * product.quantity
@@ -74,6 +104,24 @@ const Cart = () => {
}, [products])
useEffect(() => {
+ const updateData = () => {
+ updateItemCart({
+ productId: variantId,
+ quantity,
+ programLineId: promotionActiveId,
+ selected: true
+ }).then(() => {
+ getCart().then(() => {
+ if(promotionActiveId){
+ setPromotionType(false)
+ }
+ })
+ })
+ }
+ updateData()
+ }, [promotionActiveId])
+
+ useEffect(() => {
const LoadProductSimilar = async () => {
const randProductIndex = Math.floor(Math.random() * products.length)
const productLoad = await productSearchApi({
@@ -109,8 +157,16 @@ const Cart = () => {
quantity = value != '' && value < 1 ? 1 : value
break
}
- productsToUpdate[productIndex].quantity = quantity
+ let qty = quantity
+ productsToUpdate[productIndex].quantity = qty
+
setProducts([...productsToUpdate])
+ addCart(
+ productId,
+ qty,
+ productsToUpdate[productIndex].selected,
+ productsToUpdate[productIndex].program?.id
+ )
}
const toggleSelected = (productId) => {
@@ -118,8 +174,16 @@ const Cart = () => {
if (productIndex < 0) return
let productsToUpdate = products
- productsToUpdate[productIndex].selected = !productsToUpdate[productIndex].selected
+ let isSelected = !productsToUpdate[productIndex].selected
+ productsToUpdate[productIndex].selected = isSelected
+
setProducts([...productsToUpdate])
+ addCart(
+ productId,
+ productsToUpdate[productIndex].quantity,
+ isSelected,
+ productsToUpdate[productIndex].program?.id
+ )
}
const selectedProduct = () => {
@@ -135,6 +199,13 @@ const Cart = () => {
toast.success('Berhasil menghapus barang dari keranjang')
}
+ const handlePopUpPromo = (variantId, quantity, promoId = null) => {
+ setPromotionType(true)
+ setVariantId(variantId)
+ setQuantity(quantity)
+ setPromotionActiveId(promoId)
+ }
+
const handleCheckout = () => {
gtagBeginCheckout(products)
router.push('/shop/checkout')
@@ -173,6 +244,23 @@ const Cart = () => {
</div>
</BottomPopup>
+ <BottomPopup
+ className=' !h-[75%]'
+ title='Pakai Promo'
+ active={promotionType}
+ close={() => setPromotionType(false)}
+ >
+ <div className='flex mt-4'>
+ <PromotionType
+ isModal={true}
+ variantId={variantId}
+ setPromotionActiveId={setPromotionActiveId}
+ promotionActiveId={promotionActiveId}
+ quantity={quantity}
+ ></PromotionType>
+ </div>
+ </BottomPopup>
+
<MobileView>
<div className='pt-4'>
<div className='flex justify-between mb-4 px-4'>
@@ -181,13 +269,13 @@ const Cart = () => {
</div>
<div className='flex flex-col gap-y-4 h-screen'>
- {cart.isLoading && (
+ {isLoading && (
<div className='flex justify-center my-4'>
<Spinner className='w-6 text-gray_r-12/50 fill-gray_r-12' />
</div>
)}
- {!cart.isLoading && (!products || products?.length == 0) && (
+ {!isLoading && (!products || products?.length == 0) && (
<div className='px-4'>
<Alert className='text-center my-2' type='info'>
Keranjang belanja anda masih kosong
@@ -195,85 +283,204 @@ const Cart = () => {
</div>
)}
- {products?.map((product) => (
- <div key={product?.id} className='flex mx-4'>
- <input
- type='checkbox'
- onClick={() => toggleSelected(product.id)}
- checked={product?.selected}
- className='mr-2 accent-danger-500 w-4'
- />
-
- <Link
- href={createSlug('/shop/product/', product?.parent.name, product?.parent.id)}
- className='w-[30%] flex-shrink-0'
- >
- <Image
- src={product?.parent?.image}
- alt={product?.name}
- className='object-contain object-center border border-gray_r-6 h-40 w-full rounded-md'
- />
- </Link>
- <div className='flex-1 px-2 text-caption-2'>
- <Link
- href={createSlug('/shop/product/', product?.parent.name, product?.parent.id)}
- className='line-clamp-2 leading-6 !text-gray_r-12 font-normal'
- >
- {product?.parent?.name}
- </Link>
- <div className='text-gray_r-11 mt-1'>
- {product?.code}{' '}
- {product?.attributes.length > 0 ? `| ${product?.attributes.join(', ')}` : ''}
- </div>
- {product?.price?.discountPercentage > 0 && (
- <div className='flex gap-x-1 items-center mt-3'>
- <div className='text-gray_r-11 line-through text-caption-2'>
- {currencyFormat(product?.price?.price)}
+ {products &&
+ products?.map((product) => (
+ <>
+ {product.hasProgram > 0 && (
+ <div className='flex gap-x-2 bg-yellow-100 p-2 items-center'>
+ {product.program ? (
+ <>
+ <div className='border border-solid border-red-600 rounded-md p-1 text-caption-2 whitespace-nowrap'>
+ <span className='text-red-600'>{product.program.type.label}</span>
+ </div>
+ <div className='text-caption-2'>
+ {product.program.type.value == 'merchandise' ? (
+ <>Selamat anda mendapatkan merchandise indoteknik.com</>
+ ) : (
+ <>
+ Selamat! Pembelian anda hemat
+ <span className='text-red-600 font-semibold text-caption-2'>
+ {' '}
+ {currencyFormat(product.program?.totalSavings)}
+ </span>
+ </>
+ )}
+ </div>
+ </>
+ ) : (
+ <>
+ <div className='flex border border-solid border-red-600 rounded-md p-1'>
+ <span className='text-red-600'>Promo</span>
+ </div>
+ <div className='flex'>Pilih Promo Yang Tersedia Bisa lebih Hemat</div>
+ </>
+ )}
+ <div
+ onClick={() =>
+ handlePopUpPromo(product.id, product.quantity, product.program?.id)
+ }
+ className='ml-auto text-red-500 flex gap-x-0 cursor-pointer'
+ >
+ <div className='font-semibold text-caption-2 whitespace-nowrap'>
+ {' '}
+ Cek Promo
+ </div>
+ <div>
+ <svg
+ aria-hidden='true'
+ fill='none'
+ stroke='currentColor'
+ stroke-width='1.5'
+ viewBox='0 0 20 24'
+ className='h-5 w-5 font-semibold'
+ >
+ <path
+ d='M8.25 4.5l7.5 7.5-7.5 7.5'
+ stroke-linecap='round'
+ stroke-linejoin='round'
+ ></path>
+ </svg>
+ </div>
</div>
- <div className='badge-solid-red'>{product?.price?.discountPercentage}%</div>
</div>
)}
- <div className='font-normal mt-1'>
- {currencyFormat(product?.price?.priceDiscount)}
- </div>
- <div className='flex justify-between items-center mt-1'>
- <div className='text-danger-500 font-medium'>
- {currencyFormat(product?.price?.priceDiscount * product?.quantity)}
- </div>
- <div className='flex gap-x-1'>
- <button
- type='button'
- className='btn-light px-2 py-1'
- onClick={() => updateQuantity(1, product?.id, 'MINUS')}
- disabled={product?.quantity == 1}
- >
- -
- </button>
- <input
- className='form-input w-6 border-0 border-b rounded-none py-1 px-0 text-center'
- type='number'
- value={product?.quantity}
- onChange={(e) => updateQuantity(e.target.value, product?.id)}
- onBlur={(e) => updateQuantity(e.target.value, product?.id, 'BLUR')}
+ <div key={product?.id} className='flex mx-4 relative'>
+ <ComponentCanBuy canBuy={product.canBuy} />
+ <input
+ type='checkbox'
+ onClick={() => toggleSelected(product.id)}
+ checked={product?.selected}
+ className='mr-2 accent-danger-500 w-4'
+ />
+
+ <Link
+ href={createSlug('/shop/product/', product?.parent.name, product?.parent.id)}
+ className='w-[30%] flex-shrink-0'
+ >
+ <Image
+ src={product?.parent?.image}
+ alt={product?.name}
+ className='object-contain object-center border border-gray_r-6 h-40 w-full rounded-md'
/>
- <button
- type='button'
- className='btn-light px-2 py-1'
- onClick={() => updateQuantity(1, product?.id, 'PLUS')}
- >
- +
- </button>
- <button
- className='btn-red p-1 ml-1'
- onClick={() => setDeleteConfirmation(product)}
+ </Link>
+ <div className='flex-1 px-2 text-caption-2'>
+ <Link
+ href={createSlug(
+ '/shop/product/',
+ product?.parent.name,
+ product?.parent.id
+ )}
+ className='line-clamp-2 leading-6 !text-gray_r-12 font-normal'
>
- <TrashIcon className='w-4' />
- </button>
+ {product?.parent?.name}
+ </Link>
+ <div className='text-gray_r-11 mt-1'>
+ {product?.code}{' '}
+ {product?.attributes.length > 0
+ ? `| ${product?.attributes.join(', ')}`
+ : ''}
+ </div>
+ {product?.price?.discountPercentage > 0 && (
+ <div className='flex gap-x-1 items-center mt-3'>
+ <div className='text-gray_r-11 line-through text-caption-2'>
+ {currencyFormat(product?.price?.price)}
+ </div>
+ <div className='badge-solid-red'>
+ {product?.price?.discountPercentage}%
+ </div>
+ </div>
+ )}
+ <div className='font-normal mt-1'>
+ {currencyFormat(product?.price?.priceDiscount)}
+ </div>
+ <div className='flex justify-between items-center mt-1'>
+ <div className='text-danger-500 font-medium'>
+ {currencyFormat(product?.price?.priceDiscount * product?.quantity)}
+ </div>
+ <div className='flex gap-x-1'>
+ <button
+ type='button'
+ className='btn-light px-2 py-1'
+ onClick={() => updateQuantity(1, product?.id, 'MINUS')}
+ disabled={product?.quantity == 1}
+ >
+ -
+ </button>
+ <input
+ className='form-input w-6 border-0 border-b rounded-none py-1 px-0 text-center'
+ type='number'
+ value={product?.quantity}
+ onChange={(e) => updateQuantity(e.target.value, product?.id)}
+ onBlur={(e) => updateQuantity(e.target.value, product?.id, 'BLUR')}
+ />
+ <button
+ type='button'
+ className='btn-light px-2 py-1'
+ onClick={() => updateQuantity(1, product?.id, 'PLUS')}
+ >
+ +
+ </button>
+ <button
+ className='btn-red p-1 ml-1'
+ onClick={() => setDeleteConfirmation(product)}
+ >
+ <TrashIcon className='w-4' />
+ </button>
+ </div>
+ </div>
</div>
</div>
- </div>
- </div>
- ))}
+ {product.program &&
+ product.program.items &&
+ product.program.items.map((item) => (
+ <div key={item.id} className='flex mx-4 relative'>
+ <ComponentCanBuy canBuy={product.canBuy} />
+ <input className='mr-2 accent-danger-500 w-4' />
+ <Link
+ href={createSlug(
+ '/shop/product/',
+ product?.parent.name,
+ product?.parent.id
+ )}
+ className='w-[30%] flex-shrink-0'
+ >
+ <Image
+ src={item?.parent?.image}
+ alt={item?.name}
+ className='object-contain object-center border border-gray_r-6 h-40 w-full rounded-md'
+ />
+ </Link>
+ <div className='flex-1 px-2 text-caption-2'>
+ <Link
+ href={createSlug(
+ '/shop/product/',
+ product?.parent.name,
+ product?.parent.id
+ )}
+ className='line-clamp-2 leading-6 !text-gray_r-12 font-normal'
+ >
+ <div className='px-2 text-left mt-2'>
+ <div className='mb-2'>
+ <span className='border border-solid border-red-600 rounded-md p-1 text-red-600'>
+ {product.program.type.label}
+ </span>
+ </div>
+ <div>{item.name}</div>
+ </div>
+ </Link>
+ {item.price?.discountPercentage > 0 && (
+ <div className='flex gap-x-1 items-center mt-3 ml-3'>
+ <div className='text-gray_r-11 line-through text-caption-2'>
+ {currencyFormat(item?.price?.price)}
+ </div>
+ </div>
+ )}
+ <div className='text-danger-500 font-medium mt-1 ml-3'>Gratis</div>
+ </div>
+ </div>
+ ))}
+ </>
+ ))}
<div className='sticky bottom-0 left-0 w-full p-4 mt-auto border-t border-gray_r-6 bg-white'>
<div className='flex flex-col gap-y-3 mb-4'>
@@ -330,105 +537,240 @@ const Cart = () => {
</tr>
</thead>
<tbody>
- {cart.isLoading && (
+ {isLoading && (
<tr>
<td colSpan={6}>
- <div className='flex justify-center my-2'>
- <Spinner className='w-6 text-gray_r-12/50 fill-gray_r-12' />
+ <div className='container flex justify-center my-4'>
+ <LogoSpinner width={48} height={48} />
</div>
</td>
</tr>
)}
- {!cart.isLoading && (!products || products?.length == 0) && (
+ {!isLoading && (!products || products?.length == 0) && (
<tr>
<td colSpan={6}>Keranjang belanja anda masih kosong</td>
</tr>
)}
{products &&
products?.map((product) => (
- <tr key={product.id}>
- <td>
- <input
- type='checkbox'
- onClick={() => toggleSelected(product.id)}
- checked={product?.selected}
- className='accent-danger-500 w-4'
- />
- </td>
- <td className='flex'>
- <Link
- href={createSlug(
- '/shop/product/',
- product?.parent.name,
- product?.parent.id
- )}
- className='w-[20%] flex-shrink-0'
- >
- <Image
- src={product?.parent?.image}
- alt={product?.name}
- className='object-contain object-center border border-gray_r-6 h-28 w-full rounded-md'
+ <>
+ {product.hasProgram && (
+ <tr className='!border-b-0 pb-0'>
+ <td colSpan={6}>
+ <div className='flex gap-x-2 bg-yellow-100 p-2 items-center'>
+ {product.program ? (
+ <>
+ <div className='flex border border-solid border-red-600 rounded-md p-1'>
+ <span className='text-red-600'>
+ {product.program.type.label}
+ </span>
+ </div>
+ <div className='flex'>
+ {product.program.type.value == 'merchandise' ? (
+ <>Selamat anda mendapatkan merchandise indoteknik.com</>
+ ) : (
+ <>
+ Selamat! Pembelian anda lebih hemat
+ <span className='text-red-600 font-semibold ml-2'>
+ {' '}
+ {currencyFormat(product.program?.totalSavings)}
+ </span>
+ </>
+ )}
+ </div>
+ </>
+ ) : (
+ <>
+ <div className='flex border border-solid border-red-600 rounded-md p-1'>
+ <span className='text-red-600'>Promo</span>
+ </div>
+ <div className='flex'>
+ Pilih Promo Yang Tersedia Bisa lebih Hemat
+ </div>
+ </>
+ )}
+ <div
+ onClick={() =>
+ handlePopUpPromo(
+ product.id,
+ product.quantity,
+ product.program?.id
+ )
+ }
+ className='ml-auto text-red-500 flex gap-x-1 cursor-pointer'
+ >
+ <div className='font-semibold'> Cek Promo</div>
+ <div>
+ <svg
+ aria-hidden='true'
+ fill='none'
+ stroke='currentColor'
+ stroke-width='1.5'
+ viewBox='0 0 20 24'
+ className='h-5 w-5 font-semibold'
+ >
+ <path
+ d='M8.25 4.5l7.5 7.5-7.5 7.5'
+ stroke-linecap='round'
+ stroke-linejoin='round'
+ ></path>
+ </svg>
+ </div>
+ </div>
+ </div>
+ </td>
+ </tr>
+ )}
+ <tr
+ key={product.id}
+ className={`${product.hasProgram ? '!border-t-0 !border-b-0' : ''}`}
+ >
+ <td className='relative'>
+ <ComponentCanBuy canBuy={product.canBuy} />
+ <input
+ type='checkbox'
+ onClick={() => toggleSelected(product.id)}
+ checked={product?.selected}
+ className='accent-danger-500 w-4'
/>
- </Link>
- <div className='px-2 text-left'>
+ </td>
+ <td className='flex relative'>
+ <ComponentCanBuy canBuy={product.canBuy} />
<Link
href={createSlug(
'/shop/product/',
product?.parent.name,
product?.parent.id
)}
- className='line-clamp-2 leading-6 !text-gray_r-12 font-normal'
+ className='w-[20%] flex-shrink-0'
>
- {product?.parent?.name}
+ <Image
+ src={product?.parent?.image}
+ alt={product?.name}
+ className='object-contain object-center border border-gray_r-6 h-28 w-full rounded-md'
+ />
</Link>
- <div className='text-gray_r-11 mt-2'>
- {product?.code}{' '}
- {product?.attributes.length > 0
- ? `| ${product?.attributes.join(', ')}`
- : ''}
- </div>
- </div>
- </td>
- <td>
- <input
- className='form-input w-16 py-2 text-center bg-gray_r-1'
- type='number'
- value={product?.quantity}
- onChange={(e) => updateQuantity(e.target.value, product?.id)}
- onBlur={(e) => updateQuantity(e.target.value, product?.id, 'BLUR')}
- />
- </td>
- <td>
- {product?.price?.discountPercentage > 0 && (
- <div className='flex gap-x-1 items-center justify-center mt-3'>
- <div className='text-gray_r-11 line-through text-caption-1'>
- {currencyFormat(product?.price?.price)}
+ <div className='px-2 text-left'>
+ <Link
+ href={createSlug(
+ '/shop/product/',
+ product?.parent.name,
+ product?.parent.id
+ )}
+ className='line-clamp-2 leading-6 !text-gray_r-12 font-normal'
+ >
+ {product?.parent?.name}
+ </Link>
+ <div className='text-gray_r-11 mt-2'>
+ {product?.code}{' '}
+ {product?.attributes.length > 0
+ ? `| ${product?.attributes.join(', ')}`
+ : ''}
</div>
- <div className='badge-solid-red'>
- {product?.price?.discountPercentage}%
+ </div>
+ </td>
+ <td className='relative'>
+ <ComponentCanBuy canBuy={product.canBuy} />
+ <input
+ className='form-input w-16 py-2 text-center bg-gray_r-1'
+ type='number'
+ value={product?.quantity}
+ onChange={(e) => updateQuantity(e.target.value, product?.id)}
+ onBlur={(e) => updateQuantity(e.target.value, product?.id, 'BLUR')}
+ />
+ </td>
+ <td className='relative'>
+ <ComponentCanBuy canBuy={product.canBuy} />
+ {product?.price?.discountPercentage > 0 && (
+ <div className='flex gap-x-1 items-center justify-center mt-3'>
+ <div className='text-gray_r-11 line-through text-caption-1'>
+ {currencyFormat(product?.price?.price)}
+ </div>
+ <div className='badge-solid-red'>
+ {product?.price?.discountPercentage}%
+ </div>
</div>
+ )}
+ <div className='font-normal mt-1'>
+ {currencyFormat(product?.price?.priceDiscount)}
</div>
- )}
- <div className='font-normal mt-1'>
- {currencyFormat(product?.price?.priceDiscount)}
- </div>
- </td>
- <td>
- <div className='text-danger-500 font-medium'>
- {currencyFormat(product?.price?.priceDiscount * product?.quantity)}
- </div>
- </td>
- <td>
- <div className='flex justify-center items-center h-full'>
- <button
- className='btn-red p-1 ml-1'
- onClick={() => setDeleteConfirmation(product)}
- >
- <TrashIcon className='w-4' />
- </button>
- </div>
- </td>
- </tr>
+ </td>
+ <td className='relative'>
+ <ComponentCanBuy canBuy={product.canBuy} />
+ <div className='text-danger-500 font-medium'>
+ {currencyFormat(product?.price?.priceDiscount * product?.quantity)}
+ </div>
+ </td>
+ <td>
+ <div className='flex justify-center items-center h-full'>
+ <button
+ className='btn-red p-1 ml-1'
+ onClick={() => setDeleteConfirmation(product)}
+ >
+ <TrashIcon className='w-4' />
+ </button>
+ </div>
+ </td>
+ </tr>
+ {product?.program?.items && (
+ <tr key={product.program.id} className='!border-t-0'>
+ {product.program.items.map((item) => (
+ <>
+ <td className='relative'>
+ <ComponentCanBuy canBuy={product.canBuy} />
+ </td>
+ <td className='flex relative'>
+ <ComponentCanBuy canBuy={product.canBuy} />
+ <div className='w-[20%] flex-shrink-0'>
+ <Image
+ src={item.parent.image}
+ alt={item.name}
+ className='object-contain object-center border border-gray_r-6 h-28 w-full rounded-md'
+ />
+ </div>
+ <div className='px-2 text-left'>
+ <div className=''>
+ <span className='border border-solid border-red-600 rounded-md p-1 text-red-600'>
+ {product.program.type.label}
+ </span>
+ </div>
+ <div className='mt-2 line-clamp-2 leading-6'>{item.name}</div>
+ </div>
+ </td>
+ <td className='relative'>
+ <ComponentCanBuy canBuy={product.canBuy} />
+ <input
+ className='form-input w-16 py-2 text-center bg-gray_r-1'
+ type='number'
+ value={1}
+ disabled
+ />
+ </td>
+ <td className='relative'>
+ <ComponentCanBuy canBuy={product.canBuy} />
+ {item?.price?.discountPercentage > 0 && (
+ <div className='flex gap-x-1 items-center justify-center mt-3'>
+ <div className='text-gray_r-11 line-through text-caption-1'>
+ {currencyFormat(product?.price?.price)}
+ </div>
+ </div>
+ )}
+ <div className='font-normal mt-1'>
+ {item?.price.priceDiscount > 0 ? 'Gratis' : ''}
+ </div>
+ </td>
+ <td className='relative'>
+ <ComponentCanBuy canBuy={product.canBuy} />
+ <div className='text-danger-500 font-medium'>
+ {item.price.priceDiscount > 0 ? 'Gratis' : ''}
+ </div>
+ </td>
+ <td></td>
+ </>
+ ))}
+ </tr>
+ )}
+ </>
))}
</tbody>
</table>
@@ -506,4 +848,7 @@ const Cart = () => {
)
}
+const ComponentCanBuy = ({ canBuy }) =>
+ !canBuy && <div className='absolute w-full h-full bg-gray_r-3/40 top-0 left-0' />
+
export default Cart
diff --git a/src/lib/cart/components/CartOld.jsx b/src/lib/cart/components/CartOld.jsx
new file mode 100644
index 00000000..718541af
--- /dev/null
+++ b/src/lib/cart/components/CartOld.jsx
@@ -0,0 +1,497 @@
+import Link from '@/core/components/elements/Link/Link'
+import useCart from '../hooks/useCart'
+import Image from '@/core/components/elements/Image/Image'
+import NextImage from 'next/image'
+import currencyFormat from '@/core/utils/currencyFormat'
+import { useEffect, useState } from 'react'
+import { deleteItemCart, getItemCart, updateItemCart } from '@/core/utils/cart'
+import { CheckIcon, TrashIcon } from '@heroicons/react/24/outline'
+import { createSlug } from '@/core/utils/slug'
+import { useRouter } from 'next/router'
+import BottomPopup from '@/core/components/elements/Popup/BottomPopup'
+import { toast } from 'react-hot-toast'
+import Spinner from '@/core/components/elements/Spinner/Spinner'
+import Alert from '@/core/components/elements/Alert/Alert'
+import MobileView from '@/core/components/views/MobileView'
+import DesktopView from '@/core/components/views/DesktopView'
+import ProductCard from '@/lib/product/components/ProductCard'
+import productSearchApi from '@/lib/product/api/productSearchApi'
+import whatsappUrl from '@/core/utils/whatsappUrl'
+import useAuth from '@/core/hooks/useAuth'
+
+const Cart = () => {
+ const router = useRouter()
+ const [products, setProducts] = useState(null)
+ const auth = useAuth()
+
+ useEffect(() => {
+ if (!auth) return
+ }, [auth])
+ const { cart } = useCart({ enabled: !products })
+
+ const [totalPriceBeforeTax, setTotalPriceBeforeTax] = useState(0)
+ const [totalTaxAmount, setTotalTaxAmount] = useState(0)
+ const [totalDiscountAmount, setTotalDiscountAmount] = useState(0)
+
+ const [deleteConfirmation, setDeleteConfirmation] = useState(null)
+
+ const [productRecomendation, setProductRecomendation] = useState(null)
+
+ useEffect(() => {
+ if (cart.data && !products) {
+ const productsWithQuantity = cart.data.map((product) => {
+ const productInCart = getItemCart({ productId: product.id })
+ if (!productInCart) return
+ return {
+ ...product,
+ quantity: productInCart.quantity,
+ selected: productInCart.selected
+ }
+ })
+ setProducts(productsWithQuantity)
+ }
+ }, [cart, products])
+
+ useEffect(() => {
+ if (!products) return
+
+ let calculateTotalPriceBeforeTax = 0
+ let calculateTotalTaxAmount = 0
+ let calculateTotalDiscountAmount = 0
+ for (const product of products) {
+ if (product.quantity == '') continue
+ updateItemCart({
+ productId: product.id,
+ quantity: product.quantity,
+ selected: product.selected
+ })
+
+ if (!product.selected) continue
+ let priceBeforeTax = product.price.price / 1.11
+ calculateTotalPriceBeforeTax += priceBeforeTax * product.quantity
+ calculateTotalTaxAmount += (product.price.price - priceBeforeTax) * product.quantity
+ calculateTotalDiscountAmount +=
+ (product.price.price - product.price.priceDiscount) * product.quantity
+ }
+ setTotalPriceBeforeTax(calculateTotalPriceBeforeTax)
+ setTotalTaxAmount(calculateTotalTaxAmount)
+ setTotalDiscountAmount(calculateTotalDiscountAmount)
+ }, [products])
+
+ useEffect(() => {
+ const LoadProductSimilar = async () => {
+ const randProductIndex = Math.floor(Math.random() * products.length)
+ const productLoad = await productSearchApi({
+ query: `q=${products?.[randProductIndex].parent.name}&limit=10&operation=OR`
+ })
+
+ setProductRecomendation(productLoad)
+ }
+ if (products?.length > 0 && !productRecomendation) LoadProductSimilar()
+ }, [products, productRecomendation])
+
+ const updateQuantity = (value, productId, operation = '') => {
+ let productIndex = products.findIndex((product) => product.id == productId)
+ if (productIndex < 0) return
+
+ let productsToUpdate = products
+ let quantity = productsToUpdate[productIndex].quantity
+ if (value != '' && isNaN(parseInt(value))) return
+ value = value != '' ? parseInt(value) : ''
+ switch (operation) {
+ case 'PLUS':
+ quantity += value
+ break
+ case 'MINUS':
+ if (quantity - value < 1) return
+ quantity -= value
+ break
+ case 'BLUR':
+ if (value != '' && value > 0) return
+ quantity = 1
+ break
+ default:
+ quantity = value != '' && value < 1 ? 1 : value
+ break
+ }
+ productsToUpdate[productIndex].quantity = quantity
+ setProducts([...productsToUpdate])
+ }
+
+ const toggleSelected = (productId) => {
+ let productIndex = products.findIndex((product) => product.id == productId)
+ if (productIndex < 0) return
+
+ let productsToUpdate = products
+ productsToUpdate[productIndex].selected = !productsToUpdate[productIndex].selected
+ setProducts([...productsToUpdate])
+ }
+
+ const selectedProduct = () => {
+ if (!products) return []
+ return products?.filter((product) => product?.selected == true)
+ }
+
+ const deleteProduct = (productId) => {
+ const productsToUpdate = products.filter((product) => product.id != productId)
+ deleteItemCart({ productId })
+ setDeleteConfirmation(null)
+ setProducts([...productsToUpdate])
+ toast.success('Berhasil menghapus barang dari keranjang')
+ }
+
+ return (
+ <>
+ <BottomPopup
+ active={deleteConfirmation}
+ close={() => setDeleteConfirmation(null)}
+ title='Hapus dari Keranjang'
+ >
+ <div className='leading-7 text-gray_r-12/80'>
+ Apakah anda yakin menghapus barang{' '}
+ <span className='underline'>{deleteConfirmation?.name}</span> dari keranjang?
+ </div>
+ <div className='flex mt-6 gap-x-4 md:justify-end'>
+ <button
+ className='btn-solid-red flex-1 md:flex-none'
+ type='button'
+ onClick={() => deleteProduct(deleteConfirmation?.id)}
+ >
+ Ya, Hapus
+ </button>
+ <button
+ className='btn-light flex-1 md:flex-none'
+ type='button'
+ onClick={() => setDeleteConfirmation(null)}
+ >
+ Batal
+ </button>
+ </div>
+ </BottomPopup>
+
+ <MobileView>
+ <div className='pt-4'>
+ <div className='flex justify-between mb-4 px-4'>
+ <h1 className='font-semibold'>Keranjang</h1>
+ <Link href='/'>Cari Produk Lain</Link>
+ </div>
+
+ <div className='flex flex-col gap-y-4 h-screen'>
+ {cart.isLoading && (
+ <div className='flex justify-center my-4'>
+ <Spinner className='w-6 text-gray_r-12/50 fill-gray_r-12' />
+ </div>
+ )}
+
+ {!cart.isLoading && (!products || products?.length == 0) && (
+ <div className='px-4'>
+ <Alert className='text-center my-2' type='info'>
+ Keranjang belanja anda masih kosong
+ </Alert>
+ </div>
+ )}
+
+ {products?.map((product) => (
+ <div key={product?.id} className='flex mx-4'>
+ <input
+ type='checkbox'
+ onClick={() => toggleSelected(product.id)}
+ checked={product?.selected}
+ className='mr-2 accent-danger-500 w-4'
+ />
+
+ <Link
+ href={createSlug('/shop/product/', product?.parent.name, product?.parent.id)}
+ className='w-[30%] flex-shrink-0'
+ >
+ <Image
+ src={product?.parent?.image}
+ alt={product?.name}
+ className='object-contain object-center border border-gray_r-6 h-40 w-full rounded-md'
+ />
+ </Link>
+ <div className='flex-1 px-2 text-caption-2'>
+ <Link
+ href={createSlug('/shop/product/', product?.parent.name, product?.parent.id)}
+ className='line-clamp-2 leading-6 !text-gray_r-12 font-normal'
+ >
+ {product?.parent?.name}
+ </Link>
+ <div className='text-gray_r-11 mt-1'>
+ {product?.code}{' '}
+ {product?.attributes.length > 0 ? `| ${product?.attributes.join(', ')}` : ''}
+ </div>
+ {product?.price?.discountPercentage > 0 && (
+ <div className='flex gap-x-1 items-center mt-3'>
+ <div className='text-gray_r-11 line-through text-caption-2'>
+ {currencyFormat(product?.price?.price)}
+ </div>
+ <div className='badge-solid-red'>{product?.price?.discountPercentage}%</div>
+ </div>
+ )}
+ <div className='font-normal mt-1'>
+ {currencyFormat(product?.price?.priceDiscount)}
+ </div>
+ <div className='flex justify-between items-center mt-1'>
+ <div className='text-danger-500 font-medium'>
+ {currencyFormat(product?.price?.priceDiscount * product?.quantity)}
+ </div>
+ <div className='flex gap-x-1'>
+ <button
+ type='button'
+ className='btn-light px-2 py-1'
+ onClick={() => updateQuantity(1, product?.id, 'MINUS')}
+ disabled={product?.quantity == 1}
+ >
+ -
+ </button>
+ <input
+ className='form-input w-6 border-0 border-b rounded-none py-1 px-0 text-center'
+ type='number'
+ value={product?.quantity}
+ onChange={(e) => updateQuantity(e.target.value, product?.id)}
+ onBlur={(e) => updateQuantity(e.target.value, product?.id, 'BLUR')}
+ />
+ <button
+ type='button'
+ className='btn-light px-2 py-1'
+ onClick={() => updateQuantity(1, product?.id, 'PLUS')}
+ >
+ +
+ </button>
+ <button
+ className='btn-red p-1 ml-1'
+ onClick={() => setDeleteConfirmation(product)}
+ >
+ <TrashIcon className='w-4' />
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+ ))}
+
+ <div className='sticky bottom-0 left-0 w-full p-4 mt-auto border-t border-gray_r-6 bg-white'>
+ <div className='flex justify-between mb-4'>
+ <div className='text-gray_r-11'>
+ Total:
+ <span className='text-danger-500 font-semibold'>
+ &nbsp;
+ {selectedProduct().length > 0
+ ? currencyFormat(totalPriceBeforeTax - totalDiscountAmount + totalTaxAmount)
+ : '-'}
+ </span>
+ </div>
+ </div>
+ <div className='flex gap-x-3'>
+ <button
+ type='button'
+ className='btn-yellow flex-1'
+ disabled={selectedProduct().length == 0}
+ onClick={() => router.push('/shop/quotation')}
+ >
+ Quotation
+ </button>
+ <button
+ type='button'
+ className='btn-solid-red flex-1'
+ disabled={selectedProduct().length == 0}
+ onClick={() => router.push('/shop/checkout')}
+ >
+ Checkout
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </MobileView>
+
+ <DesktopView>
+ <div className='container mx-auto py-10 grid grid-cols-12'>
+ <div className='col-span-9 border border-gray_r-6 rounded bg-white p-4 pt-6'>
+ <h1 className='text-title-sm font-semibold mb-6'>Keranjang</h1>
+
+ <table className='table-cart'>
+ <thead>
+ <tr>
+ <th colSpan={2}>Nama Produk</th>
+ <th>Jumlah</th>
+ <th>Harga</th>
+ <th>Subtotal</th>
+ <th>Action</th>
+ </tr>
+ </thead>
+ <tbody>
+ {cart.isLoading && (
+ <tr>
+ <td colSpan={6}>
+ <div className='flex justify-center my-2'>
+ <Spinner className='w-6 text-gray_r-12/50 fill-gray_r-12' />
+ </div>
+ </td>
+ </tr>
+ )}
+ {!cart.isLoading && (!products || products?.length == 0) && (
+ <tr>
+ <td colSpan={6}>Keranjang belanja anda masih kosong</td>
+ </tr>
+ )}
+ {products &&
+ products?.map((product) => (
+ <tr key={product.id}>
+ <td>
+ <input
+ type='checkbox'
+ onClick={() => toggleSelected(product.id)}
+ checked={product?.selected}
+ className='accent-danger-500 w-4'
+ />
+ </td>
+ <td className='flex'>
+ <Link
+ href={createSlug(
+ '/shop/product/',
+ product?.parent.name,
+ product?.parent.id
+ )}
+ className='w-[20%] flex-shrink-0'
+ >
+ <Image
+ src={product?.parent?.image}
+ alt={product?.name}
+ className='object-contain object-center border border-gray_r-6 h-28 w-full rounded-md'
+ />
+ </Link>
+ <div className='px-2 text-left'>
+ <Link
+ href={createSlug(
+ '/shop/product/',
+ product?.parent.name,
+ product?.parent.id
+ )}
+ className='line-clamp-2 leading-6 !text-gray_r-12 font-normal'
+ >
+ {product?.parent?.name}
+ </Link>
+ <div className='text-gray_r-11 mt-2'>
+ {product?.code}{' '}
+ {product?.attributes.length > 0
+ ? `| ${product?.attributes.join(', ')}`
+ : ''}
+ </div>
+ </div>
+ </td>
+ <td>
+ <input
+ className='form-input w-16 py-2 text-center bg-gray_r-1'
+ type='number'
+ value={product?.quantity}
+ onChange={(e) => updateQuantity(e.target.value, product?.id)}
+ onBlur={(e) => updateQuantity(e.target.value, product?.id, 'BLUR')}
+ />
+ </td>
+ <td>
+ {product?.price?.discountPercentage > 0 && (
+ <div className='flex gap-x-1 items-center justify-center mt-3'>
+ <div className='text-gray_r-11 line-through text-caption-1'>
+ {currencyFormat(product?.price?.price)}
+ </div>
+ <div className='badge-solid-red'>
+ {product?.price?.discountPercentage}%
+ </div>
+ </div>
+ )}
+ <div className='font-normal mt-1'>
+ {currencyFormat(product?.price?.priceDiscount)}
+ </div>
+ </td>
+ <td>
+ <div className='text-danger-500 font-medium'>
+ {currencyFormat(product?.price?.priceDiscount * product?.quantity)}
+ </div>
+ </td>
+ <td>
+ <div className='flex justify-center items-center h-full'>
+ <button
+ className='btn-red p-1 ml-1'
+ onClick={() => setDeleteConfirmation(product)}
+ >
+ <TrashIcon className='w-4' />
+ </button>
+ </div>
+ </td>
+ </tr>
+ ))}
+ </tbody>
+ </table>
+
+ <div className='pt-2 pb-6 flex items-center gap-x-3'>
+ <NextImage
+ src='/images/logo-question.png'
+ alt='Logo Question Indoteknik'
+ width={60}
+ height={60}
+ />
+ <div className='text-gray_r-12/90'>
+ Tanya stock untuk pembelian anda sebelum melanjutkan pembayaran!
+ <span>
+ {' '}
+ <a href={whatsappUrl()} className='text-danger-500'>
+ Hubungi Kami
+ </a>
+ </span>
+ </div>
+ </div>
+ </div>
+
+ <div className='col-span-3 pl-4'>
+ <div className='sticky top-48 w-full p-4 rounded border border-gray_r-6 bg-white'>
+ <h1 className='text-title-sm font-semibold mb-6'>Ringkasan Belanja</h1>
+ <div className='flex justify-between mb-4'>
+ <div className='text-gray_r-11'>
+ Total:
+ <span className='text-danger-500 font-semibold'>
+ &nbsp;
+ {selectedProduct().length > 0
+ ? currencyFormat(totalPriceBeforeTax - totalDiscountAmount + totalTaxAmount)
+ : '-'}
+ </span>
+ </div>
+ </div>
+ <div className='flex gap-x-3'>
+ <button
+ type='button'
+ className='btn-yellow flex-1'
+ disabled={selectedProduct().length == 0}
+ onClick={() => router.push('/shop/quotation')}
+ >
+ Quotation
+ </button>
+ <button
+ type='button'
+ className='btn-solid-red flex-1'
+ disabled={selectedProduct().length == 0}
+ onClick={() => router.push('/shop/checkout')}
+ >
+ Checkout
+ </button>
+ </div>
+ </div>
+ </div>
+
+ <div className='col-span-9 pt-2 pb-6 mt-6'>
+ <h1 className='text-title-sm font-semibold mb-6'>Produk Yang Mungkin Kamu Suka</h1>
+ <div className='grid grid-cols-5 gap-x-3 gap-y-6'>
+ {productRecomendation &&
+ productRecomendation.response.products.map((product) => (
+ <ProductCard product={product} key={product.id} />
+ ))}
+ </div>
+ </div>
+ </div>
+ </DesktopView>
+ </>
+ )
+}
+
+export default Cart