diff options
| -rw-r--r-- | src/core/components/elements/Sidebar/Sidebar.jsx | 8 | ||||
| -rw-r--r-- | src/core/components/layouts/AppLayout.jsx | 4 | ||||
| -rw-r--r-- | src/lib/auth/components/Activate.jsx | 2 | ||||
| -rw-r--r-- | src/lib/auth/components/Login.jsx | 2 | ||||
| -rw-r--r-- | src/lib/auth/components/Register.jsx | 2 | ||||
| -rw-r--r-- | src/lib/checkout/components/Checkout.jsx | 49 | ||||
| -rw-r--r-- | src/lib/checkout/components/FinishCheckout.jsx | 11 | ||||
| -rw-r--r-- | src/lib/checkout/email/FinishCheckoutEmail.jsx | 281 | ||||
| -rw-r--r-- | src/lib/variant/components/VariantCard.jsx | 3 | ||||
| -rw-r--r-- | src/pages/api/shop/finish-checkout.js | 35 | ||||
| -rw-r--r-- | src/pages/my/menu.jsx | 1 | ||||
| -rw-r--r-- | src/pages/shop/cart.jsx | 5 | ||||
| -rw-r--r-- | src/pages/shop/checkout/[status].jsx (renamed from src/pages/shop/checkout/finish.jsx) | 11 |
13 files changed, 263 insertions, 151 deletions
diff --git a/src/core/components/elements/Sidebar/Sidebar.jsx b/src/core/components/elements/Sidebar/Sidebar.jsx index c39b5e34..308538e7 100644 --- a/src/core/components/elements/Sidebar/Sidebar.jsx +++ b/src/core/components/elements/Sidebar/Sidebar.jsx @@ -134,9 +134,15 @@ const Sidebar = ({ active, close }) => { </SidebarLink> <SidebarLink className={itemClassName} + href='/faqs' + > + F.A.Q + </SidebarLink> + <SidebarLink + className={itemClassName} href='/' > - Pusat Bantuan + Hubungi Kami </SidebarLink> <button className={`${itemClassName} w-full text-left flex`} diff --git a/src/core/components/layouts/AppLayout.jsx b/src/core/components/layouts/AppLayout.jsx index a325b1c1..d74d61e3 100644 --- a/src/core/components/layouts/AppLayout.jsx +++ b/src/core/components/layouts/AppLayout.jsx @@ -2,14 +2,14 @@ import AppBar from '../elements/Appbar/Appbar' import BasicFooter from '../elements/Footer/BasicFooter' import AnimationLayout from './AnimationLayout' -const AppLayout = ({ children, title }) => { +const AppLayout = ({ children, title, withFooter = true }) => { return ( <> <AnimationLayout> <AppBar title={title} /> {children} </AnimationLayout> - <BasicFooter /> + {withFooter && <BasicFooter />} </> ) } diff --git a/src/lib/auth/components/Activate.jsx b/src/lib/auth/components/Activate.jsx index f67cc168..8fdc1096 100644 --- a/src/lib/auth/components/Activate.jsx +++ b/src/lib/auth/components/Activate.jsx @@ -117,7 +117,7 @@ const Activate = () => { } return ( - <div className='p-6 pt-10 flex flex-col items-center'> + <div className='p-6 pt-10 flex flex-col items-center min-h-screen'> <Link href='/'> <Image src={IndoteknikLogo} diff --git a/src/lib/auth/components/Login.jsx b/src/lib/auth/components/Login.jsx index b25cf4fe..6fbc1475 100644 --- a/src/lib/auth/components/Login.jsx +++ b/src/lib/auth/components/Login.jsx @@ -53,7 +53,7 @@ const Login = () => { } return ( - <div className='p-6 pt-10 flex flex-col items-center'> + <div className='p-6 pt-10 flex flex-col items-center min-h-screen'> <Link href='/'> <Image src={IndoteknikLogo} diff --git a/src/lib/auth/components/Register.jsx b/src/lib/auth/components/Register.jsx index 82b81b52..4ea62899 100644 --- a/src/lib/auth/components/Register.jsx +++ b/src/lib/auth/components/Register.jsx @@ -49,7 +49,7 @@ const Register = () => { } return ( - <div className='p-6 pt-10 flex flex-col items-center'> + <div className='p-6 pt-10 flex flex-col items-center min-h-screen'> <Link href='/'> <Image src={IndoteknikLogo} diff --git a/src/lib/checkout/components/Checkout.jsx b/src/lib/checkout/components/Checkout.jsx index 8af3d996..b4fd33ff 100644 --- a/src/lib/checkout/components/Checkout.jsx +++ b/src/lib/checkout/components/Checkout.jsx @@ -17,6 +17,7 @@ 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' const Checkout = () => { const router = useRouter() @@ -56,6 +57,7 @@ const Checkout = () => { }) }, [addresses]) + const [paymentMethod, setPaymentMethod] = useState('') const [products, setProducts] = useState(null) const [totalAmount, setTotalAmount] = useState(0) const [totalDiscountAmount, setTotalDiscountAmount] = useState(0) @@ -96,6 +98,11 @@ const Checkout = () => { 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' }) @@ -121,12 +128,16 @@ const Checkout = () => { toast.error('Gagal melakukan transaksi, terjadi kesalahan internal') return } - - const payment = await axios.post( - `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/midtrans-payment?transactionId=${isCheckouted.id}` - ) + for (const product of products) deleteItemCart({ productId: product.id }) - window.location.href = payment.data.redirectUrl + 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}`) + } } return ( @@ -222,6 +233,34 @@ const Checkout = () => { <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> + + <Divider /> + + <div className='p-4'> <div className='font-medium'>Purchase Order</div> <div className='mt-4 flex gap-x-3'> diff --git a/src/lib/checkout/components/FinishCheckout.jsx b/src/lib/checkout/components/FinishCheckout.jsx index f5346d67..33c0d46a 100644 --- a/src/lib/checkout/components/FinishCheckout.jsx +++ b/src/lib/checkout/components/FinishCheckout.jsx @@ -1,17 +1,6 @@ import Link from '@/core/components/elements/Link/Link' -import axios from 'axios' -import { useEffect } from 'react' const FinishCheckout = ({ query }) => { - useEffect(() => { - if (query?.order_id) { - console.log(`${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/finish-checkout?orderName=${query.order_id}`); - axios.post( - `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/finish-checkout?orderId=${query.order_id}` - ) - } - }, [query]) - return ( <div className='p-4'> <div className='rounded-xl bg-yellow_r-4 text-center border border-yellow_r-7'> diff --git a/src/lib/checkout/email/FinishCheckoutEmail.jsx b/src/lib/checkout/email/FinishCheckoutEmail.jsx index 950fe318..1a4940ee 100644 --- a/src/lib/checkout/email/FinishCheckoutEmail.jsx +++ b/src/lib/checkout/email/FinishCheckoutEmail.jsx @@ -33,6 +33,7 @@ const FinishCheckoutEmail = ({ transaction, payment, statusPayment }) => { {statusPayment == 'success' && 'Terimakasih untuk pembelian anda!'} {statusPayment == 'pending' && 'Menunggu Pembayaran'} {statusPayment == 'failed' && 'Pembayaran Tidak Berhasil'} + {statusPayment == 'manual' && 'Pembelian di Indoteknik.com'} </Heading> <Text style={style.text}>Hai {transaction.address.customer.name},</Text> @@ -63,6 +64,12 @@ const FinishCheckoutEmail = ({ transaction, payment, statusPayment }) => { kembali dengan produk yang anda inginkan di website Indoteknik.com. </> )} + {statusPayment == 'manual' && ( + <> + Terima kasih telah mempercayai Indoteknik.com sebagai Platform B2B industrial Supply + & Solution + </> + )} </Text> <Text style={style.text}> {['pending', 'failed'].includes(statusPayment) && ( @@ -77,127 +84,185 @@ const FinishCheckoutEmail = ({ transaction, payment, statusPayment }) => { sudah berhasil anda lakukan melalui Whatsapp kami. </> )} + {statusPayment == 'manual' && ( + <> + Kami mohon kepada {transaction.address.customer.name} untuk dapat segera + menyelesaikan transaksi dengan detail dibawah ini: + <ul> + <li>Nomor Pembelian: {transaction.name}</li> + <li>Nominal: {transaction.amountTotal}</li> + <li>Tanggal: {transaction.dateOrder}</li> + </ul> + </> + )} </Text> - <Text style={{ ...style.text, lineHeight: '100%', marginTop: '24px' }}> - <strong>Detail Transaksi</strong> - </Text> + {['pending', 'failed', 'success'].includes(statusPayment) && ( + <> + <Text style={{ ...style.text, lineHeight: '100%', marginTop: '24px' }}> + <strong>Detail Transaksi</strong> + </Text> - <Hr style={style.hr} /> + <Hr style={style.hr} /> - <Section style={style.alert}> - {statusPayment == 'success' && - 'Struk ini dapat anda simpan sebagai bukti tambahan dalam transaksi yang telah dilakukan.'} - {statusPayment == 'pending' && - 'Kami akan menginformasikan melalui email setelah anda berhasil melakukan pembayaran.'} - {statusPayment == 'failed' && - 'Dimohon untuk tidak melakukan pembayaran. Karena transaksi anda tidak berhasil dibuat.'} - </Section> + <Section style={style.alert}> + {statusPayment == 'success' && + 'Struk ini dapat anda simpan sebagai bukti tambahan dalam transaksi yang telah dilakukan.'} + {statusPayment == 'pending' && + 'Kami akan menginformasikan melalui email setelah anda berhasil melakukan pembayaran.'} + {statusPayment == 'failed' && + 'Dimohon untuk tidak melakukan pembayaran. Karena transaksi anda tidak berhasil dibuat.'} + </Section> - <Row style={style.descriptionRow}> - <Column style={style.descriptionLCol}>No Transaksi (SO)</Column> - <Column style={style.descriptionRCol}>{transaction.name}</Column> - </Row> - <Row style={style.descriptionRow}> - <Column style={style.descriptionLCol}>Tanggal Transaksi</Column> - <Column style={style.descriptionRCol}>{payment.transactionTime}</Column> - </Row> - <Row style={style.descriptionRow}> - <Column style={style.descriptionLCol}>Status Pembayaran</Column> - <Column style={{ ...style.descriptionRCol }}> - {statusPayment == 'success' && ( - <div style={{ ...style.badge, ...style.badgeGreen }}>Berhasil</div> - )} - {statusPayment == 'pending' && ( - <div style={{ ...style.badge, ...style.badgeRed }}>Pending</div> - )} - {statusPayment == 'failed' && ( - <div style={{ ...style.badge, ...style.badgeRed }}>Tidak Berhasil</div> - )} - </Column> - </Row> - <Row style={style.descriptionRow}> - <Column style={style.descriptionLCol}>Metode Pembayaran</Column> - <Column style={style.descriptionRCol}> - {toTitleCase(payment.paymentType.replaceAll('_', ' '))} - </Column> - </Row> - <Row style={style.descriptionRow}> - <Column style={style.descriptionLCol}>Batas Akhir Pembayaran</Column> - <Column style={style.descriptionRCol}>{payment.expiryTime}</Column> - </Row> - <Row style={style.descriptionRow}> - <Column style={style.descriptionLCol}>Nominal Transfer</Column> - <Column style={style.descriptionRCol}> - <span style={{ fontWeight: '600' }}>{currencyFormat(payment.grossAmount)}</span> - </Column> - </Row> + <Row style={style.descriptionRow}> + <Column style={style.descriptionLCol}>No Transaksi (SO)</Column> + <Column style={style.descriptionRCol}>{transaction.name}</Column> + </Row> + <Row style={style.descriptionRow}> + <Column style={style.descriptionLCol}>Tanggal Transaksi</Column> + <Column style={style.descriptionRCol}>{payment.transactionTime}</Column> + </Row> + <Row style={style.descriptionRow}> + <Column style={style.descriptionLCol}>Status Pembayaran</Column> + <Column style={{ ...style.descriptionRCol }}> + {statusPayment == 'success' && ( + <div style={{ ...style.badge, ...style.badgeGreen }}>Berhasil</div> + )} + {statusPayment == 'pending' && ( + <div style={{ ...style.badge, ...style.badgeRed }}>Pending</div> + )} + {statusPayment == 'failed' && ( + <div style={{ ...style.badge, ...style.badgeRed }}>Tidak Berhasil</div> + )} + </Column> + </Row> + <Row style={style.descriptionRow}> + <Column style={style.descriptionLCol}>Metode Pembayaran</Column> + <Column style={style.descriptionRCol}> + {toTitleCase(payment.paymentType.replaceAll('_', ' '))} + </Column> + </Row> + <Row style={style.descriptionRow}> + <Column style={style.descriptionLCol}>Batas Akhir Pembayaran</Column> + <Column style={style.descriptionRCol}>{payment.expiryTime}</Column> + </Row> + <Row style={style.descriptionRow}> + <Column style={style.descriptionLCol}>Nominal Transfer</Column> + <Column style={style.descriptionRCol}> + <span style={{ fontWeight: '600' }}>{currencyFormat(payment.grossAmount)}</span> + </Column> + </Row> - <Text style={{ ...style.text, lineHeight: '100%', marginTop: '24px' }}> - <strong>Detail Produk</strong> - </Text> + <Text style={{ ...style.text, lineHeight: '100%', marginTop: '24px' }}> + <strong>Detail Produk</strong> + </Text> - <Hr style={style.hr} /> + <Hr style={style.hr} /> - {transaction.products.map((product) => ( - <Row - style={style.productRow} - key={product.id} - > - <Column style={style.productLCol}> - <Img - src={product.parent.image} - width='100%' - /> - </Column> - <Column style={style.productRCol}> - <Text style={style.productName}>{product.name}</Text> - <Text style={style.productCode}>{product.code}</Text> - <div style={{ dislay: 'flex' }}> - <span style={style.productPriceA}> - {currencyFormat(product.price.priceDiscount)} - </span> - {product.price.discountPercentage > 0 && ( - <> - - <span style={style.productPriceB}>{currencyFormat(product.price.price)}</span> - </> - )} - x {product.quantity} barang - </div> - </Column> - </Row> - ))} + {transaction.products.map((product) => ( + <Row + style={style.productRow} + key={product.id} + > + <Column style={style.productLCol}> + <Img + src={product.parent.image} + width='100%' + /> + </Column> + <Column style={style.productRCol}> + <Text style={style.productName}>{product.name}</Text> + <Text style={style.productCode}>{product.code}</Text> + <div style={{ dislay: 'flex' }}> + <span style={style.productPriceA}> + {currencyFormat(product.price.priceDiscount)} + </span> + {product.price.discountPercentage > 0 && ( + <> + + <span style={style.productPriceB}> + {currencyFormat(product.price.price)} + </span> + </> + )} + x {product.quantity} barang + </div> + </Column> + </Row> + ))} - <Hr style={style.hr} /> + <Hr style={style.hr} /> - <Row style={style.descriptionRow}> - <Column style={style.descriptionLCol}>Subtotal</Column> - <Column style={style.descriptionRCol}>{currencyFormat(transaction.subtotal)}</Column> - </Row> - <Row style={style.descriptionRow}> - <Column style={style.descriptionLCol}>Total Diskon</Column> - <Column style={{ ...style.descriptionRCol, color: '#E20613' }}> - {currencyFormat(transaction.discountTotal)} - </Column> - </Row> - <Row style={style.descriptionRow}> - <Column style={style.descriptionLCol}>PPN 11% (Incl.)</Column> - <Column style={style.descriptionRCol}> - {currencyFormat(transaction.subtotal * 0.11)} - </Column> - </Row> + <Row style={style.descriptionRow}> + <Column style={style.descriptionLCol}>Subtotal</Column> + <Column style={style.descriptionRCol}> + {currencyFormat(transaction.subtotal)} + </Column> + </Row> + <Row style={style.descriptionRow}> + <Column style={style.descriptionLCol}>Total Diskon</Column> + <Column style={{ ...style.descriptionRCol, color: '#E20613' }}> + {currencyFormat(transaction.discountTotal)} + </Column> + </Row> + <Row style={style.descriptionRow}> + <Column style={style.descriptionLCol}>PPN 11% (Incl.)</Column> + <Column style={style.descriptionRCol}> + {currencyFormat(transaction.subtotal * 0.11)} + </Column> + </Row> - <Hr style={style.hr} /> + <Hr style={style.hr} /> + + <Row style={style.descriptionRow}> + <Column style={style.descriptionLCol}>Grand Total</Column> + <Column style={style.descriptionRCol}> + <span style={{ fontWeight: '600' }}> + {currencyFormat(transaction.amountTotal)} + </span> + </Column> + </Row> - <Row style={style.descriptionRow}> - <Column style={style.descriptionLCol}>Grand Total</Column> - <Column style={style.descriptionRCol}> - <span style={{ fontWeight: '600' }}>{currencyFormat(transaction.amountTotal)}</span> - </Column> - </Row> + <Hr style={style.hr} /> + </> + )} - <Hr style={style.hr} /> + {statusPayment == 'manual' && ( + <> + <Text style={style.text}> + Dengan cara dibawah ini: + <ul> + <li> + Lakukan pembayaran manual via mobile app perbankan{' '} + {transaction.address.customer.name} Bank Central Asia (BCA) 8870400081 A/N: + INDOTEKNIK DOTCOM GEMILANG PT + </li> + <li> + Setelah berhasil melakukan pembayaran, mohon agar melakukan Screen Capture bukti + bayar sebagai bukti untuk kami bahwa {transaction.address.customer.name} telah + melakukan transaksi pembayaran + </li> + <li> + Kirimkan bukti transaksi pembayaran anda dengan melakukan reply / balas email + ini dengan melampirkan bukti di attachment / lampiran + </li> + <li> + Transaksi {transaction.address.customer.name} akan segera diproses oleh salah + satu Account Representative Indoteknik + </li> + </ul> + </Text> + <Text style={style.text}> + Jika ada pertanyaan seputar teknis pembayaran {transaction.address.customer.name}{' '} + dapat hubungi kami melalui Email{' '} + <a href='mailto:sales@indoteknik.com'>(sales@indoteknik.com)</a> atau Whatsapp{' '} + <a href='https://wa.me/628128080622'>(+62 812-8080-622)</a>. + </Text> + <Text style={style.text}> + Terima kasih atas perhatiannya, selamat kembali beraktifitas + </Text> + </> + )} <Text style={{ ...style.text, margin: '12px 0 3px' }}>Best regards,</Text> diff --git a/src/lib/variant/components/VariantCard.jsx b/src/lib/variant/components/VariantCard.jsx index 6e7ea871..8161473d 100644 --- a/src/lib/variant/components/VariantCard.jsx +++ b/src/lib/variant/components/VariantCard.jsx @@ -47,9 +47,6 @@ const VariantCard = ({ product, openOnClick = true, buyMore = false }) => { <span className='badge-red'>{product.price.discountPercentage}%</span> </> )} - <p className='text-caption-2 text-gray_r-12'> - {currencyFormat(product.price.priceDiscount)} - </p> </div> <p className='text-caption-2 text-gray_r-11 mt-1'> {currencyFormat(product.price.priceDiscount)} × {product.quantity} Barang diff --git a/src/pages/api/shop/finish-checkout.js b/src/pages/api/shop/finish-checkout.js index 66874549..04e82118 100644 --- a/src/pages/api/shop/finish-checkout.js +++ b/src/pages/api/shop/finish-checkout.js @@ -25,12 +25,25 @@ export default async function handler(req, res) { 'Content-Type': 'application/json', Authorization: `Basic ${midtransAuthKey}` } - let midtransStatus = await axios.get(`${process.env.MIDTRANS_HOST}/v2/${orderName}/status`, { - headers: midtransHeaders - }) - midtransStatus = camelcaseObjectDeep(midtransStatus.data) - if (!midtransStatus?.orderId) { - return res.status(400).json({ error: 'Payment Not Found' }) + let midtransStatus = {} + try { + midtransStatus = await axios.get(`${process.env.MIDTRANS_HOST}/v2/${orderName}/status`, { + headers: midtransHeaders + }) + midtransStatus = camelcaseObjectDeep(midtransStatus.data) + } catch (error) { + console.log(error) + } + + let statusPayment = 'manual' + if (midtransStatus?.orderId) { + const transactionStatus = midtransStatus.transactionStatus + statusPayment = 'failed' + if (['capture', 'settlement'].includes(transactionStatus)) { + statusPayment = 'success' + } else if (transactionStatus == 'pending') { + statusPayment = 'pending' + } } const query = `name=${orderName.replaceAll('-', '/')}&limit=1` @@ -62,16 +75,6 @@ export default async function handler(req, res) { (product.price.price - product.price.priceDiscount) * product.quantity } - let statusPayment = '' - const transactionStatus = midtransStatus.transactionStatus - if (['capture', 'settlement'].includes(transactionStatus)) { - statusPayment = 'success' - } else if (transactionStatus == 'pending') { - statusPayment = 'pending' - } else { - statusPayment = 'failed' - } - const emailMessage = render( <FinishCheckoutEmail transaction={transaction} diff --git a/src/pages/my/menu.jsx b/src/pages/my/menu.jsx index b9fd30ee..67158242 100644 --- a/src/pages/my/menu.jsx +++ b/src/pages/my/menu.jsx @@ -54,7 +54,6 @@ export default function Menu() { <div className='divide-y divide-gray_r-6 border-y border-gray_r-6 mt-4'> <LinkItem href='/'>Customer Support</LinkItem> - <LinkItem href='/faqs'>F.A.Q</LinkItem> </div> </div> diff --git a/src/pages/shop/cart.jsx b/src/pages/shop/cart.jsx index 97f98843..7e78f215 100644 --- a/src/pages/shop/cart.jsx +++ b/src/pages/shop/cart.jsx @@ -5,7 +5,10 @@ const CartComponent = dynamic(() => import('@/lib/cart/components/Cart')) export default function Cart() { return ( - <AppLayout title='Keranjang'> + <AppLayout + title='Keranjang' + withFooter={false} + > <CartComponent /> </AppLayout> ) diff --git a/src/pages/shop/checkout/finish.jsx b/src/pages/shop/checkout/[status].jsx index eb7631a0..bf184c0e 100644 --- a/src/pages/shop/checkout/finish.jsx +++ b/src/pages/shop/checkout/[status].jsx @@ -2,6 +2,17 @@ import BasicLayout from '@/core/components/layouts/BasicLayout' import IsAuth from '@/lib/auth/components/IsAuth' import FinishCheckoutComponent from '@/lib/checkout/components/FinishCheckout' import { useRouter } from 'next/router' +import axios from 'axios' + +export async function getServerSideProps(context) { + const { order_id } = context.query + await axios.post( + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/finish-checkout?orderName=${order_id}`, + {}, + { headers: context.req.headers } + ) + return { props: {} } +} export default function Finish() { const router = useRouter() |
