diff options
| -rw-r--r-- | src/core/components/elements/Footer/BasicFooter.jsx | 88 | ||||
| -rw-r--r-- | src/core/components/elements/Popup/BottomPopup.jsx | 55 | ||||
| -rw-r--r-- | src/lib/cart/components/Cart.jsx | 218 | ||||
| -rw-r--r-- | src/lib/checkout/components/Checkout.jsx | 449 | ||||
| -rw-r--r-- | src/lib/product/components/Product/ProductDesktop.jsx | 2 | ||||
| -rw-r--r-- | src/lib/quotation/components/Quotation.jsx | 273 | ||||
| -rw-r--r-- | src/pages/shop/checkout/index.jsx | 17 | ||||
| -rw-r--r-- | src/pages/shop/quotation/index.jsx | 17 | ||||
| -rw-r--r-- | src/styles/globals.css | 38 |
9 files changed, 847 insertions, 310 deletions
diff --git a/src/core/components/elements/Footer/BasicFooter.jsx b/src/core/components/elements/Footer/BasicFooter.jsx index 8819b87b..25f8f552 100644 --- a/src/core/components/elements/Footer/BasicFooter.jsx +++ b/src/core/components/elements/Footer/BasicFooter.jsx @@ -7,36 +7,70 @@ import { } from '@heroicons/react/24/outline' import Link from '../Link/Link' import MobileView from '../../views/MobileView' +import DesktopView from '../../views/DesktopView' const BasicFooter = () => { return ( - <MobileView> - <footer className='flex flex-wrap p-4 bg-gray_r-3 text-caption-1'> - <div className='w-1/2 flex flex-col gap-y-4 pr-1.5'> - <div> - <NextImage src={IndoteknikLogo} alt='Logo Indoteknik' width={90} height={30} /> + <> + <MobileView> + <footer className='flex flex-wrap p-4 bg-gray_r-3 text-caption-1'> + <div className='w-1/2 flex flex-col gap-y-4 pr-1.5'> + <div> + <NextImage src={IndoteknikLogo} alt='Logo Indoteknik' width={90} height={30} /> + + <div className='font-semibold mt-2'>PT. Indoteknik Dotcom Gemilang</div> + </div> + + <OfficeLocation /> + <WarehouseLocation /> + <InformationCenter /> + <OpenHours /> + <SocialMedias /> + </div> - <div className='font-semibold mt-2'>PT. Indoteknik Dotcom Gemilang</div> + <div className='w-1/2 flex flex-col gap-y-4 pl-1.5'> + <AboutUs /> + <CustomerGuide /> + <Payments /> </div> - <OfficeLocation /> - <WarehouseLocation /> - <InformationCenter /> - <OpenHours /> - <SocialMedias /> - </div> - - <div className='w-1/2 flex flex-col gap-y-4 pl-1.5'> - <AboutUs /> - <CustomerGuide /> - <Payments /> - </div> - - <div className='w-full mt-4 leading-5 text-caption-2 text-gray_r-12/80'> - Copyright © 2007 - 2022, PT. Indoteknik Dotcom Gemilang - </div> - </footer> - </MobileView> + <div className='w-full mt-4 leading-5 text-caption-2 text-gray_r-12/80'> + Copyright © 2007 - {new Date().getFullYear()}, PT. Indoteknik Dotcom Gemilang + </div> + </footer> + </MobileView> + + <DesktopView> + <footer className='bg-gray_r-3 py-6'> + <div className='container mx-auto flex flex-wrap justify-between'> + <div className='w-3/12'> + <NextImage src={IndoteknikLogo} alt='Logo Indoteknik' width={180} height={60} /> + <div className='font-semibold mt-2 mb-4'>PT. Indoteknik Dotcom Gemilang</div> + <InformationCenter /> + </div> + <CustomerGuide /> + <AboutUs /> + <div className='w-3/12'> + <div className='grid grid-cols-1 gap-y-4'> + <OfficeLocation /> + <WarehouseLocation /> + <OpenHours /> + <Payments /> + </div> + </div> + <hr className='w-full my-4 border-gray_r-7' /> + <div className='w-full flex justify-between items-center'> + <div className='text-caption-1'> + Copyright © 2007 - {new Date().getFullYear()}, PT. Indoteknik Dotcom Gemilang + </div> + <div> + <SocialMedias /> + </div> + </div> + </div> + </footer> + </DesktopView> + </> ) } @@ -154,11 +188,11 @@ const OpenHours = () => ( <div> <div className={headerClassName}>Jam Operasional</div> <ul className='flex flex-col gap-y-1'> - <li> + <li className='block md:flex md:gap-x-2'> <div className='text-gray_r-12'>Senin - Jumat:</div> <div className='text-gray_r-12/80'>08:30 - 17:00</div> </li> - <li> + <li className='block md:flex md:gap-x-2'> <div className='text-gray_r-12'>Sabtu:</div> <div className='text-gray_r-12/80'>08:30 - 14:00</div> </li> @@ -168,7 +202,7 @@ const OpenHours = () => ( const SocialMedias = () => ( <div> - <div className={headerClassName}>Temukan Kami</div> + <div className={headerClassName + 'block md:hidden'}>Temukan Kami</div> <div className='flex flex-wrap gap-2'> <NextImage src='/images/socials/Whatsapp.png' alt='Whatsapp Logo' width={24} height={24} /> <NextImage src='/images/socials/Facebook.png' alt='Facebook Logo' width={24} height={24} /> diff --git a/src/core/components/elements/Popup/BottomPopup.jsx b/src/core/components/elements/Popup/BottomPopup.jsx index 24366802..95c75473 100644 --- a/src/core/components/elements/Popup/BottomPopup.jsx +++ b/src/core/components/elements/Popup/BottomPopup.jsx @@ -1,6 +1,8 @@ import { XMarkIcon } from '@heroicons/react/24/outline' import { AnimatePresence, motion } from 'framer-motion' import { useEffect } from 'react' +import MobileView from '../../views/MobileView' +import DesktopView from '../../views/DesktopView' const transition = { ease: 'linear', duration: 0.2 } @@ -26,24 +28,41 @@ const BottomPopup = ({ children, active = false, title, close }) => { transition={transition} onClick={close} /> - <motion.div - initial={{ bottom: '-100%' }} - animate={{ bottom: 0 }} - exit={{ bottom: '-100%' }} - transition={transition} - className='fixed left-0 w-full border-t border-gray_r-6 rounded-t-xl z-[60] p-4 pt-0 bg-white' - > - <div className='flex justify-between py-4'> - <div className='font-semibold text-h-sm'>{title}</div> - <button - type='button' - onClick={close} - > - <XMarkIcon className='w-5 stroke-2' /> - </button> - </div> - {children} - </motion.div> + <MobileView> + <motion.div + initial={{ bottom: '-100%' }} + animate={{ bottom: 0 }} + exit={{ bottom: '-100%' }} + transition={transition} + className='fixed left-0 w-full border-t border-gray_r-6 rounded-t-xl z-[60] p-4 pt-0 bg-white' + > + <div className='flex justify-between py-4'> + <div className='font-semibold text-h-sm'>{title}</div> + <button type='button' onClick={close}> + <XMarkIcon className='w-5 stroke-2' /> + </button> + </div> + {children} + </motion.div> + </MobileView> + + <DesktopView> + <motion.div + initial={{ bottom: '35%', opacity: 0 }} + animate={{ bottom: '50%', opacity: 1 }} + exit={{ bottom: '35%', opacity: 0 }} + transition={transition} + className='fixed left-1/2 -translate-x-1/2 w-2/5 border border-gray_r-6 rounded-xl z-[60] p-4 pt-0 bg-white' + > + <div className='flex justify-between py-4'> + <div className='font-semibold text-h-sm'>{title}</div> + <button type='button' onClick={close}> + <XMarkIcon className='w-5 stroke-2' /> + </button> + </div> + {children} + </motion.div> + </DesktopView> </> )} </AnimatePresence> diff --git a/src/lib/cart/components/Cart.jsx b/src/lib/cart/components/Cart.jsx index b48b41fc..8bd9e362 100644 --- a/src/lib/cart/components/Cart.jsx +++ b/src/lib/cart/components/Cart.jsx @@ -1,6 +1,7 @@ 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' @@ -118,6 +119,33 @@ const Cart = () => { 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'> + <button + className='btn-solid-red flex-1' + type='button' + onClick={() => deleteProduct(deleteConfirmation?.id)} + > + Ya, Hapus + </button> + <button + className='btn-light flex-1' + type='button' + onClick={() => setDeleteConfirmation(null)} + > + Batal + </button> + </div> + </BottomPopup> + <MobileView> <div className='pt-4'> <div className='flex justify-between mb-4 px-4'> @@ -255,41 +283,169 @@ const Cart = () => { </div> </div> </div> - - <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'> - <button - className='btn-solid-red flex-1' - type='button' - onClick={() => deleteProduct(deleteConfirmation?.id)} - > - Ya, Hapus - </button> - <button - className='btn-light flex-1' - type='button' - onClick={() => setDeleteConfirmation(null)} - > - Batal - </button> - </div> - </BottomPopup> </div> </MobileView> <DesktopView> - <div className='container mx-auto pt-10'> - <div className='flex'> - <div className='w-9/12'> - <h1 className='text-title-sm font-semibold'>Daftar Produk Belanja</h1> + <div className='container mx-auto py-10 flex'> + <div className='w-9/12 border border-gray_r-6 rounded bg-white p-4'> + <h1 className='text-title-sm font-semibold mb-6'>Daftar Produk Belanja</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 && (!products || products?.length == 0) && ( + <tr> + <td colSpan={6}>Keranjang belanja anda masih kosong</td> + </tr> + )} + {products?.map((product) => ( + <tr key={product.id}> + <td> + <button + type='button' + className='flex items-center mr-2' + onClick={() => toggleSelected(product.id)} + > + {!product?.selected && ( + <div className='w-5 h-5 border border-gray_r-11 rounded' /> + )} + {product?.selected && ( + <CheckIcon className='border bg-red_r-10 w-5 text-white' /> + )} + </button> + </td> + <td className='flex'> + <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='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-red_r-11 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! + </div> + </div> + </div> + + <div className='w-3/12 pl-4'> + <div className='sticky top-32 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-red_r-11 font-semibold'> + + {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> diff --git a/src/lib/checkout/components/Checkout.jsx b/src/lib/checkout/components/Checkout.jsx index ef0b1d54..57d217a7 100644 --- a/src/lib/checkout/components/Checkout.jsx +++ b/src/lib/checkout/components/Checkout.jsx @@ -16,8 +16,9 @@ import checkoutApi from '../api/checkoutApi' import { useRouter } from 'next/router' import VariantGroupCard from '@/lib/variant/components/VariantGroupCard' import axios from 'axios' -import Script from 'next/script' import Image from '@/core/components/elements/Image/Image' +import MobileView from '@/core/components/views/MobileView' +import DesktopView from '@/core/components/views/DesktopView' const Checkout = () => { const router = useRouter() @@ -99,17 +100,12 @@ const Checkout = () => { } }, [products]) - const poNumber = useRef('') - const poFile = useRef('') + const poNumber = useRef(null) + const poFile = useRef(null) const [isLoading, setIsLoading] = useState(false) const checkout = async () => { - if (!paymentMethod) { - toast.error('Metode pembayaran harus dipilih', { position: 'bottom-center' }) - return - } - const file = poFile.current.files[0] if (typeof file !== 'undefined' && file.size > 5000000) { toast.error('Maksimal ukuran file adalah 5MB', { position: 'bottom-center' }) @@ -130,179 +126,331 @@ const Checkout = () => { if (typeof file !== 'undefined') data.po_file = await getFileBase64(file) const isCheckouted = await checkoutApi({ data }) - setIsLoading(false) if (!isCheckouted?.id) { toast.error('Gagal melakukan transaksi, terjadi kesalahan internal') return } for (const product of products) deleteItemCart({ productId: product.id }) - if (paymentMethod == 'midtrans') { - const payment = await axios.post( - `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/midtrans-payment?transactionId=${isCheckouted.id}` - ) - window.location.href = payment.data.redirectUrl - } else { - router.push(`/shop/checkout/finish?order_id=${isCheckouted.name}`) - } + const payment = await axios.post( + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/midtrans-payment?transactionId=${isCheckouted.id}` + ) + setIsLoading(false) + window.location.href = payment.data.redirectUrl } return ( <> - <div className='p-4'> - <Alert - type='info' - className='text-caption-2 flex gap-x-3' - > - <div> - <ExclamationCircleIcon className='w-7 text-blue-700' /> - </div> - <span className='leading-5'> - Jika mengalami kesulitan dalam melakukan pembelian di website Indoteknik. Hubungi kami - disini - </span> - </Alert> - </div> + <MobileView> + <div className='p-4'> + <Alert type='info' className='text-caption-2 flex gap-x-3'> + <div> + <ExclamationCircleIcon className='w-7 text-blue-700' /> + </div> + <span className='leading-5'> + Jika mengalami kesulitan dalam melakukan pembelian di website Indoteknik. Hubungi kami + disini + </span> + </Alert> + </div> - <Divider /> + <Divider /> - <SectionAddress - label='Alamat Pengiriman' - url='/my/address?select=shipping' - address={selectedAddress.shipping} - /> + <SectionAddress + label='Alamat Pengiriman' + url='/my/address?select=shipping' + address={selectedAddress.shipping} + /> - <Divider /> + <Divider /> - <div className='p-4 flex flex-col gap-y-4'> - {products && ( - <VariantGroupCard - openOnClick={false} - variants={products} - /> - )} - </div> + <div className='p-4 flex flex-col gap-y-4'> + {products && <VariantGroupCard openOnClick={false} variants={products} />} + </div> - <Divider /> + <Divider /> - <div className='p-4'> - <div className='flex justify-between items-center'> - <div className='font-medium'>Ringkasan Pesanan</div> - <div className='text-gray_r-11 text-caption-1'>{products?.length} Barang</div> - </div> - <hr className='my-4 border-gray_r-6' /> - <div className='flex flex-col gap-y-4'> - <div className='flex gap-x-2 justify-between'> - <div className='text-gray_r-11'>Total Belanja</div> - <div>{currencyFormat(totalAmount)}</div> - </div> - <div className='flex gap-x-2 justify-between'> - <div className='text-gray_r-11'>Total Diskon</div> - <div className='text-red_r-11'>- {currencyFormat(totalDiscountAmount)}</div> + <div className='p-4'> + <div className='flex justify-between items-center'> + <div className='font-medium'>Ringkasan Pesanan</div> + <div className='text-gray_r-11 text-caption-1'>{products?.length} Barang</div> </div> - <div className='flex gap-x-2 justify-between'> - <div className='text-gray_r-11'>Subtotal</div> - <div>{currencyFormat(totalAmount - totalDiscountAmount)}</div> + <hr className='my-4 border-gray_r-6' /> + <div className='flex flex-col gap-y-4'> + <div className='flex gap-x-2 justify-between'> + <div className='text-gray_r-11'>Total Belanja</div> + <div>{currencyFormat(totalAmount)}</div> + </div> + <div className='flex gap-x-2 justify-between'> + <div className='text-gray_r-11'>Total Diskon</div> + <div className='text-red_r-11'>- {currencyFormat(totalDiscountAmount)}</div> + </div> + <div className='flex gap-x-2 justify-between'> + <div className='text-gray_r-11'>Subtotal</div> + <div>{currencyFormat(totalAmount - totalDiscountAmount)}</div> + </div> + <div className='flex gap-x-2 justify-between'> + <div className='text-gray_r-11'>PPN 11% (Incl.)</div> + <div>{currencyFormat((totalAmount - totalDiscountAmount) * 0.11)}</div> + </div> </div> - <div className='flex gap-x-2 justify-between'> - <div className='text-gray_r-11'>PPN 11% (Incl.)</div> - <div>{currencyFormat((totalAmount - totalDiscountAmount) * 0.11)}</div> + <hr className='my-4 border-gray_r-6' /> + <div className='flex gap-x-2 justify-between mb-4'> + <div>Grand Total</div> + <div className='font-semibold text-gray_r-12'> + {currencyFormat(totalAmount - totalDiscountAmount)} + </div> </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'> + Dengan melakukan pembelian melalui website Indoteknik, saya menyetujui{' '} + <Link href='/' className='inline font-normal'> + Syarat & Ketentuan + </Link>{' '} + yang berlaku + </p> </div> - <hr className='my-4 border-gray_r-6' /> - <div className='flex gap-x-2 justify-between mb-4'> - <div>Grand Total</div> - <div className='font-semibold text-gray_r-12'> - {currencyFormat(totalAmount - totalDiscountAmount)} + + <Divider /> + + <SectionAddress + label='Alamat Penagihan' + url='/my/address?select=invoicing' + address={selectedAddress.invoicing} + /> + + <Divider /> + + <div className='p-4'> + <div className='font-medium mb-4'>Metode Pembayaran</div> + <div className='flex flex-col gap-y-3'> + <div + className={`p-2 idt-transition border rounded text-gray_r-12/80 ${ + paymentMethod == 'manual' ? 'border-yellow_r-8 bg-yellow_r-2' : 'border-gray_r-6' + }`} + onClick={() => setPaymentMethod('manual')} + > + Bank BCA (PT. Indoteknik Dotcom) + <div className='mt-1'>8870-4000-81</div> + </div> + <div + className={`p-2 idt-transition border rounded ${ + paymentMethod == 'midtrans' ? 'border-yellow_r-8 bg-yellow_r-2' : 'border-gray_r-6' + }`} + onClick={() => setPaymentMethod('midtrans')} + > + <Image src='/images/payments/midtrans.jpg' alt='Midtrans Payment' /> + </div> </div> </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'> - Dengan melakukan pembelian melalui website Indoteknik, saya menyetujui{' '} - <Link - href='/' - className='inline font-normal' - > - Syarat & Ketentuan - </Link>{' '} - yang berlaku - </p> - </div> - <Divider /> - - <SectionAddress - label='Alamat Penagihan' - url='/my/address?select=invoicing' - address={selectedAddress.invoicing} - /> + <Divider /> + + <div className='p-4'> + <div className='font-medium'>Purchase Order</div> + + <div className='mt-4 flex gap-x-3'> + <div className='w-6/12'> + <label className='form-label font-normal'>Dokumen PO</label> + <input + type='file' + className='form-input mt-2 h-12' + accept='image/*,application/pdf' + ref={poFile} + /> + </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' ref={poNumber} /> + </div> + </div> + <p className='text-caption-2 text-gray_r-11 mt-2'>Ukuran dokumen PO Maksimal 5MB</p> + </div> - <Divider /> + <Divider /> - <div className='p-4'> - <div className='font-medium mb-4'>Metode Pembayaran</div> - <div className='flex flex-col gap-y-3'> - <div - className={`p-2 idt-transition border rounded text-gray_r-12/80 ${ - paymentMethod == 'manual' ? 'border-yellow_r-8 bg-yellow_r-2' : 'border-gray_r-6' - }`} - onClick={() => setPaymentMethod('manual')} - > - Bank BCA (PT. Indoteknik Dotcom) - <div className='mt-1'>8870-4000-81</div> - </div> - <div - className={`p-2 idt-transition border rounded ${ - paymentMethod == 'midtrans' ? 'border-yellow_r-8 bg-yellow_r-2' : 'border-gray_r-6' - }`} - onClick={() => setPaymentMethod('midtrans')} + <div className='flex gap-x-3 p-4'> + <button + className='flex-1 btn-yellow' + onClick={checkout} + disabled={isLoading || !products || products?.length == 0} > - <Image - src='/images/payments/midtrans.jpg' - alt='Midtrans Payment' - /> - </div> + {isLoading ? 'Loading...' : 'Bayar'} + </button> </div> - </div> - - <Divider /> + </MobileView> + + <DesktopView> + <div className='container mx-auto py-10 flex'> + <div className='w-3/4 border border-gray_r-6 rounded bg-white'> + <SectionAddress + label='Alamat Pengiriman' + url='/my/address?select=shipping' + address={selectedAddress.shipping} + /> - <div className='p-4'> - <div className='font-medium'>Purchase Order</div> + <Divider /> - <div className='mt-4 flex gap-x-3'> - <div className='w-6/12'> - <label className='form-label font-normal'>Dokumen PO</label> - <input - type='file' - className='form-input mt-2 h-12' - accept='image/*,application/pdf' - ref={poFile} + <SectionAddress + label='Alamat Penagihan' + url='/my/address?select=invoicing' + address={selectedAddress.invoicing} /> + + <Divider /> + + <div className='p-4'> + <div className='font-medium'>Detail Pesanan</div> + <table className='table-checkout'> + <thead> + <tr> + <th>Nama Produk</th> + <th>Jumlah</th> + <th>Harga</th> + <th>Subtotal</th> + </tr> + </thead> + <tbody> + {products?.map((product) => ( + <tr key={product.id}> + <td className='flex'> + <div 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' + /> + </div> + <div className='px-2 text-left'> + <div className='line-clamp-2 leading-6 !text-gray_r-12 font-normal'> + {product?.parent?.name} + </div> + <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} + disabled + /> + </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-red_r-11 font-medium'> + {currencyFormat(product?.price?.priceDiscount * product?.quantity)} + </div> + </td> + </tr> + ))} + </tbody> + </table> + </div> </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' - ref={poNumber} - /> + + <div className='w-1/4 pl-4'> + <div className='border border-gray_r-6 bg-white rounded p-4'> + <div className='flex justify-between items-center'> + <div className='font-medium'>Ringkasan Pesanan</div> + <div className='text-gray_r-11 text-caption-1'>{products?.length} Barang</div> + </div> + + <hr className='my-4 border-gray_r-6' /> + + <div className='flex flex-col gap-y-4'> + <div className='flex gap-x-2 justify-between'> + <div className='text-gray_r-11'>Total Belanja</div> + <div>{currencyFormat(totalAmount)}</div> + </div> + <div className='flex gap-x-2 justify-between'> + <div className='text-gray_r-11'>Total Diskon</div> + <div className='text-red_r-11'>- {currencyFormat(totalDiscountAmount)}</div> + </div> + <div className='flex gap-x-2 justify-between'> + <div className='text-gray_r-11'>Subtotal</div> + <div>{currencyFormat(totalAmount - totalDiscountAmount)}</div> + </div> + <div className='flex gap-x-2 justify-between'> + <div className='text-gray_r-11'>PPN 11% (Incl.)</div> + <div>{currencyFormat((totalAmount - totalDiscountAmount) * 0.11)}</div> + </div> + </div> + + <hr className='my-4 border-gray_r-6' /> + + <div className='flex gap-x-2 justify-between mb-4'> + <div>Grand Total</div> + <div className='font-semibold text-gray_r-12'> + {currencyFormat(totalAmount - totalDiscountAmount)} + </div> + </div> + <p className='text-caption-2 text-gray_r-11 mb-2'> + *) Belum termasuk biaya pengiriman + </p> + <p className='text-caption-2 text-gray_r-11 leading-5'> + Dengan melakukan pembelian melalui website Indoteknik, saya menyetujui{' '} + <Link href='/' className='inline font-normal'> + Syarat & Ketentuan + </Link>{' '} + yang berlaku + </p> + + <hr className='my-4 border-gray_r-6' /> + + <div className='font-medium mt-4'> + Purchase Order <span className='font-normal text-gray_r-11'>(Opsional)</span> + </div> + + <div className='mt-4 flex gap-x-3'> + <div className='w-6/12'> + <label className='form-label font-normal'>Dokumen PO</label> + <input + type='file' + className='form-input mt-2 h-12' + accept='image/*,application/pdf' + ref={poFile} + /> + </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' ref={poNumber} /> + </div> + </div> + <p className='text-caption-2 text-gray_r-11 mt-2'>Ukuran dokumen PO Maksimal 5MB</p> + + <hr className='my-4 border-gray_r-6' /> + + <button + className='w-full btn-yellow mt-4' + onClick={checkout} + disabled={isLoading || !products || products?.length == 0} + > + {isLoading ? 'Loading...' : 'Lanjut Pembayaran'} + </button> + </div> </div> </div> - <p className='text-caption-2 text-gray_r-11 mt-2'>Ukuran dokumen PO Maksimal 5MB</p> - </div> - - <Divider /> - - <div className='flex gap-x-3 p-4'> - <button - className='flex-1 btn-yellow' - onClick={checkout} - disabled={isLoading || !products || products?.length == 0} - > - {isLoading ? 'Loading...' : 'Bayar'} - </button> - </div> + </DesktopView> </> ) } @@ -311,10 +459,7 @@ const SectionAddress = ({ address, label, url }) => ( <div className='p-4'> <div className='flex justify-between items-center'> <div className='font-medium'>{label}</div> - <Link - className='text-caption-1' - href={url} - > + <Link className='text-caption-1' href={url}> Pilih Alamat Lain </Link> </div> diff --git a/src/lib/product/components/Product/ProductDesktop.jsx b/src/lib/product/components/Product/ProductDesktop.jsx index ac17ec6e..98b40400 100644 --- a/src/lib/product/components/Product/ProductDesktop.jsx +++ b/src/lib/product/components/Product/ProductDesktop.jsx @@ -154,6 +154,7 @@ const ProductDesktop = ({ product, wishlist, toggleWishlist }) => { <thead> <tr> <th>Part Number</th> + <th>Varian</th> <th>Harga</th> <th>Jumlah</th> <th>Action</th> @@ -163,6 +164,7 @@ const ProductDesktop = ({ product, wishlist, toggleWishlist }) => { {product.variants.map((variant) => ( <tr key={variant.id}> <td>{variant.code}</td> + <td>{variant.attributes.join(', ')}</td> <td> {variant.price.discountPercentage > 0 && ( <> diff --git a/src/lib/quotation/components/Quotation.jsx b/src/lib/quotation/components/Quotation.jsx index b6e276a3..3054616c 100644 --- a/src/lib/quotation/components/Quotation.jsx +++ b/src/lib/quotation/components/Quotation.jsx @@ -12,6 +12,9 @@ import { toast } from 'react-hot-toast' import checkoutApi from '@/lib/checkout/api/checkoutApi' import { useRouter } from 'next/router' import VariantGroupCard from '@/lib/variant/components/VariantGroupCard' +import MobileView from '@/core/components/views/MobileView' +import DesktopView from '@/core/components/views/DesktopView' +import Image from '@/core/components/elements/Image/Image' const Quotation = () => { const router = useRouter() @@ -77,89 +80,207 @@ const Quotation = () => { return ( <> - <div className='p-4'> - <Alert - type='info' - className='text-caption-2 flex gap-x-3' - > - <div> - <ExclamationCircleIcon className='w-7 text-blue-700' /> - </div> - <span className='leading-5'> - Jika mengalami kesulitan dalam melakukan pembelian di website Indoteknik. Hubungi kami - disini - </span> - </Alert> - </div> - - <Divider /> - - <div className='p-4 flex flex-col gap-y-4'> - {products && ( - <VariantGroupCard - openOnClick={false} - variants={products} - /> - )} - </div> - - <Divider /> - - <div className='p-4'> - <div className='flex justify-between items-center'> - <div className='font-medium'>Ringkasan Penawaran</div> - <div className='text-gray_r-11 text-caption-1'>{products?.length} Barang</div> + <MobileView> + <div className='p-4'> + <Alert type='info' className='text-caption-2 flex gap-x-3'> + <div> + <ExclamationCircleIcon className='w-7 text-blue-700' /> + </div> + <span className='leading-5'> + Jika mengalami kesulitan dalam melakukan pembelian di website Indoteknik. Hubungi kami + disini + </span> + </Alert> </div> - <hr className='my-4 border-gray_r-6' /> - <div className='flex flex-col gap-y-4'> - <div className='flex gap-x-2 justify-between'> - <div className='text-gray_r-11'>Total Belanja</div> - <div>{currencyFormat(totalAmount)}</div> - </div> - <div className='flex gap-x-2 justify-between'> - <div className='text-gray_r-11'>Total Diskon</div> - <div className='text-red_r-11'>- {currencyFormat(totalDiscountAmount)}</div> + + <Divider /> + + <div className='p-4 flex flex-col gap-y-4'> + {products && <VariantGroupCard openOnClick={false} variants={products} />} + </div> + + <Divider /> + + <div className='p-4'> + <div className='flex justify-between items-center'> + <div className='font-medium'>Ringkasan Penawaran</div> + <div className='text-gray_r-11 text-caption-1'>{products?.length} Barang</div> </div> - <div className='flex gap-x-2 justify-between'> - <div className='text-gray_r-11'>Subtotal</div> - <div>{currencyFormat(totalAmount - totalDiscountAmount)}</div> + <hr className='my-4 border-gray_r-6' /> + <div className='flex flex-col gap-y-4'> + <div className='flex gap-x-2 justify-between'> + <div className='text-gray_r-11'>Total Belanja</div> + <div>{currencyFormat(totalAmount)}</div> + </div> + <div className='flex gap-x-2 justify-between'> + <div className='text-gray_r-11'>Total Diskon</div> + <div className='text-red_r-11'>- {currencyFormat(totalDiscountAmount)}</div> + </div> + <div className='flex gap-x-2 justify-between'> + <div className='text-gray_r-11'>Subtotal</div> + <div>{currencyFormat(totalAmount - totalDiscountAmount)}</div> + </div> + <div className='flex gap-x-2 justify-between'> + <div className='text-gray_r-11'>PPN 11% (Incl.)</div> + <div>{currencyFormat((totalAmount - totalDiscountAmount) * 0.11)}</div> + </div> </div> - <div className='flex gap-x-2 justify-between'> - <div className='text-gray_r-11'>PPN 11% (Incl.)</div> - <div>{currencyFormat((totalAmount - totalDiscountAmount) * 0.11)}</div> + <hr className='my-4 border-gray_r-6' /> + <div className='flex gap-x-2 justify-between mb-4'> + <div>Grand Total</div> + <div className='font-semibold text-gray_r-12'> + {currencyFormat(totalAmount - totalDiscountAmount)} + </div> </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'> + Dengan melakukan pembelian melalui website Indoteknik, saya menyetujui{' '} + <Link href='/' className='inline font-normal'> + Syarat & Ketentuan + </Link>{' '} + yang berlaku + </p> + </div> + + <Divider /> + + <div className='flex gap-x-3 p-4'> + <button className='flex-1 btn-yellow' onClick={checkout} disabled={isLoading}> + {isLoading ? 'Loading...' : 'Quotation'} + </button> </div> - <hr className='my-4 border-gray_r-6' /> - <div className='flex gap-x-2 justify-between mb-4'> - <div>Grand Total</div> - <div className='font-semibold text-gray_r-12'> - {currencyFormat(totalAmount - totalDiscountAmount)} + </MobileView> + + <DesktopView> + <div className='container mx-auto py-10 flex'> + <div className='w-3/4 border border-gray_r-6 rounded bg-white p-4'> + <div className='font-medium'>Detail Barang</div> + <table className='table-checkout'> + <thead> + <tr> + <th>Nama Produk</th> + <th>Jumlah</th> + <th>Harga</th> + <th>Subtotal</th> + </tr> + </thead> + <tbody> + {products?.map((product) => ( + <tr key={product.id}> + <td className='flex'> + <div 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' + /> + </div> + <div className='px-2 text-left'> + <div className='line-clamp-2 leading-6 !text-gray_r-12 font-normal'> + {product?.parent?.name} + </div> + <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} + disabled + /> + </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-red_r-11 font-medium'> + {currencyFormat(product?.price?.priceDiscount * product?.quantity)} + </div> + </td> + </tr> + ))} + </tbody> + </table> + </div> + + <div className='w-1/4 pl-4'> + <div className='border border-gray_r-6 bg-white rounded p-4'> + <div className='flex justify-between items-center'> + <div className='font-medium'>Ringkasan Pesanan</div> + <div className='text-gray_r-11 text-caption-1'>{products?.length} Barang</div> + </div> + + <hr className='my-4 border-gray_r-6' /> + + <div className='flex flex-col gap-y-4'> + <div className='flex gap-x-2 justify-between'> + <div className='text-gray_r-11'>Total Belanja</div> + <div>{currencyFormat(totalAmount)}</div> + </div> + <div className='flex gap-x-2 justify-between'> + <div className='text-gray_r-11'>Total Diskon</div> + <div className='text-red_r-11'>- {currencyFormat(totalDiscountAmount)}</div> + </div> + <div className='flex gap-x-2 justify-between'> + <div className='text-gray_r-11'>Subtotal</div> + <div>{currencyFormat(totalAmount - totalDiscountAmount)}</div> + </div> + <div className='flex gap-x-2 justify-between'> + <div className='text-gray_r-11'>PPN 11% (Incl.)</div> + <div>{currencyFormat((totalAmount - totalDiscountAmount) * 0.11)}</div> + </div> + </div> + + <hr className='my-4 border-gray_r-6' /> + + <div className='flex gap-x-2 justify-between mb-4'> + <div>Grand Total</div> + <div className='font-semibold text-gray_r-12'> + {currencyFormat(totalAmount - totalDiscountAmount)} + </div> + </div> + <p className='text-caption-2 text-gray_r-11 mb-2'> + *) Belum termasuk biaya pengiriman + </p> + <p className='text-caption-2 text-gray_r-11 leading-5'> + Dengan melakukan pembelian melalui website Indoteknik, saya menyetujui{' '} + <Link href='/' className='inline font-normal'> + Syarat & Ketentuan + </Link>{' '} + yang berlaku + </p> + + <hr className='my-4 border-gray_r-6' /> + + <button + className='w-full btn-yellow mt-4' + onClick={checkout} + disabled={isLoading || !products || products?.length == 0} + > + {isLoading ? 'Loading...' : 'Buat Quotation'} + </button> + </div> </div> </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'> - Dengan melakukan pembelian melalui website Indoteknik, saya menyetujui{' '} - <Link - href='/' - className='inline font-normal' - > - Syarat & Ketentuan - </Link>{' '} - yang berlaku - </p> - </div> - - <Divider /> - - <div className='flex gap-x-3 p-4'> - <button - className='flex-1 btn-yellow' - onClick={checkout} - disabled={isLoading} - > - {isLoading ? 'Loading...' : 'Quotation'} - </button> - </div> + </DesktopView> </> ) } diff --git a/src/pages/shop/checkout/index.jsx b/src/pages/shop/checkout/index.jsx index 015a11b3..30d2c730 100644 --- a/src/pages/shop/checkout/index.jsx +++ b/src/pages/shop/checkout/index.jsx @@ -1,13 +1,24 @@ import AppLayout from '@/core/components/layouts/AppLayout' +import BasicLayout from '@/core/components/layouts/BasicLayout' +import DesktopView from '@/core/components/views/DesktopView' +import MobileView from '@/core/components/views/MobileView' import IsAuth from '@/lib/auth/components/IsAuth' import CheckoutComponent from '@/lib/checkout/components/Checkout' export default function Checkout() { return ( <IsAuth> - <AppLayout title='Checkout'> - <CheckoutComponent /> - </AppLayout> + <MobileView> + <AppLayout title='Checkout'> + <CheckoutComponent /> + </AppLayout> + </MobileView> + + <DesktopView> + <BasicLayout> + <CheckoutComponent /> + </BasicLayout> + </DesktopView> </IsAuth> ) } diff --git a/src/pages/shop/quotation/index.jsx b/src/pages/shop/quotation/index.jsx index ff8b8644..efb35db9 100644 --- a/src/pages/shop/quotation/index.jsx +++ b/src/pages/shop/quotation/index.jsx @@ -1,13 +1,24 @@ import AppLayout from '@/core/components/layouts/AppLayout' +import BasicLayout from '@/core/components/layouts/BasicLayout' +import DesktopView from '@/core/components/views/DesktopView' +import MobileView from '@/core/components/views/MobileView' import IsAuth from '@/lib/auth/components/IsAuth' import QuotationComponent from '@/lib/quotation/components/Quotation' export default function Quotation() { return ( <IsAuth> - <AppLayout title='Quotation'> - <QuotationComponent /> - </AppLayout> + <MobileView> + <AppLayout title='Quotation'> + <QuotationComponent /> + </AppLayout> + </MobileView> + + <DesktopView> + <BasicLayout> + <QuotationComponent /> + </BasicLayout> + </DesktopView> </IsAuth> ) } diff --git a/src/styles/globals.css b/src/styles/globals.css index 6e4d46e4..90761309 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -394,6 +394,10 @@ button { @apply !border-yellow_r-9; } +nav:has(> div.overlay) { + @apply z-0; +} + .table-specification { @apply max-h-[500px] overflow-y-auto border border-gray_r-6; } @@ -425,6 +429,40 @@ button { @apply odd:bg-gray_r-3 even:bg-gray_r-1; } +.table-cart, +.table-checkout { + @apply w-full + table-auto + border-collapse; +} + +.table-cart tr, +.table-checkout tr { + @apply border-y + border-gray_r-6 + first:border-t-0; +} + +.table-cart th, +.table-cart td, +.table-checkout th, +.table-checkout td { + @apply py-4 + px-3 + text-center + text-gray_r-12/90; +} + +.table-cart th, +.table-cart td { + @apply first:w-12; +} + +.table-cart th, +.table-checkout th { + @apply font-medium; +} + .navbar-user-dropdown-button { @apply flex-1 flex |
