summaryrefslogtreecommitdiff
path: root/src2/pages/shop/cart.js
diff options
context:
space:
mode:
Diffstat (limited to 'src2/pages/shop/cart.js')
-rw-r--r--src2/pages/shop/cart.js282
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