diff options
Diffstat (limited to 'src/pages')
| -rw-r--r-- | src/pages/index.js | 5 | ||||
| -rw-r--r-- | src/pages/my/address/index.js | 12 | ||||
| -rw-r--r-- | src/pages/my/invoice/[id].js | 19 | ||||
| -rw-r--r-- | src/pages/my/invoices.js | 17 | ||||
| -rw-r--r-- | src/pages/my/transactions.js | 1 | ||||
| -rw-r--r-- | src/pages/shop/checkout.js | 105 | ||||
| -rw-r--r-- | src/pages/shop/product/[slug].js | 78 |
7 files changed, 149 insertions, 88 deletions
diff --git a/src/pages/index.js b/src/pages/index.js index e7eb4af0..49300883 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -101,7 +101,7 @@ export default function Home({ heroBanners }) { </div> <div className="my-6 p-4 pt-0"> <h2 className="mb-4">Produk Populer</h2> - <ProductSlider products={popularProducts} /> + <ProductSlider products={popularProducts} simpleProductTitleLine /> </div> { categoryProducts?.map((categoryProduct, index) => ( @@ -115,7 +115,8 @@ export default function Home({ heroBanners }) { url: `/shop/search?category=${categoryProduct.name}` } } : null} - bannerMode={true} + simpleProductTitleLine + bannerMode /> </div> )) } diff --git a/src/pages/my/address/index.js b/src/pages/my/address/index.js index 54e721b6..7026d5ea 100644 --- a/src/pages/my/address/index.js +++ b/src/pages/my/address/index.js @@ -56,7 +56,7 @@ export default function Address() { return ( <div key={index} - className={"p-4 rounded-md border " + (selectedAdress && selectedAdress == address.id ? "border-yellow_r-7 bg-yellow_r-2" : "border-gray_r-7") } + className={"p-4 rounded-md border " + (selectedAdress && selectedAdress == address.id ? "border-yellow_r-7 bg-yellow_r-8" : "border-gray_r-7") } > <div onClick={() => changeSelectedAddress(address.id)}> <div className="flex gap-x-2" > @@ -65,9 +65,13 @@ export default function Address() { <div className="badge-green">Utama</div> ) } </div> - <p className="font-medium mt-1">{ address.name }</p> - <p className="mt-2 text-gray_r-11">{ address.mobile }</p> - <p className="mt-1 text-gray_r-11 leading-6">{ address.street } { address.street2 }</p> + <p className="font-medium mt-2">{ address.name }</p> + { address.mobile && ( + <p className="mt-2 text-gray_r-11">{ address.mobile }</p> + ) } + <p className={`mt-1 leading-6 ${selectedAdress && selectedAdress == address.id ? "text-gray_r-12" : "text-gray_r-11"}`}> + { address.street } + </p> </div> <Link href={`/my/address/${address.id}/edit`} className="btn-light mt-3 w-full text-gray_r-11">Ubah Alamat</Link> </div> diff --git a/src/pages/my/invoice/[id].js b/src/pages/my/invoice/[id].js index c1cacfc6..d13cf7a6 100644 --- a/src/pages/my/invoice/[id].js +++ b/src/pages/my/invoice/[id].js @@ -46,6 +46,10 @@ export default function DetailInvoice() { ); }; + const downloadTaxInvoice = () => { + window.open(`${process.env.ODOO_HOST}/api/v1/download/tax-invoice/${invoice.id}/${invoice.efaktur_token}`, '_blank') + } + return ( <WithAuth> <Layout className="pb-4"> @@ -59,9 +63,9 @@ export default function DetailInvoice() { </DescriptionRow> <DescriptionRow label="Status Transaksi"> { invoice?.amount_residual > 0 ? ( - <span className="badge-red">Belum Lunas</span> + <span className="badge-solid-red">Belum Lunas</span> ) : ( - <span className="badge-green">Lunas</span> + <span className="badge-solid-green">Lunas</span> ) } </DescriptionRow> <DescriptionRow label="Purchase Order"> @@ -81,6 +85,17 @@ export default function DetailInvoice() { <DescriptionRow label="Tanggal Invoice"> { invoice?.invoice_date } </DescriptionRow> + <div className="flex items-center"> + <p className="text-gray_r-11 leading-none">Faktur Pajak</p> + <button + type="button" + className="btn-solid-red py-1 px-2 ml-auto" + onClick={downloadTaxInvoice} + disabled={invoice.efaktur_token ? false : true} + > + Download + </button> + </div> </div> <LineDivider /> diff --git a/src/pages/my/invoices.js b/src/pages/my/invoices.js index a3107bfd..d54f9487 100644 --- a/src/pages/my/invoices.js +++ b/src/pages/my/invoices.js @@ -6,7 +6,7 @@ import Layout from "@/components/layouts/Layout"; import apiOdoo from "@/core/utils/apiOdoo"; import { useAuth } from "@/core/utils/auth"; import currencyFormat from "@/core/utils/currencyFormat"; -import { EllipsisVerticalIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline"; +import { CheckIcon, ClockIcon, EllipsisVerticalIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline"; import { useRouter } from "next/router"; import { useEffect, useRef, useState } from "react"; @@ -87,9 +87,9 @@ export default function Invoices() { </Link> <div className="flex gap-x-1 justify-end"> { invoice.amount_residual > 0 ? ( - <div className="badge-red h-fit ml-auto">Belum Lunas</div> + <div className="badge-solid-red h-fit ml-auto">Belum Lunas</div> ) : ( - <div className="badge-green h-fit ml-auto">Lunas</div> + <div className="badge-solid-green h-fit ml-auto">Lunas</div> ) } <EllipsisVerticalIcon className="w-5 h-5" onClick={() => {}} /> </div> @@ -115,6 +115,17 @@ export default function Invoices() { </div> </div> </Link> + { invoice.efaktur_token ? ( + <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 + </div> + ) : ( + <div className="badge-red h-fit mt-3 ml-auto flex items-center gap-x-0.5"> + <ClockIcon className="w-4 stroke-2" /> + Faktur Pajak + </div> + ) } </div> )) } </div> diff --git a/src/pages/my/transactions.js b/src/pages/my/transactions.js index 221859f9..a03ff007 100644 --- a/src/pages/my/transactions.js +++ b/src/pages/my/transactions.js @@ -41,7 +41,6 @@ export default function Transactions() { const dataTransactions = await apiOdoo('GET', `/api/v1/partner/${auth.partner_id}/sale_order${queryParams}`); setTransactions(dataTransactions); - console.log(dataTransactions); setPageCount(Math.ceil(dataTransactions?.sale_order_total / limit)); setIsLoading(false); }; diff --git a/src/pages/shop/checkout.js b/src/pages/shop/checkout.js index f55b200f..875cf0f1 100644 --- a/src/pages/shop/checkout.js +++ b/src/pages/shop/checkout.js @@ -20,6 +20,7 @@ import VariantCard from "@/components/variants/VariantCard"; export default function Checkout() { const router = useRouter(); + const { product_id, qty } = router.query; const [ auth ] = useAuth(); const [ addresses, setAddresses ] = useState(null); const [ poNumber, setPoNumber ] = useState(''); @@ -30,8 +31,7 @@ export default function Checkout() { }); const [ selectedPayment, setSelectedPayment ] = useState(null); const [ products, setProducts ] = useState(null); - const [ totalPriceBeforeTax, setTotalPriceBeforeTax ] = useState(0); - const [ totalTaxAmount, setTotalTaxAmount ] = useState(0); + const [ totalAmount, setTotalAmount ] = useState(0); const [ totalDiscountAmount, setTotalDiscountAmount ] = useState(0); const [ finishCheckout, setFinishCheckout ] = useState(null); @@ -53,25 +53,34 @@ export default function Checkout() { useEffect(() => { const getProducts = async () => { let cart = getCart(); - let productIds = Object - .values(cart) - .filter((itemCart) => itemCart.selected == true) - .map((itemCart) => itemCart.product_id); + let productIds = []; + if (product_id) { + productIds = [parseInt(product_id)]; + } else { + productIds = Object + .values(cart) + .filter((itemCart) => itemCart.selected == true) + .map((itemCart) => itemCart.product_id); + } 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, - })); + dataProducts = dataProducts.map((product) => { + if (product_id) { + product.quantity = 1; + if (qty) product.quantity = parseInt(qty); + } else { + product.quantity = cart[product.id].quantity; + } + return product; + }); setProducts(dataProducts); } else { if (auth) router.push('/shop/cart'); } }; getProducts(); - }, [router, auth]); + }, [router, auth, product_id, qty]); useEffect(() => { if (addresses) { @@ -92,18 +101,13 @@ export default function Checkout() { 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]); @@ -115,17 +119,18 @@ export default function Checkout() { }); return; } + if (poFile && poFile.size > 5000000) { + toast.error('Maksimal ukuran file adalah 5MB', { + position: 'bottom-center' + }); + return; + } let productOrder = products.map((product) => ({ 'product_id': product.id, 'quantity': product.quantity })); let data = { 'partner_shipping_id': selectedAddress.shipping.id, 'partner_invoice_id': selectedAddress.invoicing.id, - 'order_line': JSON.stringify(productOrder) - }; - if (auth?.company && !poFile) { - toast.error('Mohon isi file PO', { - position: 'bottom-center' - }); - return; + 'order_line': JSON.stringify(productOrder), + 'type': 'sale_order' }; if (poNumber) data.po_number = poNumber; if (poFile) data.po_file = await getFileBase64(poFile); @@ -192,7 +197,7 @@ export default function Checkout() { <div className="mt-4 text-caption-1"> <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> + <p className="mt-1 text-gray_r-11">{ selectedAddress.shipping.street }, { selectedAddress.shipping?.city?.name }</p> </div> ) } </div> @@ -219,22 +224,26 @@ export default function Checkout() { <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> - </div> - <div className="flex gap-x-2 justify-between"> - <p>PPN 11%</p> - <p className="font-medium">{currencyFormat(totalTaxAmount)}</p> + <p>Total Belanja</p> + <p className="font-medium">{currencyFormat(totalAmount)}</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> </div> + <div className="flex gap-x-2 justify-between"> + <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"> @@ -285,20 +294,8 @@ export default function Checkout() { <div className="mt-4 flex gap-x-3"> <div className="w-6/12"> - <label className="form-label font-normal">Nomor PO</label> - <input - type="text" - className="form-input mt-2 h-12" - value={poNumber} - onChange={(e) => setPoNumber(e.target.value)} - /> - </div> - <div className="w-6/12"> <label className="form-label font-normal"> - File PO - {auth?.company && ( - <span className="font-normal text-gray_r-11 ml-1">(Wajib diisi)</span> - )} + Dokumen PO </label> <input type="file" @@ -307,7 +304,17 @@ export default function Checkout() { onChange={(e) => setPoFile(e.target.files[0])} /> </div> + <div className="w-6/12"> + <label className="form-label font-normal">Nomor PO</label> + <input + type="text" + className="form-input mt-2 h-12" + value={poNumber} + onChange={(e) => setPoNumber(e.target.value)} + /> + </div> </div> + <p className="text-caption-2 text-gray_r-11 mt-2">Ukuran dokumen PO Maksimal 5MB</p> </div> <LineDivider/> diff --git a/src/pages/shop/product/[slug].js b/src/pages/shop/product/[slug].js index bcfb12ba..281f2bc2 100644 --- a/src/pages/shop/product/[slug].js +++ b/src/pages/shop/product/[slug].js @@ -138,6 +138,23 @@ export default function ProductDetail({ product }) { return true; } + const checkoutProduct = () => { + if (!auth) { + toast.error('Login terlebih dahulu untuk melanjutkan', { duration: 2000 }); + router.push('/login'); + return; + } + if (product.variant_total > 1 && !selectedVariant) { + toast.error('Pilih varian terlebih dahulu untuk melanjutkan pembelian', { duration: 2000 }); + return; + } + if (quantity < 0) { + toast.error('Jumlah barang yang ditambahkan minimal 1 pcs', { duration: 2000 }); + return; + } + router.push(`/shop/checkout?product_id=${activeVariant.id}&qty=${quantity}`); + } + return ( <> <Header title={`${product.name} - Indoteknik`}/> @@ -187,36 +204,38 @@ export default function ProductDetail({ product }) { <LineDivider /> <div className="p-4"> - <div className="flex gap-x-2"> - <div className="w-9/12"> - <label className="form-label mb-1">Pilih: <span className="text-gray_r-11 font-normal">{product.variant_total} Varian</span></label> - <select name="variant" className="form-input" value={selectedVariant} onChange={onchangeVariant} > - <option value="" disabled={selectedVariant != "" ? true : false}>Pilih Varian...</option> - {product.variants.length > 1 ? ( - product.variants.map((variant) => { - return ( - <option key={variant.id} value={variant.id}>{variant.attributes.join(', ')}</option> - ); - }) - ) : ( - <option key={product.variants[0].id} value={product.variants[0].id}>{product.variants[0].name}</option> - )} - </select> - </div> - <div className="w-3/12"> - <label htmlFor="quantity" className="form-label mb-1">Jumlah</label> - <input type="number" name="quantity" id="quantity" className="form-input text-center is-invalid" value={quantity} onChange={onChangeQuantity} /> - </div> + <div className=""> + <label className="form-label mb-2">Pilih: <span className="text-gray_r-11 font-normal">{product.variant_total} Varian</span></label> + <select name="variant" className="form-input" value={selectedVariant} onChange={onchangeVariant} > + <option value="" disabled={selectedVariant != "" ? true : false}>Pilih Varian...</option> + {product.variants.length > 1 ? ( + product.variants.map((variant) => { + return ( + <option key={variant.id} value={variant.id}>{variant.attributes.join(', ')}</option> + ); + }) + ) : ( + <option key={product.variants[0].id} value={product.variants[0].id}>{product.variants[0].name}</option> + )} + </select> </div> + <label htmlFor="quantity" className="form-label mb-1 mt-3">Jumlah</label> <div className="flex gap-x-2 mt-2"> - <button className="btn-light w-full">+ Quotation</button> + <input type="number" name="quantity" id="quantity" className="form-input h-full w-5/12 text-center" value={quantity} onChange={onChangeQuantity} /> + <button className="btn-yellow w-full" onClick={addItemToCart} disabled={(product.lowest_price.price == 0 ? true : false)} > - + Keranjang + Keranjang + </button> + <button + onClick={checkoutProduct} + className="btn-solid-red w-full" + > + Beli </button> </div> </div> @@ -230,7 +249,7 @@ export default function ProductDetail({ product }) { <p className="text-gray-800">{product.variant_total} Varian</p> </div> <div className="flex py-2 justify-between items-center gap-x-1"> - <h3 className="text-gray-900">Nomor SKU</h3> + <h3 className="text-gray-900">No. SKU</h3> <p className="text-gray-800" id="sku_number">SKU-{activeVariant.id}</p> </div> <div className="flex py-2 justify-between items-center gap-x-1"> @@ -239,9 +258,14 @@ export default function ProductDetail({ product }) { </div> <div className="flex py-2 justify-between items-center gap-x-1"> <h3 className="text-gray-900">Stok</h3> - <p className="text-gray-800" id="stock"> - {activeVariant.stock > 0 ? (activeVariant.stock > 5 ? 'Lebih dari 5' : 'Kurang dari 5') : '0'} - </p> + <div className="flex gap-x-2" id="stock"> + {activeVariant.stock > 0 ? (activeVariant.stock > 5 && ( + <> + <div className="badge-solid-red">Ready Stock</div> + <div className="badge-gray">{activeVariant.stock > 5 ? '> 5' : '< 5'}</div> + </> + )) : '0'} + </div> </div> <div className="flex py-2 justify-between items-center gap-x-1"> <h3 className="text-gray-900">Berat Barang</h3> @@ -259,7 +283,7 @@ export default function ProductDetail({ product }) { <LineDivider /> <div className="p-4"> - <h2 className="font-bold mb-4">Produk Lainnya</h2> + <h2 className="font-bold mb-4">Kamu Mungkin Juga Suka</h2> <ProductSlider products={similarProducts}/> </div> |
