diff options
| author | Rafi Zadanly <zadanlyr@gmail.com> | 2023-02-03 17:03:45 +0700 |
|---|---|---|
| committer | Rafi Zadanly <zadanlyr@gmail.com> | 2023-02-03 17:03:45 +0700 |
| commit | cd01ba82733062db99075ad7690bdb52fb85745a (patch) | |
| tree | df86ed690452945463abc77263ac058d5b7f9823 | |
| parent | 87af032177192ed1d5d7c68cab911ed102e647bc (diff) | |
no message
| -rw-r--r-- | src/components/products/ProductCategories.js | 56 | ||||
| -rw-r--r-- | src/components/products/ProductSimilar.js | 25 | ||||
| -rw-r--r-- | src/components/variants/VariantCard.js | 43 | ||||
| -rw-r--r-- | src/pages/index.js | 50 | ||||
| -rw-r--r-- | src/pages/my/invoice/[id].js | 5 | ||||
| -rw-r--r-- | src/pages/my/invoices.js | 2 | ||||
| -rw-r--r-- | src/pages/my/transaction/[id].js | 1 | ||||
| -rw-r--r-- | src/pages/shop/checkout.js | 2 | ||||
| -rw-r--r-- | src/pages/shop/product/[slug].js | 21 | ||||
| -rw-r--r-- | src/pages/shop/quotation/index.js | 34 |
10 files changed, 154 insertions, 85 deletions
diff --git a/src/components/products/ProductCategories.js b/src/components/products/ProductCategories.js new file mode 100644 index 00000000..dc1325b7 --- /dev/null +++ b/src/components/products/ProductCategories.js @@ -0,0 +1,56 @@ +import { useEffect, useState } from "react"; +import ProductSlider from "./ProductSlider"; +import apiOdoo from "@/core/utils/apiOdoo"; +import { LazyLoadComponent } from "react-lazy-load-image-component"; +import { SkeletonProduct } from "../elements/Skeleton"; + +const ProductCategory = ({ id }) => { + const [ content, setContent ] = useState(null); + + useEffect(() => { + const loadContent = async () => { + if (!content) { + const dataContent = await apiOdoo('GET', `/api/v1/categories_homepage?id=${id}`); + setContent(dataContent[0]); + } + } + loadContent(); + }, [id, content]); + + return ( + <div className="my-6 px-4 pt-4 relative"> + { content ? ( + <ProductSlider + products={{ + products: content.products, + banner: { + image: content.image, + name: content.name, + url: `/shop/search?category=${content.name}` + } + }} + simpleProductTitleLine + bannerMode + /> + ) : <SkeletonProduct /> } + </div> + ); +} + +export default function ProductCategories() { + const [ contentIds, setContentIds ] = useState([]); + + useEffect(() => { + const getContentIds = async () => { + const dataContentIds = await apiOdoo('GET', '/api/v1/categories_homepage/ids'); + setContentIds(dataContentIds); + } + getContentIds(); + }, []); + + return contentIds.map((contentId) => ( + <LazyLoadComponent key={contentId}> + <ProductCategory id={contentId} /> + </LazyLoadComponent> + )); +}
\ No newline at end of file diff --git a/src/components/products/ProductSimilar.js b/src/components/products/ProductSimilar.js new file mode 100644 index 00000000..9e2292cb --- /dev/null +++ b/src/components/products/ProductSimilar.js @@ -0,0 +1,25 @@ +import apiOdoo from '@/core/utils/apiOdoo'; +import { useEffect, useState } from 'react'; +import ProductSlider from './ProductSlider'; + +export default function ProductSimilar({ productId }) { + const [similarProducts, setSimilarProducts] = useState(null); + + useEffect(() => { + const getSimilarProducts = async () => { + if (productId && !similarProducts) { + const dataSimilarProducts = await apiOdoo('GET', `/api/v1/product/${productId}/similar?limit=20`); + setSimilarProducts(dataSimilarProducts); + } + } + getSimilarProducts(); + }, [productId, similarProducts]); + + + return ( + <div className="p-4"> + <h2 className="font-bold mb-4">Kamu Mungkin Juga Suka</h2> + <ProductSlider products={similarProducts}/> + </div> + ) +}
\ No newline at end of file diff --git a/src/components/variants/VariantCard.js b/src/components/variants/VariantCard.js index cb4d8272..2d27371b 100644 --- a/src/components/variants/VariantCard.js +++ b/src/components/variants/VariantCard.js @@ -2,12 +2,27 @@ import { createSlug } from "@/core/utils/slug"; import Image from "../elements/Image"; import Link from "../elements/Link"; import currencyFormat from "@/core/utils/currencyFormat"; +import { useRouter } from "next/router"; +import { toast } from "react-hot-toast"; +import { createOrUpdateItemCart } from "@/core/utils/cart"; export default function VariantCard({ data, - openOnClick = true + openOnClick = true, + buyMore = false }) { let product = data; + const router = useRouter(); + + const addItemToCart = () => { + toast.success('Berhasil menambahkan ke keranjang', { duration: 1500 }); + createOrUpdateItemCart(product.id, 1); + return; + }; + + const checkoutItem = () => { + router.push(`/shop/checkout?product_id=${product.id}&qty=${product.quantity}`); + } const Card = () => ( <div className="flex gap-x-3"> @@ -38,9 +53,29 @@ export default function VariantCard({ if (openOnClick) { return ( - <Link href={'/shop/product/' + createSlug(product.parent.name, product.parent.id)}> - <Card /> - </Link> + <> + <Link href={'/shop/product/' + createSlug(product.parent.name, product.parent.id)}> + <Card /> + </Link> + { buyMore && ( + <div className="flex justify-end gap-x-2 mb-2"> + <button + type="button" + onClick={addItemToCart} + className="btn-yellow text-gray_r-12 py-2 px-3" + > + Tambah Keranjang + </button> + <button + type="button" + onClick={checkoutItem} + className="btn-solid-red py-2 px-3" + > + Beli Lagi + </button> + </div> + ) } + </> ); } diff --git a/src/pages/index.js b/src/pages/index.js index 49300883..fcf47b34 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -16,6 +16,8 @@ import Layout from "@/components/layouts/Layout"; import ManufactureCard from "@/components/manufactures/ManufactureCard"; import Footer from "@/components/layouts/Footer"; import Image from "@/components/elements/Image"; +import ProductCategories from "@/components/products/ProductCategories"; +import { LazyLoadComponent } from "react-lazy-load-image-component"; export async function getServerSideProps() { const heroBanners = await apiOdoo('GET', `/api/v1/banner?type=index-a-1`); @@ -26,8 +28,6 @@ export async function getServerSideProps() { export default function Home({ heroBanners }) { const [manufactures, setManufactures] = useState(null); const [popularProducts, setPopularProducts] = useState(null); - const [categoryProductIds, setCategoryProductIds] = useState(null); - const [categoryProducts, setCategoryProducts] = useState([]); useEffect(() => { const getManufactures = async () => { @@ -43,33 +43,6 @@ export default function Home({ heroBanners }) { getPopularProducts(); }, []); - useEffect(() => { - const getCategoryProductIds = async () => { - if (!categoryProductIds) { - const dataCategoryProductIds = await apiOdoo('GET', '/api/v1/categories_homepage/ids'); - setCategoryProductIds(dataCategoryProductIds) - } - } - getCategoryProductIds(); - }, [ categoryProductIds ]); - - useEffect(() => { - const getCategoryProducts = async () => { - const currentCategoryId = categoryProductIds ? categoryProductIds[categoryProducts.length] : false; - if (currentCategoryId) { - const isAdded = categoryProducts.findIndex((categoryProduct) => categoryProduct.id == currentCategoryId); - if (isAdded < 0) { - const dataCategoryProducts = await apiOdoo('GET', `/api/v1/categories_homepage?id=${currentCategoryId}`); - setCategoryProducts((categoryProducts) => ([ - ...categoryProducts, - ...dataCategoryProducts - ])); - } - } - } - getCategoryProducts(); - }, [ categoryProducts, categoryProductIds ]); - return ( <> <Header title='Home - Indoteknik' /> @@ -104,22 +77,9 @@ export default function Home({ heroBanners }) { <ProductSlider products={popularProducts} simpleProductTitleLine /> </div> - { categoryProducts?.map((categoryProduct, index) => ( - <div className="my-6 px-4 pt-4 relative" key={index}> - <ProductSlider - products={categoryProduct ? { - products: categoryProduct.products, - banner: { - image: categoryProduct.image, - name: categoryProduct.name, - url: `/shop/search?category=${categoryProduct.name}` - } - } : null} - simpleProductTitleLine - bannerMode - /> - </div> - )) } + <LazyLoadComponent> + <ProductCategories /> + </LazyLoadComponent> <div className="px-4"> <h5 className="h2 mb-2">Platform Belanja B2B Alat Teknik & Industri di Indonesia</h5> diff --git a/src/pages/my/invoice/[id].js b/src/pages/my/invoice/[id].js index 05247210..10f625a9 100644 --- a/src/pages/my/invoice/[id].js +++ b/src/pages/my/invoice/[id].js @@ -93,7 +93,7 @@ export default function DetailInvoice() { <p className="text-gray_r-11 leading-none">Faktur Pembelian</p> <button type="button" - className="btn-solid-red py-1 px-2 ml-auto" + className="btn-light py-1.5 px-3 ml-auto" onClick={downloadInvoice} > Download @@ -103,7 +103,7 @@ export default function DetailInvoice() { <p className="text-gray_r-11 leading-none">Faktur Pajak</p> <button type="button" - className="btn-solid-red py-1 px-2 ml-auto" + className="btn-light py-1.5 px-3 ml-auto" onClick={downloadTaxInvoice} disabled={invoice.efaktur ? false : true} > @@ -131,6 +131,7 @@ export default function DetailInvoice() { <VariantCard key={index} data={product} + buyMore /> )) } <div className="flex justify-between mt-3 font-medium"> diff --git a/src/pages/my/invoices.js b/src/pages/my/invoices.js index d54f9487..a86ddffc 100644 --- a/src/pages/my/invoices.js +++ b/src/pages/my/invoices.js @@ -115,7 +115,7 @@ export default function Invoices() { </div> </div> </Link> - { invoice.efaktur_token ? ( + { invoice.efaktur ? ( <div className="badge-green h-fit mt-3 ml-auto flex items-center gap-x-0.5"> <CheckIcon className="w-4 stroke-2" /> Faktur Pajak diff --git a/src/pages/my/transaction/[id].js b/src/pages/my/transaction/[id].js index a508ef77..428d71fb 100644 --- a/src/pages/my/transaction/[id].js +++ b/src/pages/my/transaction/[id].js @@ -72,6 +72,7 @@ export default function DetailTransaction() { <VariantCard key={index} data={product} + buyMore /> )) } <div className="flex justify-between mt-3 font-medium"> diff --git a/src/pages/shop/checkout.js b/src/pages/shop/checkout.js index 875cf0f1..8a540bcd 100644 --- a/src/pages/shop/checkout.js +++ b/src/pages/shop/checkout.js @@ -195,6 +195,7 @@ export default function Checkout() { { selectedAddress.shipping && ( <div className="mt-4 text-caption-1"> + <div className="badge-red mb-2">{ selectedAddress.invoicing.type.charAt(0).toUpperCase() + selectedAddress.invoicing.type.slice(1) + ' Address' }</div> <p className="font-medium">{ selectedAddress.shipping.name }</p> <p className="mt-2 text-gray_r-11">{ selectedAddress.shipping.mobile }</p> <p className="mt-1 text-gray_r-11">{ selectedAddress.shipping.street }, { selectedAddress.shipping?.city?.name }</p> @@ -261,6 +262,7 @@ export default function Checkout() { { selectedAddress.invoicing && ( <div className="mt-4 text-caption-1"> + <div className="badge-red mb-2">{ selectedAddress.invoicing.type.charAt(0).toUpperCase() + selectedAddress.invoicing.type.slice(1) + ' Address' }</div> <p className="font-medium">{ selectedAddress.invoicing.name }</p> <p className="mt-2 text-gray_r-11">{ selectedAddress.invoicing.mobile }</p> <p className="mt-1 text-gray_r-11">{ selectedAddress.invoicing.street } { selectedAddress.invoicing.street2 }</p> diff --git a/src/pages/shop/product/[slug].js b/src/pages/shop/product/[slug].js index 03fac0be..c3d34806 100644 --- a/src/pages/shop/product/[slug].js +++ b/src/pages/shop/product/[slug].js @@ -15,6 +15,8 @@ import LineDivider from "@/components/elements/LineDivider"; import { HeartIcon as HeartIconSolid } from "@heroicons/react/24/solid"; import { useAuth } from "@/core/utils/auth"; import { HeartIcon } from "@heroicons/react/24/outline"; +import { LazyLoadComponent } from "react-lazy-load-image-component"; +import ProductSimilar from "@/components/products/ProductSimilar"; export async function getServerSideProps( context ) { const { slug } = context.query; @@ -37,7 +39,6 @@ export default function ProductDetail({ product }) { const { slug } = router.query; const [selectedVariant, setSelectedVariant] = useState(""); const [quantity, setQuantity] = useState("1"); - const [similarProducts, setSimilarProducts] = useState(null); const [activeVariant, setActiveVariant] = useState({ id: product.id, code: product.code, @@ -83,15 +84,6 @@ export default function ProductDetail({ product }) { }, [ product ]); useEffect(() => { - setSimilarProducts(null); - const getSimilarProducts = async () => { - const dataSimilarProducts = await apiOdoo('GET', `/api/v1/product/${getIdFromSlug(slug)}/similar?limit=20`); - setSimilarProducts(dataSimilarProducts); - } - if (slug) getSimilarProducts(); - }, [slug]); - - useEffect(() => { if (selectedVariant != '') { let newActiveVariant = product.variants.filter((variant) => { return variant.id == selectedVariant; @@ -281,11 +273,10 @@ export default function ProductDetail({ product }) { </div> <LineDivider /> - - <div className="p-4"> - <h2 className="font-bold mb-4">Kamu Mungkin Juga Suka</h2> - <ProductSlider products={similarProducts}/> - </div> + + <LazyLoadComponent> + <ProductSimilar productId={getIdFromSlug(slug || '')} /> + </LazyLoadComponent> <Footer /> </Layout> diff --git a/src/pages/shop/quotation/index.js b/src/pages/shop/quotation/index.js index cf2b956d..e1c196db 100644 --- a/src/pages/shop/quotation/index.js +++ b/src/pages/shop/quotation/index.js @@ -16,8 +16,7 @@ export default function Quotation() { const router = useRouter(); const [ auth ] = useAuth(); const [ products, setProducts ] = useState([]); - const [ totalPriceBeforeTax, setTotalPriceBeforeTax ] = useState(0); - const [ totalTaxAmount, setTotalTaxAmount ] = useState(0); + const [ totalAmount, setTotalAmount ] = useState(0); const [ totalDiscountAmount, setTotalDiscountAmount ] = useState(0); useEffect(() => { @@ -43,18 +42,13 @@ export default function Quotation() { useEffect(() => { if (products) { - const productsSelected = products.filter((product) => product.selected == true); - let calculateTotalPriceBeforeTax = 0; - let calculateTotalTaxAmount = 0; + let calculateTotalAmount = 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; + products.forEach(product => { + calculateTotalAmount += product.price.price * product.quantity; calculateTotalDiscountAmount += (product.price.price - product.price.price_discount) * product.quantity; }); - setTotalPriceBeforeTax(calculateTotalPriceBeforeTax); - setTotalTaxAmount(calculateTotalTaxAmount); + setTotalAmount(calculateTotalAmount); setTotalDiscountAmount(calculateTotalDiscountAmount); } }, [products]); @@ -102,22 +96,26 @@ export default function Quotation() { <hr className="my-4 border-gray_r-6"/> <div className="flex flex-col gap-y-4"> <div className="flex gap-x-2 justify-between"> - <p>Subtotal</p> - <p className="font-medium">{currencyFormat(totalPriceBeforeTax)}</p> + <p>Total Belanja</p> + <p className="font-medium">{currencyFormat(totalAmount)}</p> </div> <div className="flex gap-x-2 justify-between"> - <p>PPN 11%</p> - <p className="font-medium">{currencyFormat(totalTaxAmount)}</p> + <p>Total Diskon</p> + <p className="font-medium text-red_r-11">{'- ' + currencyFormat(totalDiscountAmount)}</p> </div> <div className="flex gap-x-2 justify-between"> - <p>Total Diskon</p> - <p className="font-medium text-red_r-11">- {currencyFormat(totalDiscountAmount)}</p> + <p>Subtotal</p> + <p className="font-medium">{currencyFormat(totalAmount - totalDiscountAmount)}</p> + </div> + <div className="flex gap-x-2 justify-between"> + <p>PPN 11% (Incl.)</p> + <p className="font-medium">{currencyFormat((totalAmount - totalDiscountAmount) * 0.11)}</p> </div> </div> <hr className="my-4 border-gray_r-6"/> <div className="flex gap-x-2 justify-between mb-4"> <p>Grand Total</p> - <p className="font-medium text-yellow_r-11">{currencyFormat(totalPriceBeforeTax + totalTaxAmount - totalDiscountAmount)}</p> + <p className="font-medium text-yellow_r-11">{currencyFormat(totalAmount - totalDiscountAmount)}</p> </div> <p className="text-caption-2 text-gray_r-10 mb-2">*) Belum termasuk biaya pengiriman</p> <p className="text-caption-2 text-gray_r-10 leading-5"> |
