diff options
| author | Rafi Zadanly <zadanlyr@gmail.com> | 2023-02-17 17:07:50 +0700 |
|---|---|---|
| committer | Rafi Zadanly <zadanlyr@gmail.com> | 2023-02-17 17:07:50 +0700 |
| commit | f99e0aba70efad0deb907d8e27f09fc9f527c8a4 (patch) | |
| tree | f0ac96e4e736a1d385e32553f0e641ee27e11fd3 /src2/pages/shop/cart.js | |
| parent | 90e1edab9b6a8ccc09a49fed3addbec2cbc4e4c3 (diff) | |
Refactor
Diffstat (limited to 'src2/pages/shop/cart.js')
| -rw-r--r-- | src2/pages/shop/cart.js | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/src2/pages/shop/cart.js b/src2/pages/shop/cart.js new file mode 100644 index 00000000..1178781b --- /dev/null +++ b/src2/pages/shop/cart.js @@ -0,0 +1,282 @@ +import { useEffect, useState } from "react"; +import { toast } from "react-hot-toast"; +import { + TrashIcon, + PlusIcon, + MinusIcon, + ExclamationCircleIcon, +} from "@heroicons/react/24/solid"; +import { ExclamationTriangleIcon } from "@heroicons/react/24/outline"; +import { useRouter } from "next/router"; + +// Helpers +import { + createOrUpdateItemCart, + deleteItemCart, + getCart +} from "@/core/utils/cart"; +import { createSlug } from "@/core/utils/slug"; +import apiOdoo from "@/core/utils/apiOdoo"; +import currencyFormat from "@/core/utils/currencyFormat"; + +// Components +import Image from "@/components/elements/Image"; +import Layout from "@/components/layouts/Layout"; +import Link from "@/components/elements/Link"; +import Alert from "@/components/elements/Alert"; +import Spinner from "@/components/elements/Spinner"; +import AppBar from "@/components/layouts/AppBar"; +import ProgressBar from "@/components/elements/ProgressBar"; +import LineDivider from "@/components/elements/LineDivider"; +import useConfirmAlert from "@/lib/elements/hooks/useConfirmAlert"; + +export default function Cart() { + const router = useRouter(); + const [isLoadingProducts, setIsLoadingProducts] = useState(true); + const [products, setProducts] = useState([]); + const [totalPriceBeforeTax, setTotalPriceBeforeTax] = useState(0); + const [totalTaxAmount, setTotalTaxAmount] = useState(0); + const [totalDiscountAmount, setTotalDiscountAmount] = useState(0); + + useEffect(() => { + const getProducts = async () => { + let cart = getCart(); + let productIds = Object.keys(cart); + if (productIds.length > 0) { + productIds = productIds.join(','); + let dataProducts = await apiOdoo('GET', `/api/v1/product_variant/${productIds}`); + dataProducts = dataProducts.map((product) => ({ + ...product, + quantity: cart[product.id].quantity, + selected: cart[product.id].selected, + })); + setProducts(dataProducts); + } + setIsLoadingProducts(false); + } + getProducts(); + }, []); + + useEffect(() => { + for (const product of products) { + if (product.quantity != '') createOrUpdateItemCart(product.id, product.quantity, product.selected); + } + const productsSelected = products.filter((product) => product.selected == true); + let calculateTotalPriceBeforeTax = 0; + let calculateTotalTaxAmount = 0; + let calculateTotalDiscountAmount = 0; + productsSelected.forEach(product => { + let priceBeforeTax = product.price.price / 1.11; + calculateTotalPriceBeforeTax += priceBeforeTax * product.quantity; + calculateTotalTaxAmount += (product.price.price - priceBeforeTax) * product.quantity; + calculateTotalDiscountAmount += (product.price.price - product.price.price_discount) * product.quantity; + }); + setTotalPriceBeforeTax(calculateTotalPriceBeforeTax); + setTotalTaxAmount(calculateTotalTaxAmount); + setTotalDiscountAmount(calculateTotalDiscountAmount); + }, [products]); + + const getProductsSelected = () => { + return products.filter((product) => product.selected == true); + } + + const updateCart = (productId, quantity) => { + let productIndexToUpdate = products.findIndex((product) => product.id == productId); + let productsToUpdate = products; + productsToUpdate[productIndexToUpdate].quantity = quantity; + setProducts([...productsToUpdate]); + }; + + const blurQuantity = (productId, quantity) => { + quantity = quantity == ('' || 0) ? 1 : parseInt(quantity); + if (typeof quantity === 'number') { + quantity = parseInt(quantity); + quantity = Math.floor(quantity); + } + updateCart(productId, quantity); + }; + + const updateQuantity = (productId, quantity) => { + quantity = quantity == '' ? '' : parseInt(quantity); + updateCart(productId, quantity); + }; + + const plusQuantity = (productId) => { + let productIndexToUpdate = products.findIndex((product) => product.id == productId); + let quantity = products[productIndexToUpdate].quantity + 1; + updateCart(productId, quantity); + } + + const minusQuantity = (productId) => { + let productIndexToUpdate = products.findIndex((product) => product.id == productId); + let quantity = products[productIndexToUpdate].quantity - 1; + updateCart(productId, quantity); + } + + const toggleProductSelected = (productId) => { + let productIndexToUpdate = products.findIndex((product) => product.id == productId); + let productsToUpdate = products; + productsToUpdate[productIndexToUpdate].selected = !productsToUpdate[productIndexToUpdate].selected; + setProducts([...productsToUpdate]); + } + + const deleteItem = (productId) => { + let productIndexToUpdate = products.findIndex((product) => product.id == productId); + let productsToUpdate = products; + productsToUpdate.splice(productIndexToUpdate, 1); + setProducts([...productsToUpdate]); + deleteItemCart(productId); + toast.success('Berhasil menghapus 1 barang dari keranjang', { duration: 1500 }); + } + + const { + openConfirmAlert, + ConfirmAlert + } = useConfirmAlert({ + title: 'Hapus barang dari keranjang', + caption:'Apakah anda yakin menghapus barang dari keranjang?', + closeText: 'Batal', + submitText: 'Hapus', + onSubmit: deleteItem + }) + + return ( + <> + { ConfirmAlert } + + <Layout> + <AppBar title="Keranjang Saya" /> + + {isLoadingProducts && ( + <div className="flex justify-center items-center gap-x-3 mt-14"> + <Spinner className="w-10 text-gray_r-8 fill-gray_r-12" /> + </div> + ) } + + { !isLoadingProducts && products.length == 0 && ( + <div className="text-center mt-14"> + <ExclamationTriangleIcon className="w-12 mx-auto"/> + <p className="mt-2 h2">Keranjang belanja anda masih kosong.</p> + <Link href="/" className="btn-yellow text-gray_r-12 mx-auto mt-4">Mulai Belanja</Link> + </div> + ) } + + { !isLoadingProducts && products.length > 0 && ( + <> + <ProgressBar + current={1} + labels={['Keranjang', 'Pembayaran', 'Selesai']} + /> + + <LineDivider /> + + <div className="p-4"> + <Alert type="warning" className="text-caption-2 flex gap-x-3 items-center"> + <div> + <ExclamationCircleIcon className="w-8 text-yellow_r-11"/> + </div> + <span>Mohon dicek kembali & pastikan pesanan kamu sudah sesuai dengan yang kamu butuhkan. Atau bisa hubungi kami.</span> + </Alert> + </div> + + <LineDivider /> + + <div className="p-4 flex flex-col gap-y-6"> + <div className="flex justify-between items-center"> + <h2>Daftar Produk Belanja</h2> + <Link href="/" className="text-caption-1">Cari Produk Lain</Link> + </div> + {products.map((product, index) => ( + <div className="flex gap-x-3" key={index}> + <div className="w-4/12 flex items-center gap-x-2" onClick={() => toggleProductSelected(product.id)}> + <button + className={'p-2 rounded border-2 ' + (product.selected ? 'border-yellow_r-9 bg-yellow_r-9' : 'border-gray_r-12')} + ></button> + <Image + src={product.parent.image} + alt={product.parent.name} + className="object-contain object-center border border-gray_r-6 h-32 w-full rounded-md" + /> + </div> + <div className="w-8/12 flex flex-col"> + <Link href={'/shop/product/' + createSlug(product.parent.name, product.parent.id)} className="product-card__title wrap-line-ellipsis-2"> + {product.parent.name} + </Link> + <p className="text-caption-2 text-gray_r-11 mt-1"> + {product.code || '-'} + {product.attributes.length > 0 ? ` | ${product.attributes.join(', ')}` : ''} + </p> + <div className="flex flex-wrap gap-x-1 items-center mb-2 mt-auto"> + {product.price.discount_percentage > 0 && ( + <> + <p className="text-caption-2 text-gray_r-11 line-through">{currencyFormat(product.price.price)}</p> + <span className="badge-red">{product.price.discount_percentage}%</span> + </> + )} + <p className="text-caption-2 text-gray_r-12">{currencyFormat(product.price.price_discount)}</p> + </div> + <div className="flex items-center"> + <p className="mr-auto text-caption-2 text-gray_r-12 font-bold">{currencyFormat(product.quantity * product.price.price_discount)}</p> + <div className="flex gap-x-2 items-center"> + <button + className="btn-red p-2 rounded" + onClick={() => openConfirmAlert(product.id)} + > + <TrashIcon className="text-red_r-11 w-3"/> + </button> + <button + className="btn-light p-2 rounded" + disabled={product.quantity == 1} + onClick={() => minusQuantity(product.id)} + > + <MinusIcon className={'text-gray_r-12 w-3' + (product.quantity == 1 ? ' text-gray_r-11' : '')}/> + </button> + <input + type="number" + className="bg-transparent border-none w-6 text-center outline-none" + onBlur={(e) => blurQuantity(product.id, e.target.value)} + onChange={(e) => updateQuantity(product.id, e.target.value)} + value={product.quantity} + /> + <button className="btn-light p-2 rounded" onClick={() => plusQuantity(product.id)}> + <PlusIcon className="text-gray_r-12 w-3"/> + </button> + </div> + </div> + </div> + </div> + ))} + </div> + + <div className="p-4 bg-gray_r-1 sticky bottom-0 border-t-4 border-gray_r-4"> + <div className="flex"> + <p>Total</p> + <p className="text-gray_r-11 ml-1">{getProductsSelected().length > 0 && ( + <>({ getProductsSelected().length } Barang)</> + )}</p> + <p className="font-semibold text-red_r-11 ml-auto">{currencyFormat(totalPriceBeforeTax + totalTaxAmount - totalDiscountAmount)}</p> + </div> + + <div className="flex gap-x-3 mt-4"> + <button + className="flex-1 btn-light" + disabled={getProductsSelected().length == 0} + onClick={() => router.push('/shop/quotation')} + > + Quotation {getProductsSelected().length > 0 && `(${getProductsSelected().length})`} + </button> + <button + className="flex-1 btn-yellow" + disabled={getProductsSelected().length == 0} + onClick={() => router.push('/shop/checkout')} + > + Checkout {getProductsSelected().length > 0 && `(${getProductsSelected().length})`} + </button> + </div> + </div> + </> + ) } + </Layout> + </> + ); +}
\ No newline at end of file |
