summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiqdad <ahmadmiqdad27@gmail.com>2025-05-31 21:39:53 +0700
committerMiqdad <ahmadmiqdad27@gmail.com>2025-05-31 21:39:53 +0700
commit77f976d3bd09d9e00d4d55bbd40579b439405d96 (patch)
treea8959bba3d8a51570c439789f92653409a0065ae
parentca05a70e98e9066882de6394ffbd89db7af2cb9d (diff)
parent2a1dea70b8f0062fe8eebeb7139a7b77a24e220b (diff)
Merge branch 'new-release' of https://bitbucket.org/altafixco/next-indoteknik into biteship-merge
# Conflicts: # src/lib/checkout/components/Checkout.jsx # src/lib/transaction/components/Transaction.jsx
-rw-r--r--public/file/Surat Pernyataan Nomor Rekening.docxbin0 -> 18271 bytes
-rw-r--r--public/images/ICON_TEMPO.pngbin0 -> 148062 bytes
-rw-r--r--src-migrate/modules/cart/components/ItemAction.tsx246
-rw-r--r--src-migrate/modules/cart/components/ItemSelect.tsx172
-rw-r--r--src-migrate/modules/cart/components/Summary.tsx201
-rw-r--r--src-migrate/modules/cart/stores/useCartStore.ts151
-rw-r--r--src-migrate/modules/popup-information/index.tsx6
-rw-r--r--src-migrate/modules/product-detail/components/ProductDetail.tsx44
-rw-r--r--src-migrate/modules/promo/components/Voucher.tsx1
-rw-r--r--src-migrate/pages/shop/cart/index.tsx399
-rw-r--r--src-migrate/types/product.ts3
-rw-r--r--src-migrate/types/voucher.ts1
-rw-r--r--src-migrate/utils/cart.js455
-rw-r--r--src-migrate/utils/checkBoxState.js90
-rw-r--r--src-migrate/validations/tempo.ts8
-rw-r--r--src/api/productApi.js14
-rw-r--r--src/lib/auth/components/CompanyProfile.jsx16
-rw-r--r--src/lib/checkout/components/Checkout.jsx275
-rw-r--r--src/lib/checkout/components/CheckoutSection.jsx2
-rw-r--r--src/lib/checkout/components/FinishCheckout.jsx9
-rw-r--r--src/lib/flashSale/api/flashSaleApi.js5
-rw-r--r--src/lib/flashSale/components/FlashSaleNonDisplay.jsx12
-rw-r--r--src/lib/invoice/components/Invoice.jsx158
-rw-r--r--src/lib/merchant/api/createMerchantApi.js14
-rw-r--r--src/lib/merchant/api/getMerchantApi.js13
-rw-r--r--src/lib/merchant/api/getMerchantProgresApi.js10
-rw-r--r--src/lib/merchant/components/AccountSwitch.jsx60
-rw-r--r--src/lib/merchant/components/Dokumen.jsx1252
-rw-r--r--src/lib/merchant/components/InformasiPerusahaan.jsx1399
-rw-r--r--src/lib/merchant/components/InformasiVendor.jsx748
-rw-r--r--src/lib/merchant/components/Konfirmasi.jsx224
-rw-r--r--src/lib/merchant/components/Merchant.jsx147
-rw-r--r--src/lib/merchant/components/SyaratDagang.jsx822
-rw-r--r--src/lib/pengajuan-tempo/component/Pengiriman.jsx51
-rw-r--r--src/lib/product/components/ProductSearch.jsx7
-rw-r--r--src/lib/shipment/components/Shipments.jsx2
-rw-r--r--src/lib/transaction/components/Transaction.jsx55
-rw-r--r--src/pages/api/flashsale-header.js2
-rw-r--r--src/pages/api/shop/search.js5
-rw-r--r--src/pages/daftar-merchant.jsx2
-rw-r--r--src/utils/solrMapping.js1
41 files changed, 6652 insertions, 430 deletions
diff --git a/public/file/Surat Pernyataan Nomor Rekening.docx b/public/file/Surat Pernyataan Nomor Rekening.docx
new file mode 100644
index 00000000..b431de24
--- /dev/null
+++ b/public/file/Surat Pernyataan Nomor Rekening.docx
Binary files differ
diff --git a/public/images/ICON_TEMPO.png b/public/images/ICON_TEMPO.png
new file mode 100644
index 00000000..dee76189
--- /dev/null
+++ b/public/images/ICON_TEMPO.png
Binary files differ
diff --git a/src-migrate/modules/cart/components/ItemAction.tsx b/src-migrate/modules/cart/components/ItemAction.tsx
index 7220e362..4dcebd9e 100644
--- a/src-migrate/modules/cart/components/ItemAction.tsx
+++ b/src-migrate/modules/cart/components/ItemAction.tsx
@@ -1,74 +1,214 @@
-import style from '../styles/item-action.module.css'
+import style from '../styles/item-action.module.css';
-import React, { useEffect, useState } from 'react'
+import React, { useEffect, useState } from 'react';
-import { Spinner, Tooltip } from '@chakra-ui/react'
-import { MinusIcon, PlusIcon, Trash2Icon } from 'lucide-react'
+import { Spinner, Tooltip } from '@chakra-ui/react';
+import { MinusIcon, PlusIcon, Trash2Icon } from 'lucide-react';
-import { CartItem } from '~/types/cart'
-import { getAuth } from '~/libs/auth'
-import { deleteUserCart, upsertUserCart } from '~/services/cart'
+import { CartItem } from '~/types/cart';
+import { getAuth } from '~/libs/auth';
+import { deleteUserCart, upsertUserCart } from '~/services/cart';
-import { useDebounce } from 'usehooks-ts'
-import { useCartStore } from '../stores/useCartStore'
-import { useProductCartContext } from '@/contexts/ProductCartContext'
+import { useDebounce } from 'usehooks-ts';
+import { useCartStore } from '../stores/useCartStore';
+import { useProductCartContext } from '@/contexts/ProductCartContext';
+import {
+ removeSelectedItemsFromCookie,
+ removeCartItemsFromCookie,
+ quantityUpdateState,
+ getCartDataFromCookie,
+ setCartDataToCookie,
+} from '~/utils/cart';
+
+import { toast } from 'react-hot-toast';
type Props = {
- item: CartItem
-}
+ item: CartItem;
+};
const CartItemAction = ({ item }: Props) => {
- const auth = getAuth()
- const { setRefreshCart } = useProductCartContext()
- const [isLoadDelete, setIsLoadDelete] = useState<boolean>(false)
- const [isLoadQuantity, setIsLoadQuantity] = useState<boolean>(false)
+ const auth = getAuth();
+ const { setRefreshCart } = useProductCartContext();
+ const [isLoadDelete, setIsLoadDelete] = useState<boolean>(false);
+ const [isLoadQuantity, setIsLoadQuantity] = useState<boolean>(false);
- const [quantity, setQuantity] = useState<number>(item.quantity)
+ const [quantity, setQuantity] = useState<number>(item.quantity);
- const { loadCart } = useCartStore()
+ const { loadCart, cart, updateCartItem } = useCartStore();
- const limitQty = item.limit_qty?.transaction || 0
+ const limitQty = item.limit_qty?.transaction || 0;
const handleDelete = async () => {
- if (typeof auth !== 'object') return
-
- setIsLoadDelete(true)
- await deleteUserCart(auth.id, [item.cart_id])
- await loadCart(auth.id)
- setIsLoadDelete(false)
- setRefreshCart(true)
- }
-
- const decreaseQty = () => { setQuantity((quantity) => quantity -= 1) }
- const increaseQty = () => { setQuantity((quantity) => quantity += 1) }
- const debounceQty = useDebounce(quantity, 1000)
+ if (typeof auth !== 'object') return;
+
+ setIsLoadDelete(true);
+
+ try {
+ // Delete from server
+ await deleteUserCart(auth.id, [item.cart_id]);
+
+ // Clean up cookies immediately
+ removeSelectedItemsFromCookie([item.id]);
+ removeCartItemsFromCookie([item.cart_id]);
+
+ // Update local cart state optimistically
+ if (cart) {
+ const updatedProducts = cart.products.filter(
+ (product) => product.id !== item.id
+ );
+ const updatedCart = {
+ ...cart,
+ products: updatedProducts,
+ product_total: updatedProducts.length,
+ };
+ updateCartItem(updatedCart);
+ }
+
+ // Reload from server and refresh context
+ await loadCart(auth.id);
+ setRefreshCart(true);
+
+ toast.success('Item berhasil dihapus');
+ } catch (error) {
+ console.error('Failed to delete cart item:', error);
+ toast.error('Gagal menghapus item');
+
+ // Reload on error
+ await loadCart(auth.id);
+ } finally {
+ setIsLoadDelete(false);
+ }
+ };
+
+ const updateQuantityInCookie = (productId, cartId, newQuantity) => {
+ try {
+ const cartData = getCartDataFromCookie();
+ let itemFound = false;
+
+ // Find item by cart_id key or search within objects
+ if (cartData[cartId]) {
+ cartData[cartId].quantity = newQuantity;
+ itemFound = true;
+ } else {
+ // Search by product id or cart_id within objects
+ for (const key in cartData) {
+ const item = cartData[key];
+ if (item.id === productId || item.cart_id === cartId) {
+ item.quantity = newQuantity;
+ itemFound = true;
+ break;
+ }
+ }
+ }
+
+ if (itemFound) {
+ setCartDataToCookie(cartData);
+ return true;
+ }
+
+ return false;
+ } catch (error) {
+ console.error('Error updating quantity in cookie:', error);
+ return false;
+ }
+ };
+
+ const decreaseQty = () => {
+ setQuantity((quantity) => (quantity -= 1));
+ };
+
+ const increaseQty = () => {
+ setQuantity((quantity) => (quantity += 1));
+ };
+
+ const debounceQty = useDebounce(quantity, 1000);
+
useEffect(() => {
- if (isNaN(debounceQty)) setQuantity(1)
- if (limitQty > 0 && debounceQty > limitQty) setQuantity(limitQty)
- }, [debounceQty, limitQty])
+ if (isNaN(debounceQty)) setQuantity(1);
+ if (limitQty > 0 && debounceQty > limitQty) setQuantity(limitQty);
+ }, [debounceQty, limitQty]);
useEffect(() => {
const updateCart = async () => {
- if (typeof auth !== 'object' || isNaN(debounceQty)) return
-
- setIsLoadQuantity(true)
- await upsertUserCart({
- userId: auth.id,
- type: item.cart_type,
- id: item.id,
- qty: debounceQty,
- selected: item.selected,
- })
- await loadCart(auth.id)
- setIsLoadQuantity(false)
- }
- updateCart()
+ if (typeof auth !== 'object' || isNaN(debounceQty)) return;
+ if (debounceQty === item.quantity) return;
+
+ quantityUpdateState.startUpdate(item.id);
+ setIsLoadQuantity(true);
+
+ try {
+ // Update cookie immediately for responsive UI
+ updateQuantityInCookie(item.id, item.cart_id, debounceQty);
+
+ // Update local cart state optimistically
+ if (cart) {
+ const updatedProducts = cart.products.map((product) =>
+ product.id === item.id
+ ? { ...product, quantity: debounceQty }
+ : product
+ );
+ const updatedCart = {
+ ...cart,
+ products: updatedProducts,
+ };
+ updateCartItem(updatedCart);
+ }
+
+ // Send update to server
+ await upsertUserCart({
+ userId: auth.id,
+ type: item.cart_type,
+ id: item.id,
+ qty: debounceQty,
+ selected: item.selected,
+ });
+
+ // Reload from server to ensure consistency
+ await loadCart(auth.id);
+
+ // Re-update cookie if server reload overwrote it
+ const currentCookieData = getCartDataFromCookie();
+ let needsReUpdate = false;
+
+ for (const key in currentCookieData) {
+ const cookieItem = currentCookieData[key];
+ if (
+ (cookieItem.id === item.id ||
+ cookieItem.cart_id === item.cart_id) &&
+ cookieItem.quantity !== debounceQty
+ ) {
+ needsReUpdate = true;
+ break;
+ }
+ }
+
+ if (needsReUpdate) {
+ updateQuantityInCookie(item.id, item.cart_id, debounceQty);
+ }
+ } catch (error) {
+ console.error('Error updating quantity:', error);
+ toast.error('Gagal mengupdate quantity');
+
+ // Revert changes on error
+ updateQuantityInCookie(item.id, item.cart_id, item.quantity);
+ loadCart(auth.id);
+ } finally {
+ setIsLoadQuantity(false);
+ quantityUpdateState.endUpdate(item.id);
+ }
+ };
+
+ updateCart();
//eslint-disable-next-line react-hooks/exhaustive-deps
- }, [debounceQty])
+ }, [debounceQty]);
return (
<div className={style.actionSection}>
- <button className={style.deleteButton} onClick={handleDelete} disabled={isLoadDelete}>
+ <button
+ className={style.deleteButton}
+ onClick={handleDelete}
+ disabled={isLoadDelete}
+ >
{isLoadDelete && <Spinner size='xs' />}
{!isLoadDelete && <Trash2Icon size={16} />}
</button>
@@ -106,7 +246,7 @@ const CartItemAction = ({ item }: Props) => {
</Tooltip>
</div>
</div>
- )
-}
+ );
+};
-export default CartItemAction \ No newline at end of file
+export default CartItemAction; \ No newline at end of file
diff --git a/src-migrate/modules/cart/components/ItemSelect.tsx b/src-migrate/modules/cart/components/ItemSelect.tsx
index d4a1b537..72ab49aa 100644
--- a/src-migrate/modules/cart/components/ItemSelect.tsx
+++ b/src-migrate/modules/cart/components/ItemSelect.tsx
@@ -1,56 +1,140 @@
-import { Checkbox, Spinner } from '@chakra-ui/react'
-import React, { useState } from 'react'
-
-import { getAuth } from '~/libs/auth'
-import { CartItem } from '~/types/cart'
-import { upsertUserCart } from '~/services/cart'
-
-import { useCartStore } from '../stores/useCartStore'
+import { Checkbox } from '@chakra-ui/react';
+import React, { useState, useCallback, useEffect } from 'react';
+import { getAuth } from '~/libs/auth';
+import { CartItem } from '~/types/cart';
+import { upsertUserCart } from '~/services/cart';
+import { useCartStore } from '../stores/useCartStore';
+import { toast } from 'react-hot-toast';
+import {
+ getSelectedItemsFromCookie,
+ updateSelectedItemInCookie,
+ checkboxUpdateState,
+} from '~/utils/cart';
type Props = {
- item: CartItem
-}
+ item: CartItem;
+};
const CartItemSelect = ({ item }: Props) => {
- const auth = getAuth()
- const { updateCartItem, cart } = useCartStore()
+ const auth = getAuth();
+ const { updateCartItem, cart, loadCart } = useCartStore();
+ const [isUpdating, setIsUpdating] = useState<boolean>(false);
+ const [localSelected, setLocalSelected] = useState<boolean>(item.selected);
+
+ // Subscribe to global checkbox update state
+ useEffect(() => {
+ const handleUpdateStateChange = (isUpdating) => {
+ // This component doesn't need to react to global state changes
+ // Individual checkboxes are managed independently
+ };
+
+ checkboxUpdateState.addListener(handleUpdateStateChange);
+ return () => checkboxUpdateState.removeListener(handleUpdateStateChange);
+ }, []);
+
+ // Sync local state with cookie and server data
+ useEffect(() => {
+ if (isUpdating) return;
+
+ const selectedItems = getSelectedItemsFromCookie();
+ const storedState = selectedItems[item.id];
+
+ if (storedState !== undefined) {
+ // Update local state if cookie differs
+ if (localSelected !== storedState) {
+ setLocalSelected(storedState);
+ }
+
+ // Sync cart state with cookie if needed
+ if (storedState !== item.selected && cart) {
+ const updatedCartItems = cart.products.map((cartItem) =>
+ cartItem.id === item.id
+ ? { ...cartItem, selected: storedState }
+ : cartItem
+ );
+ updateCartItem({ ...cart, products: updatedCartItems });
+ }
+ } else {
+ // Initialize cookie with server state
+ setLocalSelected(item.selected);
+ updateSelectedItemInCookie(item.id, item.selected, false);
+ }
+ }, [item.id, item.selected, localSelected, cart, updateCartItem, isUpdating]);
+
+ const handleChange = useCallback(
+ async (e: React.ChangeEvent<HTMLInputElement>) => {
+ if (typeof auth !== 'object' || !cart || isUpdating) return;
+
+ const newSelectedState = e.target.checked;
+
+ // Update local state immediately
+ setLocalSelected(newSelectedState);
+ setIsUpdating(true);
+ checkboxUpdateState.startUpdate(item.id);
+
+ try {
+ // Update cookie immediately for responsive UI
+ updateSelectedItemInCookie(item.id, newSelectedState, false);
+
+ // Update cart state optimistically
+ const updatedCartItems = cart.products.map((cartItem) =>
+ cartItem.id === item.id
+ ? { ...cartItem, selected: newSelectedState }
+ : cartItem
+ );
+ updateCartItem({ ...cart, products: updatedCartItems });
- const [isLoad, setIsLoad] = useState<boolean>(false)
+ // Send update to server
+ await upsertUserCart({
+ userId: auth.id,
+ type: item.cart_type,
+ id: item.id,
+ qty: item.quantity,
+ selected: newSelectedState,
+ // purchase_tax_id: item.purchase_tax_id,
+ // vendor_id: item.vendor_id
+ });
- const handleChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
- if (typeof auth !== 'object' || !cart) return
-
- setIsLoad(true);
- const updatedCartItems = cart.products.map(cartItem =>
- cartItem.id === item.id
- ? { ...cartItem, selected: e.target.checked }
- : cartItem
- );
+ // Reload cart for consistency
+ await loadCart(auth.id);
+ } catch (error) {
+ console.error('Failed to update item selection:', error);
+ toast.error('Gagal memperbarui pilihan barang');
- // Update the entire cart
- const updatedCart = { ...cart, products: updatedCartItems };
- updateCartItem(updatedCart);
+ // Revert changes on error
+ setLocalSelected(!newSelectedState);
+ updateSelectedItemInCookie(item.id, !newSelectedState, false);
+ loadCart(auth.id);
+ } finally {
+ setIsUpdating(false);
+ checkboxUpdateState.endUpdate(item.id);
+ }
+ },
+ [auth, cart, item, isUpdating, updateCartItem, loadCart]
+ );
- setIsLoad(false);
- }
+ const isDisabled =
+ isUpdating || checkboxUpdateState.isCheckboxUpdating(item.id);
return (
- <div className='w-6 my-auto'>
- {isLoad && (
- <Spinner className='my-auto' size='sm' />
- )}
-
- {!isLoad && (
- <Checkbox
- borderColor='gray.600'
- colorScheme='red'
- size='lg'
- isChecked={item.selected}
- onChange={handleChange}
- />
- )}
+ <div className='w-6 my-auto relative'>
+ <Checkbox
+ borderColor='gray.600'
+ colorScheme='red'
+ size='lg'
+ isChecked={localSelected}
+ onChange={handleChange}
+ isDisabled={isDisabled}
+ opacity={isDisabled ? 0.5 : 1}
+ cursor={isDisabled ? 'not-allowed' : 'pointer'}
+ _disabled={{
+ opacity: 0.5,
+ cursor: 'not-allowed',
+ backgroundColor: 'gray.100',
+ }}
+ />
</div>
- )
-}
+ );
+};
-export default CartItemSelect \ No newline at end of file
+export default CartItemSelect;
diff --git a/src-migrate/modules/cart/components/Summary.tsx b/src-migrate/modules/cart/components/Summary.tsx
index 0af5ab18..68db6323 100644
--- a/src-migrate/modules/cart/components/Summary.tsx
+++ b/src-migrate/modules/cart/components/Summary.tsx
@@ -1,20 +1,19 @@
-import style from '../styles/summary.module.css'
-
-import React from 'react'
-import formatCurrency from '~/libs/formatCurrency'
-import clsxm from '~/libs/clsxm'
-import { Skeleton } from '@chakra-ui/react'
-import _ from 'lodash'
+import style from '../styles/summary.module.css';
+import React, { useEffect, useState, useMemo } from 'react';
+import formatCurrency from '~/libs/formatCurrency';
+import clsxm from '~/libs/clsxm';
+import { Skeleton, Box, useColorModeValue, Text } from '@chakra-ui/react';
type Props = {
- total?: number
- discount?: number
- subtotal?: number
- tax?: number
- shipping?: number
- grandTotal?: number
- isLoaded: boolean
-}
+ total?: number;
+ discount?: number;
+ subtotal?: number;
+ tax?: number;
+ shipping?: number;
+ grandTotal?: number;
+ isLoaded: boolean;
+ products?: any[]; // Added to detect changes in selected products
+};
const CartSummary = ({
total,
@@ -24,53 +23,191 @@ const CartSummary = ({
shipping,
grandTotal,
isLoaded = false,
+ products = [],
}: Props) => {
- const PPN : number = process.env.NEXT_PUBLIC_PPN ? parseFloat(process.env.NEXT_PUBLIC_PPN) : 0;
- return (
- <>
- <div className='text-h-sm font-medium'>Ringkasan Pesanan</div>
+ const PPN: number = process.env.NEXT_PUBLIC_PPN
+ ? parseFloat(process.env.NEXT_PUBLIC_PPN)
+ : 0;
+ const [isMounted, setIsMounted] = useState(false);
+
+ // Local state to store calculated values
+ const [summaryValues, setSummaryValues] = useState({
+ subtotal: 0,
+ discount: 0,
+ total: 0,
+ tax: 0,
+ shipping: 0,
+ grandTotal: 0,
+ });
+
+ // This fixes hydration issues by ensuring the component only renders fully after mounting
+ useEffect(() => {
+ setIsMounted(true);
+ }, []);
+
+ // Calculate summary based on products whenever products change
+ useMemo(() => {
+ if (!products || products.length === 0) return;
+
+ // Only count selected products
+ const selectedProducts = products.filter((product) => product.selected);
+
+ // Calculate values based on selected products
+ let calculatedSubtotal = 0;
+ let calculatedDiscount = 0;
+
+ selectedProducts.forEach((product) => {
+ // Get raw price and discount from product
+ const productBasePrice = product.price?.price || 0;
+ const productQty = product.quantity || 1;
+ const productDiscountedPrice =
+ product.price?.price_discount || productBasePrice;
+ const productDiscount = productBasePrice - productDiscountedPrice;
+
+ calculatedSubtotal += productBasePrice * productQty;
+ calculatedDiscount += productDiscount * productQty;
+ });
+
+ const calculatedTotal = calculatedSubtotal - calculatedDiscount;
+ const calculatedTax = calculatedTotal * (PPN - 1);
+ const calculatedShipping = shipping || 0;
+ const calculatedGrandTotal =
+ calculatedTotal + calculatedTax + calculatedShipping;
+
+ // If calculated values are different from props, use calculated ones
+ const shouldUpdateValues =
+ Math.abs((subtotal || 0) - calculatedSubtotal) > 0.01 ||
+ Math.abs((discount || 0) - calculatedDiscount) > 0.01 ||
+ Math.abs((total || 0) - calculatedTotal) > 0.01 ||
+ Math.abs((tax || 0) - calculatedTax) > 0.01 ||
+ Math.abs((grandTotal || 0) - calculatedGrandTotal) > 0.01;
- <div className="h-6" />
+ if (shouldUpdateValues && isLoaded) {
+ setSummaryValues({
+ subtotal: calculatedSubtotal,
+ discount: calculatedDiscount,
+ total: calculatedTotal,
+ tax: calculatedTax,
+ shipping: calculatedShipping,
+ grandTotal: calculatedGrandTotal,
+ });
+ } else if (isLoaded) {
+ // Use values from props when available
+ setSummaryValues({
+ subtotal: subtotal || 0,
+ discount: discount || 0,
+ total: total || 0,
+ tax: tax || 0,
+ shipping: shipping || 0,
+ grandTotal: grandTotal || 0,
+ });
+ }
+ }, [
+ products,
+ isLoaded,
+ subtotal,
+ discount,
+ total,
+ tax,
+ shipping,
+ grandTotal,
+ PPN,
+ ]);
+
+ // Update local values whenever props change
+ useEffect(() => {
+ if (isLoaded) {
+ setSummaryValues({
+ subtotal: subtotal || 0,
+ discount: discount || 0,
+ total: total || 0,
+ tax: tax || 0,
+ shipping: shipping || 0,
+ grandTotal: grandTotal || 0,
+ });
+ }
+ }, [isLoaded, subtotal, discount, total, tax, shipping, grandTotal]);
+
+ if (!isMounted) {
+ return (
+ <Box p={4} borderWidth='1px' borderRadius='lg' boxShadow='sm'>
+ <Text fontSize='lg' fontWeight='medium' mb={4}>
+ Ringkasan Pesanan
+ </Text>
+ {Array(6)
+ .fill(0)
+ .map((_, index) => (
+ <Skeleton key={index} height='24px' my={2} />
+ ))}
+ </Box>
+ );
+ }
+
+ // Use local state for rendering to ensure responsiveness
+ const {
+ subtotal: displaySubtotal,
+ discount: displayDiscount,
+ total: displayTotal,
+ tax: displayTax,
+ shipping: displayShipping,
+ grandTotal: displayGrandTotal,
+ } = summaryValues;
+
+ return (
+ <div className={style.summaryContainer}>
+ <Text fontSize='lg' fontWeight='medium' mb={4}>
+ Ringkasan Pesanan
+ </Text>
<div className='flex flex-col gap-y-3'>
<Skeleton isLoaded={isLoaded} className={style.line}>
<span className={style.label}>Total Belanja</span>
- <span className={style.value}>Rp {formatCurrency(subtotal || 0)}</span>
+ <span className={style.value}>
+ Rp {formatCurrency(displaySubtotal)}
+ </span>
</Skeleton>
<Skeleton isLoaded={isLoaded} className={style.line}>
<span className={style.label}>Total Diskon</span>
- <span className={clsxm(style.value, style.discount)}>- Rp {formatCurrency(discount || 0)}</span>
+ <span className={clsxm(style.value, style.discount)}>
+ - Rp {formatCurrency(displayDiscount)}
+ </span>
</Skeleton>
<div className={style.divider} />
<Skeleton isLoaded={isLoaded} className={style.line}>
<span className={style.label}>Subtotal</span>
- <span className={style.value}>Rp {formatCurrency(total || 0)}</span>
+ <span className={style.value}>Rp {formatCurrency(displayTotal)}</span>
</Skeleton>
<Skeleton isLoaded={isLoaded} className={style.line}>
- <span className={style.label}>Tax {((PPN - 1) * 100).toFixed(0)}%</span>
- <span className={style.value}>Rp {formatCurrency(tax || 0)}</span>
+ <span className={style.label}>
+ Tax {((PPN - 1) * 100).toFixed(0)}%
+ </span>
+ <span className={style.value}>Rp {formatCurrency(displayTax)}</span>
</Skeleton>
<Skeleton isLoaded={isLoaded} className={style.line}>
<span className={style.label}>Biaya Kirim</span>
- <span className={style.value}>Rp {formatCurrency(shipping || 0)}</span>
+ <span className={style.value}>
+ Rp {formatCurrency(displayShipping)}
+ </span>
</Skeleton>
<div className={style.divider} />
- <Skeleton isLoaded={isLoaded} className={style.line}>
+ <Skeleton isLoaded={isLoaded}>
<span className={clsxm(style.label, style.grandTotal)}>
Grand Total
</span>
- <span className={style.value}>Rp {formatCurrency(grandTotal || 0)}</span>
+ <span className={clsxm(style.value, style.grandTotalValue)}>
+ Rp {formatCurrency(displayGrandTotal)}
+ </span>
</Skeleton>
</div>
- </>
- )
-}
+ </div>
+ );
+};
-export default CartSummary \ No newline at end of file
+export default CartSummary;
diff --git a/src-migrate/modules/cart/stores/useCartStore.ts b/src-migrate/modules/cart/stores/useCartStore.ts
index e7d2cdd3..dc47b011 100644
--- a/src-migrate/modules/cart/stores/useCartStore.ts
+++ b/src-migrate/modules/cart/stores/useCartStore.ts
@@ -1,6 +1,12 @@
import { create } from 'zustand';
import { CartItem, CartProps } from '~/types/cart';
import { getUserCart } from '~/services/cart';
+import {
+ syncCartWithCookie,
+ getCartDataFromCookie,
+ getSelectedItemsFromCookie,
+ forceResetAllSelectedItems,
+} from '~/utils/cart';
type State = {
cart: CartProps | null;
@@ -17,6 +23,8 @@ type State = {
type Action = {
loadCart: (userId: number) => Promise<void>;
updateCartItem: (updateCart: CartProps) => void;
+ forceResetSelection: () => void;
+ clearCart: () => void;
};
export const useCartStore = create<State & Action>((set, get) => ({
@@ -29,50 +37,153 @@ export const useCartStore = create<State & Action>((set, get) => ({
tax: 0,
grandTotal: 0,
},
+
loadCart: async (userId) => {
- if (get().isLoadCart === true) return;
+ if (get().isLoadCart) return;
set({ isLoadCart: true });
- const cart: CartProps = (await getUserCart(userId)) as CartProps;
- set({ cart });
- set({ isLoadCart: false });
- const summary = computeSummary(cart);
- set({ summary });
+ try {
+ const cart: CartProps = (await getUserCart(userId)) as CartProps;
+
+ // Sync with cookie data
+ const syncResult = syncCartWithCookie(cart);
+
+ if (syncResult?.needsUpdate && cart.products) {
+ const selectedItems = getSelectedItemsFromCookie();
+
+ const updatedCart = {
+ ...cart,
+ products: cart.products.map((item) => ({
+ ...item,
+ selected:
+ selectedItems[item.id] !== undefined
+ ? selectedItems[item.id]
+ : item.selected,
+ })),
+ };
+
+ set({ cart: updatedCart });
+ } else {
+ set({ cart });
+ }
+
+ // Update summary
+ const summary = computeSummary(get().cart!);
+ set({ summary });
+ } catch (error) {
+ console.error('Failed to load cart:', error);
+
+ // Fallback to cookie data
+ await handleFallbackFromCookie();
+ } finally {
+ set({ isLoadCart: false });
+ }
},
+
updateCartItem: (updatedCart) => {
- const cart = get().cart;
+ set({ cart: updatedCart });
+ syncCartWithCookie(updatedCart);
+
+ const summary = computeSummary(updatedCart);
+ set({ summary });
+ },
+
+ forceResetSelection: () => {
+ const { cart } = get();
if (!cart) return;
+ forceResetAllSelectedItems();
+
+ const updatedCart = {
+ ...cart,
+ products: cart.products.map((item) => ({ ...item, selected: false })),
+ };
+
set({ cart: updatedCart });
+
const summary = computeSummary(updatedCart);
set({ summary });
},
+ clearCart: () => {
+ set({
+ cart: null,
+ summary: {
+ subtotal: 0,
+ discount: 0,
+ total: 0,
+ tax: 0,
+ grandTotal: 0,
+ },
+ });
+ },
}));
+// Helper function for cookie fallback
+const handleFallbackFromCookie = async () => {
+ try {
+ const cartData = getCartDataFromCookie();
+
+ if (Object.keys(cartData).length === 0) return;
+
+ const products = Object.values(cartData).map(transformCookieItemToProduct);
+
+ const fallbackCart: CartProps = {
+ product_total: products.length,
+ products,
+ };
+
+ useCartStore.setState({ cart: fallbackCart });
+
+ const summary = computeSummary(fallbackCart);
+ useCartStore.setState({ summary });
+ } catch (error) {
+ console.error('Cookie fallback failed:', error);
+ }
+};
+
+// Helper function to transform cookie item to product format
+const transformCookieItemToProduct = (item: any): CartItem => ({
+ cart_id: item.cart_id,
+ id: item.id,
+ cart_type: item.cart_type,
+ product_id: item.product?.id,
+ product_name: item.product?.name,
+ program_line_id: item.program_line?.id,
+ program_line_name: item.program_line?.name,
+ quantity: item.quantity,
+ selected: item.selected,
+ price: item.price,
+ package_price: item.package_price,
+ source: item.source,
+});
+
+// Helper function to compute cart summary
const computeSummary = (cart: CartProps) => {
+ if (!cart?.products) {
+ return { subtotal: 0, discount: 0, total: 0, grandTotal: 0, tax: 0 };
+ }
+
+ const PPN = parseFloat(process.env.NEXT_PUBLIC_PPN || '0');
let subtotal = 0;
let discount = 0;
- const PPN: number = process.env.NEXT_PUBLIC_PPN ? parseFloat(process.env.NEXT_PUBLIC_PPN) : 0;
-
- for (const item of cart?.products) {
+ for (const item of cart.products) {
if (!item.selected) continue;
- let price = 0;
- if (item.cart_type === 'promotion')
- price = (item?.package_price || 0) * item.quantity;
- else if (item.cart_type === 'product')
- price = item.price.price * item.quantity;
+ const price =
+ item.cart_type === 'promotion'
+ ? (item?.package_price || 0) * item.quantity
+ : item.price.price * item.quantity;
subtotal += price;
discount += price - item.price.price_discount * item.quantity;
}
- let total = subtotal - discount;
- let grandTotal = total * PPN;
- let tax = grandTotal - total;
- // let grandTotal = total + tax;
- return { subtotal, discount, total, grandTotal, tax };
+ const total = subtotal - discount;
+ const grandTotal = total * (1 + PPN);
+ const tax = grandTotal - total;
+
+ return { subtotal, discount, total, grandTotal, tax };
}; \ No newline at end of file
diff --git a/src-migrate/modules/popup-information/index.tsx b/src-migrate/modules/popup-information/index.tsx
index d50711cc..cae50abf 100644
--- a/src-migrate/modules/popup-information/index.tsx
+++ b/src-migrate/modules/popup-information/index.tsx
@@ -15,7 +15,6 @@ const PagePopupInformation = () => {
const [data, setData] = useState<any>(null);
const [loading, setLoading] = useState(true);
-
useEffect(() => {
const getData = async () => {
const res = await fetch(`/api/hero-banner?type=popup-banner`);
@@ -44,7 +43,10 @@ const PagePopupInformation = () => {
className='w-[350px] md:w-[530px]'
onClick={() => setActive(false)}
>
- <Link href={data[0].url === false ? '/' :data[0].url} aria-label='popup'>
+ <Link
+ href={data[0].url === false ? '/' : data[0].url}
+ aria-label='popup'
+ >
<Image
src={data[0]?.image}
alt={data[0]?.name}
diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx
index 1581f33d..192e1dc3 100644
--- a/src-migrate/modules/product-detail/components/ProductDetail.tsx
+++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx
@@ -2,7 +2,7 @@ import style from '../styles/product-detail.module.css';
import Link from 'next/link';
import { useRouter } from 'next/router';
-import { useEffect } from 'react';
+import { useEffect, useState } from 'react';
import { Button } from '@chakra-ui/react';
import { MessageCircleIcon, Share2Icon } from 'lucide-react';
@@ -74,6 +74,16 @@ const ProductDetail = ({ product }: Props) => {
// setSelectedVariant(product?.variants[0])
}, []);
+ // Gabungkan semua gambar produk (utama + tambahan)
+ const allImages = product.image_carousel ? [...product.image_carousel] : [];
+
+ if (product.image) {
+ allImages.unshift(product.image); // Tambahkan gambar utama di awal array
+ }
+ console.log(product);
+
+ const [mainImage, setMainImage] = useState(allImages[0] || '');
+
return (
<>
<div className='md:flex md:flex-wrap'>
@@ -83,7 +93,37 @@ const ProductDetail = ({ product }: Props) => {
<div className='md:w-9/12 md:flex md:flex-col md:pr-4 md:pt-6'>
<div className='md:flex md:flex-wrap'>
<div className='md:w-4/12'>
- <ProductImage product={product} />
+ <ProductImage product={{ ...product, image: mainImage }} />
+
+ {/* Carousel horizontal */}
+ {allImages.length > 0 && (
+ <div className='mt-4 overflow-x-auto'>
+ <div className='flex space-x-3 pb-3'>
+ {allImages.map((img, index) => (
+ <div
+ key={index}
+ className={`flex-shrink-0 w-16 h-16 cursor-pointer border-2 rounded-md transition-colors ${
+ mainImage === img
+ ? 'border-red-500 ring-2 ring-red-200'
+ : 'border-gray-200 hover:border-gray-300'
+ }`}
+ onClick={() => setMainImage(img)}
+ >
+ <img
+ src={img}
+ alt={`Thumbnail ${index + 1}`}
+ className='w-full h-full object-cover rounded-sm'
+ loading='lazy'
+ onError={(e) => {
+ (e.target as HTMLImageElement).src =
+ '/path/to/fallback-image.jpg';
+ }}
+ />
+ </div>
+ ))}
+ </div>
+ </div>
+ )}
</div>
<div className='md:w-8/12 px-4 md:pl-6'>
diff --git a/src-migrate/modules/promo/components/Voucher.tsx b/src-migrate/modules/promo/components/Voucher.tsx
index 034d13e9..0c225c74 100644
--- a/src-migrate/modules/promo/components/Voucher.tsx
+++ b/src-migrate/modules/promo/components/Voucher.tsx
@@ -18,6 +18,7 @@ interface Voucher {
name: string;
description: string;
code: string;
+ voucher_category: [];
}
const VoucherComponent = () => {
diff --git a/src-migrate/pages/shop/cart/index.tsx b/src-migrate/pages/shop/cart/index.tsx
index 24baa933..03854d79 100644
--- a/src-migrate/pages/shop/cart/index.tsx
+++ b/src-migrate/pages/shop/cart/index.tsx
@@ -1,6 +1,6 @@
import style from './cart.module.css';
-import React, { useEffect, useMemo, useRef, useState } from 'react';
+import React, { useEffect, useMemo, useState } from 'react';
import Link from 'next/link';
import { Button, Checkbox, Spinner, Tooltip } from '@chakra-ui/react';
import { toast } from 'react-hot-toast';
@@ -14,127 +14,137 @@ import clsxm from '~/libs/clsxm';
import useDevice from '@/core/hooks/useDevice';
import CartSummaryMobile from '~/modules/cart/components/CartSummaryMobile';
import Image from '~/components/ui/image';
-import { CartItem } from '~/types/cart';
import { deleteUserCart, upsertUserCart } from '~/services/cart';
import { Trash2Icon } from 'lucide-react';
import { useProductCartContext } from '@/contexts/ProductCartContext';
+import {
+ getSelectedItemsFromCookie,
+ syncSelectedItemsWithCookie,
+ setAllSelectedInCookie,
+ removeSelectedItemsFromCookie,
+ removeCartItemsFromCookie,
+ checkboxUpdateState,
+ quantityUpdateState,
+} from '~/utils/cart';
+
+const SELECT_ALL_ID = 'select_all_checkbox';
const CartPage = () => {
const router = useRouter();
const auth = getAuth();
const [isStepApproval, setIsStepApproval] = useState(false);
- const [isSelectedAll, setIsSelectedAll] = useState(false);
- const [isButtonChek, setIsButtonChek] = useState(false);
- const [buttonSelectNow, setButtonSelectNow] = useState(true);
- const [isLoad, setIsLoad] = useState<boolean>(false);
const [isLoadDelete, setIsLoadDelete] = useState<boolean>(false);
const { loadCart, cart, summary, updateCartItem } = useCartStore();
- const useDivvice = useDevice();
+ const device = useDevice();
const { setRefreshCart } = useProductCartContext();
const [isTop, setIsTop] = useState(true);
- const [hasChanged, setHasChanged] = useState(false);
- const prevCartRef = useRef<CartItem[] | null>(null);
+ const [isUpdating, setIsUpdating] = useState(false);
+ const [isAnyCheckboxUpdating, setIsAnyCheckboxUpdating] = useState(false);
+ const [isAnyQuantityUpdating, setIsAnyQuantityUpdating] = useState(false);
+ // Subscribe to update state changes
useEffect(() => {
- const handleScroll = () => {
- setIsTop(window.scrollY < 200);
- };
+ const handleCheckboxUpdate = (isUpdating) =>
+ setIsAnyCheckboxUpdating(isUpdating);
+ const handleQuantityUpdate = (isUpdating) =>
+ setIsAnyQuantityUpdating(isUpdating);
+
+ checkboxUpdateState.addListener(handleCheckboxUpdate);
+ quantityUpdateState.addListener(handleQuantityUpdate);
- window.addEventListener('scroll', handleScroll);
return () => {
- window.removeEventListener('scroll', handleScroll);
+ checkboxUpdateState.removeListener(handleCheckboxUpdate);
+ quantityUpdateState.removeListener(handleQuantityUpdate);
};
}, []);
+ // Handle scroll for sticky header styling
useEffect(() => {
- if (typeof auth === 'object' && !cart) {
- loadCart(auth.id);
- setIsStepApproval(auth?.feature?.soApproval);
- }
- }, [auth, loadCart, cart, isButtonChek]);
+ const handleScroll = () => setIsTop(window.scrollY < 200);
- useEffect(() => {
- if (typeof auth === 'object' && !cart) {
- loadCart(auth.id);
- setIsStepApproval(auth?.feature?.soApproval);
- }
- }, [auth, loadCart, cart, isButtonChek]);
+ window.addEventListener('scroll', handleScroll);
+ return () => window.removeEventListener('scroll', handleScroll);
+ }, []);
+ // Initialize cart and sync with cookies
useEffect(() => {
- const hasSelectedChanged = () => {
- if (prevCartRef.current && cart) {
- const prevCart = prevCartRef.current;
- return cart.products.some(
- (item, index) =>
- prevCart[index] && prevCart[index].selected !== item.selected
- );
+ const initializeCart = async () => {
+ if (typeof auth === 'object' && !cart) {
+ await loadCart(auth.id);
+ setIsStepApproval(auth?.feature?.soApproval);
+
+ if (cart?.products) {
+ const { items, needsUpdate } = syncSelectedItemsWithCookie(
+ cart.products
+ );
+
+ if (needsUpdate) {
+ const updatedCart = {
+ ...cart,
+ products: cart.products.map((item) => ({
+ ...item,
+ selected:
+ items[item.id] !== undefined ? items[item.id] : item.selected,
+ })),
+ };
+ updateCartItem(updatedCart);
+ }
+ }
}
- return false;
};
- if (hasSelectedChanged()) {
- setHasChanged(true);
- // Perform necessary actions here if selection has changed
- } else {
- setHasChanged(false);
- }
-
- prevCartRef.current = cart ? [...cart.products] : null;
- }, [cart]);
+ initializeCart();
+ }, [auth, cart, loadCart, updateCartItem]);
+ // Computed values
const hasSelectedPromo = useMemo(() => {
- if (!cart) return false;
- return cart?.products?.some(
- (item) => item.cart_type === 'promotion' && item.selected
+ return (
+ cart?.products?.some(
+ (item) => item.cart_type === 'promotion' && item.selected
+ ) || false
);
}, [cart]);
const hasSelected = useMemo(() => {
- if (!cart) return false;
- return cart?.products?.some((item) => item.selected);
+ return cart?.products?.some((item) => item.selected) || false;
}, [cart]);
const hasSelectNoPrice = useMemo(() => {
- if (!cart) return false;
- return cart?.products?.some(
- (item) => item.selected && item.price.price_discount === 0
+ return (
+ cart?.products?.some(
+ (item) => item.selected && item.price.price_discount === 0
+ ) || false
);
}, [cart]);
const hasSelectedAll = useMemo(() => {
- if (!cart || !Array.isArray(cart.products)) return false;
+ if (!cart?.products?.length) return false;
return cart.products.every((item) => item.selected);
}, [cart]);
- useEffect(() => {
- const updateCartItems = async () => {
- if (typeof auth === 'object' && cart) {
- const upsertPromises = cart.products.map((item) =>
- upsertUserCart({
- userId: auth.id,
- type: item.cart_type,
- id: item.id,
- qty: item.quantity,
- selected: item.selected,
- })
- );
- try {
- await Promise.all(upsertPromises);
- await loadCart(auth.id);
- } catch (error) {
- console.error('Failed to update cart items:', error);
- }
- }
- };
-
- updateCartItems();
- }, [hasChanged]);
+ // Button states
+ const areButtonsDisabled =
+ isUpdating ||
+ isLoadDelete ||
+ isAnyCheckboxUpdating ||
+ isAnyQuantityUpdating;
+ const isSelectAllDisabled =
+ isUpdating || checkboxUpdateState.isCheckboxUpdating(SELECT_ALL_ID);
+ // Handlers
const handleCheckout = () => {
+ if (areButtonsDisabled) {
+ toast.error('Harap tunggu pembaruan selesai');
+ return;
+ }
router.push('/shop/checkout');
};
const handleQuotation = () => {
+ if (areButtonsDisabled) {
+ toast.error('Harap tunggu pembaruan selesai');
+ return;
+ }
if (hasSelectedPromo || !hasSelected) {
toast.error('Maaf, Barang promo tidak dapat dibuat quotation');
} else {
@@ -142,22 +152,63 @@ const CartPage = () => {
}
};
- const handleChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
- if (cart) {
+ const handleSelectAll = async (e: React.ChangeEvent<HTMLInputElement>) => {
+ if (!cart || isUpdating || typeof auth !== 'object') return;
+
+ const newSelectedState = !hasSelectedAll;
+ setIsUpdating(true);
+ checkboxUpdateState.startUpdate(SELECT_ALL_ID);
+
+ try {
+ // Update UI immediately
const updatedCart = {
...cart,
products: cart.products.map((item) => ({
...item,
- selected: !hasSelectedAll,
+ selected: newSelectedState,
})),
};
-
updateCartItem(updatedCart);
- if (hasSelectedAll) {
- setIsSelectedAll(false);
- } else {
- setIsSelectedAll(true);
- }
+
+ // Update cookies
+ const productIds = cart.products.map((item) => item.id);
+ setAllSelectedInCookie(productIds, newSelectedState, false);
+
+ // Update server
+ const updatePromises = cart.products.map((item) =>
+ upsertUserCart({
+ userId: auth.id,
+ type: item.cart_type,
+ id: item.id,
+ qty: item.quantity,
+ selected: newSelectedState,
+ purchase_tax_id: item.purchase_tax_id || null,
+ })
+ );
+
+ await Promise.all(updatePromises);
+ await loadCart(auth.id);
+ } catch (error) {
+ console.error('Error updating select all:', error);
+ toast.error('Gagal memperbarui pilihan');
+
+ // Revert on error
+ const revertedCart = {
+ ...cart,
+ products: cart.products.map((item) => ({
+ ...item,
+ selected: !newSelectedState,
+ })),
+ };
+ updateCartItem(revertedCart);
+ setAllSelectedInCookie(
+ cart.products.map((item) => item.id),
+ !newSelectedState,
+ false
+ );
+ } finally {
+ setIsUpdating(false);
+ checkboxUpdateState.endUpdate(SELECT_ALL_ID);
}
};
@@ -165,53 +216,124 @@ const CartPage = () => {
if (typeof auth !== 'object' || !cart) return;
setIsLoadDelete(true);
- for (const item of cart.products) {
- if (item.selected === true) {
+ checkboxUpdateState.startUpdate('delete_operation');
+
+ try {
+ const itemsToDelete = cart.products.filter((item) => item.selected);
+ const itemIdsToDelete = itemsToDelete.map((item) => item.id);
+ const cartIdsToDelete = itemsToDelete.map((item) => item.cart_id);
+
+ // Delete from server
+ for (const item of itemsToDelete) {
await deleteUserCart(auth.id, [item.cart_id]);
- await loadCart(auth.id);
}
+
+ // Update local state optimistically
+ const updatedProducts = cart.products.filter((item) => !item.selected);
+ const updatedCart = {
+ ...cart,
+ products: updatedProducts,
+ product_total: updatedProducts.length,
+ };
+ updateCartItem(updatedCart);
+
+ // Clean up cookies
+ removeSelectedItemsFromCookie(itemIdsToDelete);
+ removeCartItemsFromCookie(cartIdsToDelete);
+
+ // Reload from server
+ loadCart(auth.id).catch((error) =>
+ console.error('Error reloading cart:', error)
+ );
+
+ setRefreshCart(true);
+ toast.success('Item berhasil dihapus');
+ } catch (error) {
+ console.error('Failed to delete cart items:', error);
+ toast.error('Gagal menghapus item');
+ loadCart(auth.id);
+ } finally {
+ setIsLoadDelete(false);
+ checkboxUpdateState.endUpdate('delete_operation');
}
- setIsLoadDelete(false);
- setRefreshCart(true);
+ };
+
+ // Tooltip messages
+ const getTooltipMessage = () => {
+ if (isAnyQuantityUpdating) return 'Harap tunggu update quantity selesai';
+ if (isAnyCheckboxUpdating) return 'Harap tunggu pembaruan checkbox selesai';
+ if (isLoadDelete) return 'Harap tunggu penghapusan selesai';
+ if (isUpdating) return 'Harap tunggu pembaruan selesai';
+ return '';
+ };
+
+ const getQuotationTooltip = () => {
+ const baseMessage = getTooltipMessage();
+ if (baseMessage) return baseMessage;
+ if (hasSelectedPromo) return 'Barang promo tidak dapat dibuat quotation';
+ if (!hasSelected) return 'Tidak ada item yang dipilih';
+ return '';
+ };
+
+ const getCheckoutTooltip = () => {
+ const baseMessage = getTooltipMessage();
+ if (baseMessage) return baseMessage;
+ if (!hasSelected) return 'Tidak ada item yang dipilih';
+ if (hasSelectNoPrice) return 'Terdapat item yang tidak ada harga';
+ return '';
+ };
+
+ const getDeleteTooltip = () => {
+ const baseMessage = getTooltipMessage();
+ if (baseMessage) return baseMessage;
+ if (!hasSelected) return 'Tidak ada item yang dipilih';
+ return '';
};
return (
<>
+ {/* Sticky Header */}
<div
className={`${
isTop ? 'border-b-[0px]' : 'border-b-[1px]'
- } sticky md:top-[157px] flex-col bg-white py-4 border-gray-300 z-50 sm:w-full md:w-3/4`}
+ } sticky md:top-[157px] flex-col bg-white py-4 border-gray-300 z-50 sm:w-full md:w-3/4`}
>
- <h1 className={`${style['title']}`}>Keranjang Belanja</h1>
+ <div className='flex items-center justify-between mb-2'>
+ <h1 className={style.title}>Keranjang Belanja</h1>
+ </div>
+
<div className='h-2' />
- <div className={`flex items-center object-center justify-between `}>
+ <div className='flex items-center object-center justify-between flex-wrap gap-2'>
<div className='flex items-center object-center'>
- {isLoad && <Spinner className='my-auto' size='sm' />}
- {!isLoad && (
- <Checkbox
- borderColor='gray.600'
- colorScheme='red'
- size='lg'
- isChecked={hasSelectedAll}
- onChange={handleChange}
- />
- )}
+ <Checkbox
+ borderColor='gray.600'
+ colorScheme='red'
+ size='lg'
+ isChecked={hasSelectedAll}
+ onChange={handleSelectAll}
+ isDisabled={isSelectAllDisabled}
+ opacity={isSelectAllDisabled ? 0.5 : 1}
+ cursor={isSelectAllDisabled ? 'not-allowed' : 'pointer'}
+ _disabled={{
+ opacity: 0.5,
+ cursor: 'not-allowed',
+ backgroundColor: 'gray.100',
+ }}
+ />
<p className='p-2 text-caption-2'>
{hasSelectedAll ? 'Uncheck all' : 'Select all'}
</p>
</div>
- <div className='delate all flex items-center object-center'>
- <Tooltip
- label={clsxm({
- 'Tidak ada item yang dipilih': !hasSelected,
- })}
- >
+
+ <div className='flex items-center object-center'>
+ <Tooltip label={getDeleteTooltip()}>
<Button
bg='#fadede'
variant='outline'
colorScheme='red'
- w='full'
- isDisabled={!hasSelected}
+ w='auto'
+ size={device.isMobile ? 'sm' : 'md'}
+ isDisabled={!hasSelected || areButtonsDisabled}
onClick={handleDelete}
>
{isLoadDelete && <Spinner size='xs' />}
@@ -223,19 +345,20 @@ const CartPage = () => {
</div>
</div>
- <div className={style['content']}>
+ {/* Main Content */}
+ <div className={style.content}>
<div className={style['item-wrapper']}>
<div className={style['item-skeleton']}>
{!cart && <CartItemModule.Skeleton count={5} height='120px' />}
</div>
- <div className={style['items']}>
+ <div className={style.items}>
{cart?.products?.map((item) => (
<CartItemModule key={item.id} item={item} />
))}
{cart?.products?.length === 0 && (
- <div className='flex flex-col items-center'>
+ <div className='flex flex-col items-center p-4'>
<Image
src='/images/empty_cart.svg'
alt='Empty Cart'
@@ -261,17 +384,28 @@ const CartPage = () => {
)}
</div>
</div>
+
+ {/* Cart Summary */}
<div
className={`${style['summary-wrapper']} ${
- useDivvice.isMobile && cart?.product_total === 0 ? 'hidden' : ''
+ device.isMobile && (!cart || cart?.product_total === 0)
+ ? 'hidden'
+ : ''
}`}
>
- <div className={style['summary']}>
- {useDivvice.isMobile && (
- <CartSummaryMobile {...summary} isLoaded={!!cart} />
- )}
- {!useDivvice.isMobile && (
- <CartSummary {...summary} isLoaded={!!cart} />
+ <div className={style.summary}>
+ {device.isMobile ? (
+ <CartSummaryMobile
+ {...summary}
+ isLoaded={!!cart}
+ products={cart?.products}
+ />
+ ) : (
+ <CartSummary
+ {...summary}
+ isLoaded={!!cart}
+ products={cart?.products}
+ />
)}
<div
@@ -281,34 +415,31 @@ const CartPage = () => {
: style['summary-buttons']
}
>
- <Tooltip
- label={
- hasSelectedPromo &&
- 'Barang promo tidak dapat dibuat quotation'
- }
- >
+ <Tooltip label={getQuotationTooltip()}>
<Button
colorScheme='yellow'
w='full'
- isDisabled={hasSelectedPromo || !hasSelected}
+ isDisabled={
+ hasSelectedPromo || !hasSelected || areButtonsDisabled
+ }
onClick={handleQuotation}
>
+ {areButtonsDisabled && <Spinner size='sm' mr={2} />}
Quotation
</Button>
</Tooltip>
+
{!isStepApproval && (
- <Tooltip
- label={clsxm({
- 'Tidak ada item yang dipilih': !hasSelected,
- 'Terdapat item yang tidak ada harga': hasSelectNoPrice,
- })}
- >
+ <Tooltip label={getCheckoutTooltip()}>
<Button
colorScheme='red'
w='full'
- isDisabled={!hasSelected || hasSelectNoPrice}
+ isDisabled={
+ !hasSelected || hasSelectNoPrice || areButtonsDisabled
+ }
onClick={handleCheckout}
>
+ {areButtonsDisabled && <Spinner size='sm' mr={2} />}
Checkout
</Button>
</Tooltip>
@@ -321,4 +452,4 @@ const CartPage = () => {
);
};
-export default CartPage;
+export default CartPage; \ No newline at end of file
diff --git a/src-migrate/types/product.ts b/src-migrate/types/product.ts
index 85ea702a..746cdd4a 100644
--- a/src-migrate/types/product.ts
+++ b/src-migrate/types/product.ts
@@ -4,6 +4,7 @@ export interface IProduct {
id: number;
image: string;
image_mobile: string;
+ image_carousel: string[];
code: string;
display_name: string;
name: string;
@@ -34,7 +35,7 @@ export interface IProduct {
name: string;
logo: string;
};
- voucher_pasti_hemat : any;
+ voucher_pasti_hemat: any;
}
export interface IProductDetail extends IProduct {
diff --git a/src-migrate/types/voucher.ts b/src-migrate/types/voucher.ts
index 3e90f449..d3140372 100644
--- a/src-migrate/types/voucher.ts
+++ b/src-migrate/types/voucher.ts
@@ -3,6 +3,7 @@ export interface IVoucher {
image: string;
name: string;
code: string;
+ voucher_category: [];
description: string | false;
remaining_time: string;
}
diff --git a/src-migrate/utils/cart.js b/src-migrate/utils/cart.js
new file mode 100644
index 00000000..4bdee49a
--- /dev/null
+++ b/src-migrate/utils/cart.js
@@ -0,0 +1,455 @@
+// cart-cookie-utils.js
+import Cookies from 'js-cookie';
+import checkboxUpdateState from './checkBoxState';
+
+// Constants
+const CART_ITEMS_COOKIE = 'cart_data';
+const SELECTED_ITEMS_COOKIE = 'cart_selected_items';
+const COOKIE_EXPIRY_DAYS = 7; // Cookie akan berlaku selama 7 hari
+
+/**
+ * Mengambil data cart lengkap dari cookie
+ * @returns {Object} Object dengan key cart_id dan value cart item data lengkap
+ */
+export const getCartDataFromCookie = () => {
+ try {
+ const storedData = Cookies.get(CART_ITEMS_COOKIE);
+ return storedData ? JSON.parse(storedData) : {};
+ } catch (error) {
+ console.error('Error reading cart data from cookie:', error);
+ return {};
+ }
+};
+
+/**
+ * Menyimpan data cart lengkap ke cookie
+ * @param {Object} cartData Object dengan key cart_id dan value cart item data lengkap
+ */
+export const setCartDataToCookie = (cartData) => {
+ try {
+ Cookies.set(CART_ITEMS_COOKIE, JSON.stringify(cartData), {
+ expires: COOKIE_EXPIRY_DAYS,
+ path: '/',
+ sameSite: 'strict',
+ });
+ } catch (error) {
+ console.error('Error saving cart data to cookie:', error);
+ }
+};
+
+/**
+ * Mengambil state selected items dari cookie
+ * @returns {Object} Object dengan key product id dan value boolean selected status
+ */
+export const getSelectedItemsFromCookie = () => {
+ try {
+ const storedItems = Cookies.get(SELECTED_ITEMS_COOKIE);
+ return storedItems ? JSON.parse(storedItems) : {};
+ } catch (error) {
+ console.error('Error reading selected items from cookie:', error);
+ return {};
+ }
+};
+
+/**
+ * Menyimpan state selected items ke cookie
+ * @param {Object} items Object dengan key product id dan value boolean selected status
+ */
+export const setSelectedItemsToCookie = (items) => {
+ try {
+ Cookies.set(SELECTED_ITEMS_COOKIE, JSON.stringify(items), {
+ expires: COOKIE_EXPIRY_DAYS,
+ path: '/',
+ sameSite: 'strict',
+ });
+ } catch (error) {
+ console.error('Error saving selected items to cookie:', error);
+ }
+};
+
+/**
+ * Transform cart items dari format API ke format yang lebih simpel untuk disimpan di cookie
+ * @param {Array} cartItems Array cart items dari API
+ * @returns {Object} Object dengan key cart_id dan value cart item data
+ */
+export const transformCartItemsForCookie = (cartItems) => {
+ if (!cartItems || !Array.isArray(cartItems)) return {};
+
+ const cartData = {};
+
+ cartItems.forEach((item) => {
+ // Skip items yang tidak memiliki cart_id
+ if (!item.cart_id) return;
+
+ cartData[item.cart_id] = {
+ id: item.id,
+ cart_id: item.cart_id,
+ cart_type: item.cart_type,
+ product: item.product_id
+ ? {
+ id: item.product_id,
+ name: item.product_name || '',
+ }
+ : null,
+ program_line: item.program_line_id
+ ? {
+ id: item.program_line_id,
+ name: item.program_line_name || '',
+ }
+ : null,
+ quantity: item.quantity,
+ selected: item.selected,
+ price: item.price,
+ package_price: item.package_price,
+ source: item.source || 'add_to_cart',
+ };
+ });
+
+ return cartData;
+};
+
+/**
+ * Sinkronisasi cart data dan selected items dari server dengan cookie
+ * @param {Object} cart Cart object dari API
+ * @returns {Object} Object yang berisi updated cartData dan selectedItems
+ */
+export const syncCartWithCookie = (cart) => {
+ try {
+ if (!cart || !cart.products) return { needsUpdate: false };
+
+ // Transform data API ke cookie
+ const serverCartData = transformCartItemsForCookie(cart.products);
+
+ // Ambil data lama dari cookie
+ const existingCartData = getCartDataFromCookie();
+
+ // Ambil selected status dari cookie
+ const selectedItems = getSelectedItemsFromCookie();
+
+ // Gabungkan data cart, (prioritize data server)
+ const mergedCartData = { ...existingCartData, ...serverCartData };
+
+ // Periksa apakah ada perbedaan status selected
+ let needsUpdate = false;
+
+ // Update selected status berdasarkan cookie jika ada
+ for (const cartId in mergedCartData) {
+ const item = mergedCartData[cartId];
+ if (item.id && selectedItems[item.id] !== undefined) {
+ // Jika status di cookie berbeda dengan di cart
+ if (item.selected !== selectedItems[item.id]) {
+ needsUpdate = true;
+ item.selected = selectedItems[item.id];
+ }
+ } else if (item.id) {
+ selectedItems[item.id] = item.selected;
+ }
+ }
+
+ // Simpan ke cookie
+ setCartDataToCookie(mergedCartData);
+ setSelectedItemsToCookie(selectedItems);
+
+ return {
+ cartData: mergedCartData,
+ selectedItems,
+ needsUpdate,
+ };
+ } catch (error) {
+ console.error('Error syncing cart with cookie:', error);
+ return { needsUpdate: false };
+ }
+};
+
+/**
+ * Update selected status item di cookie
+ * @param {number} productId ID produk
+ * @param {boolean} isSelected Status selected baru
+ * @param {boolean} notifyUpdate Whether to notify checkbox update state (default: true)
+ */
+export const updateSelectedItemInCookie = (
+ productId,
+ isSelected,
+ notifyUpdate = true
+) => {
+ try {
+ // Notify checkbox update state if requested
+ if (notifyUpdate) {
+ checkboxUpdateState.startUpdate();
+ }
+
+ const selectedItems = getSelectedItemsFromCookie();
+ selectedItems[productId] = isSelected;
+ setSelectedItemsToCookie(selectedItems);
+
+ // Update juga di cart data
+ const cartData = getCartDataFromCookie();
+
+ for (const cartId in cartData) {
+ const item = cartData[cartId];
+ if (item.id === productId) {
+ item.selected = isSelected;
+ }
+ }
+
+ setCartDataToCookie(cartData);
+
+ return { selectedItems, cartData };
+ } catch (error) {
+ console.error('Error updating selected item in cookie:', error);
+ return {};
+ } finally {
+ // End update notification if requested
+ if (notifyUpdate) {
+ checkboxUpdateState.endUpdate();
+ }
+ }
+};
+
+/**
+ * Set semua item menjadi selected atau unselected di cookie
+ * @param {Array} productIds Array product IDs
+ * @param {boolean} isSelected Status selected baru
+ * @param {boolean} notifyUpdate Whether to notify checkbox update state (default: true)
+ */
+export const setAllSelectedInCookie = (
+ productIds,
+ isSelected,
+ notifyUpdate = true
+) => {
+ try {
+ // Notify checkbox update state if requested
+ if (notifyUpdate) {
+ checkboxUpdateState.startUpdate();
+ }
+
+ const selectedItems = getSelectedItemsFromCookie();
+
+ productIds.forEach((id) => {
+ if (id) selectedItems[id] = isSelected;
+ });
+
+ setSelectedItemsToCookie(selectedItems);
+
+ // Update juga di cart data
+ const cartData = getCartDataFromCookie();
+
+ for (const cartId in cartData) {
+ if (productIds.includes(cartData[cartId].id)) {
+ cartData[cartId].selected = isSelected;
+ }
+ }
+
+ setCartDataToCookie(cartData);
+
+ return { selectedItems, cartData };
+ } catch (error) {
+ console.error('Error setting all selected in cookie:', error);
+ return {};
+ } finally {
+ // End update notification if requested
+ if (notifyUpdate) {
+ checkboxUpdateState.endUpdate();
+ }
+ }
+};
+
+/**
+ * Hapus item dari cookie
+ * @param {Array} cartIds Array cart IDs untuk dihapus
+ */
+export const removeCartItemsFromCookie = (cartIds) => {
+ try {
+ const cartData = getCartDataFromCookie();
+ const selectedItems = getSelectedItemsFromCookie();
+ const productIdsToRemove = [];
+
+ // Hapus item dari cartData dan catat product IDs
+ cartIds.forEach((cartId) => {
+ if (cartData[cartId]) {
+ if (cartData[cartId].id) {
+ productIdsToRemove.push(cartData[cartId].id);
+ }
+ delete cartData[cartId];
+ }
+ });
+
+ // Hapus dari selectedItems
+ productIdsToRemove.forEach((productId) => {
+ if (selectedItems[productId] !== undefined) {
+ delete selectedItems[productId];
+ }
+ });
+
+ // Simpan kembali ke cookie
+ setCartDataToCookie(cartData);
+ setSelectedItemsToCookie(selectedItems);
+
+ return { cartData, selectedItems };
+ } catch (error) {
+ console.error('Error removing cart items from cookie:', error);
+ return {};
+ }
+};
+
+/**
+ * Hapus item selected dari cookie berdasarkan product IDs
+ * @param {Array} productIds Array product IDs untuk dihapus
+ */
+/**
+ * Hapus item selected dari cookie berdasarkan product IDs dan juga hapus dari cart data
+ * @param {Array} productIds Array product IDs untuk dihapus
+ */
+
+/**
+ * Force reset semua selected items ke unselected state
+ */
+export const forceResetAllSelectedItems = () => {
+ try {
+ checkboxUpdateState.startUpdate();
+
+ const cartData = getCartDataFromCookie();
+ const selectedItems = {};
+
+ // Reset semua selected status di cartData
+ for (const cartId in cartData) {
+ cartData[cartId].selected = false;
+ if (cartData[cartId].id) {
+ selectedItems[cartData[cartId].id] = false;
+ }
+ }
+
+ // Simpan kembali ke cookie
+ setCartDataToCookie(cartData);
+ setSelectedItemsToCookie(selectedItems);
+
+ return { cartData, selectedItems };
+ } catch (error) {
+ console.error('Error resetting all selected items:', error);
+ return {};
+ } finally {
+ checkboxUpdateState.endUpdate();
+ }
+};
+
+/**
+ * Sync selected items between cookie and cart data
+ * @param {Array} cartProducts Products array from cart
+ */
+export const syncSelectedItemsWithCookie = (cartProducts) => {
+ try {
+ if (!cartProducts || !Array.isArray(cartProducts)) {
+ return { items: {}, needsUpdate: false };
+ }
+
+ const selectedItems = getSelectedItemsFromCookie();
+ let needsUpdate = false;
+
+ // Check if we need to update any items based on cookie values
+ cartProducts.forEach((product) => {
+ if (product.id && selectedItems[product.id] !== undefined) {
+ if (product.selected !== selectedItems[product.id]) {
+ needsUpdate = true;
+ }
+ } else if (product.id) {
+ // If not in cookie, add with current value
+ selectedItems[product.id] = product.selected;
+ }
+ });
+
+ // Update the cookie with the latest values
+ setSelectedItemsToCookie(selectedItems);
+
+ return { items: selectedItems, needsUpdate };
+ } catch (error) {
+ console.error('Error syncing selected items with cookie:', error);
+ return { items: {}, needsUpdate: false };
+ }
+};
+
+// Export the checkbox update state for use in components
+export { checkboxUpdateState };
+
+/**
+ * Hapus item selected dari cookie berdasarkan product IDs dan juga hapus dari cart data
+ * @param {Array} productIds Array product IDs untuk dihapus
+ */
+/**
+ * Hapus item selected dari cookie berdasarkan product IDs dan juga hapus dari cart data
+ * @param {Array} productIds Array product IDs untuk dihapus
+ */
+export const removeSelectedItemsFromCookie = (productIds) => {
+ try {
+ const selectedItems = getSelectedItemsFromCookie();
+ const cartData = getCartDataFromCookie();
+ const cartIdsToRemove = [];
+
+ // Find cart IDs that match the product IDs
+ for (const cartId in cartData) {
+ if (productIds.includes(cartData[cartId].id)) {
+ cartIdsToRemove.push(cartId);
+ }
+ }
+
+ // Remove from selectedItems
+ productIds.forEach((productId) => {
+ if (selectedItems[productId] !== undefined) {
+ delete selectedItems[productId];
+ }
+ });
+
+ // Remove from cartData
+ cartIdsToRemove.forEach((cartId) => {
+ delete cartData[cartId];
+ });
+
+ // Save both cookies
+ setSelectedItemsToCookie(selectedItems);
+ setCartDataToCookie(cartData);
+
+ return { selectedItems, cartData };
+ } catch (error) {
+ console.error('Error removing selected items from cookie:', error);
+ return {};
+ }
+};
+
+class QuantityUpdateState {
+ constructor() {
+ this.updateItems = new Set();
+ this.listeners = new Set();
+ }
+
+ startUpdate(itemId) {
+ this.updateItems.add(itemId);
+ this.notifyListeners();
+ }
+
+ endUpdate(itemId) {
+ this.updateItems.delete(itemId);
+ this.notifyListeners();
+ }
+
+ isAnyQuantityUpdating() {
+ return this.updateItems.size > 0;
+ }
+
+ isItemUpdating(itemId) {
+ return this.updateItems.has(itemId);
+ }
+
+ addListener(callback) {
+ this.listeners.add(callback);
+ }
+
+ removeListener(callback) {
+ this.listeners.delete(callback);
+ }
+
+ notifyListeners() {
+ const isUpdating = this.isAnyQuantityUpdating();
+ this.listeners.forEach(callback => callback(isUpdating));
+ }
+}
+
+export const quantityUpdateState = new QuantityUpdateState(); \ No newline at end of file
diff --git a/src-migrate/utils/checkBoxState.js b/src-migrate/utils/checkBoxState.js
new file mode 100644
index 00000000..9568c321
--- /dev/null
+++ b/src-migrate/utils/checkBoxState.js
@@ -0,0 +1,90 @@
+/**
+ * State manager for checkbox updates
+ * Tracks global and individual checkbox update states
+ */
+class CheckboxUpdateState {
+ constructor() {
+ this.updateCount = 0;
+ this.listeners = new Set();
+ this.updatingCheckboxIds = new Set();
+ }
+
+ // Global update state (for buttons quotation and checkout)
+ isUpdating() {
+ return this.updateCount > 0;
+ }
+
+ // Individual checkbox state
+ isCheckboxUpdating(itemId) {
+ return this.updatingCheckboxIds.has(String(itemId));
+ }
+
+ // Start update
+ startUpdate(itemId = null) {
+ this.updateCount++;
+
+ if (itemId !== null) {
+ this.updatingCheckboxIds.add(String(itemId));
+ }
+
+ this.notifyListeners();
+ return this.updateCount;
+ }
+
+ // End update
+ endUpdate(itemId = null) {
+ this.updateCount = Math.max(0, this.updateCount - 1);
+
+ if (itemId !== null) {
+ this.updatingCheckboxIds.delete(String(itemId));
+ }
+
+ this.notifyListeners();
+ return this.updateCount;
+ }
+
+ // Reset all states
+ reset() {
+ this.updateCount = 0;
+ this.updatingCheckboxIds.clear();
+ this.notifyListeners();
+ }
+
+ // Listener management
+ addListener(callback) {
+ if (typeof callback === 'function') {
+ this.listeners.add(callback);
+ // Immediate callback with current state
+ callback(this.isUpdating());
+ }
+ }
+
+ removeListener(callback) {
+ this.listeners.delete(callback);
+ }
+
+ // Debug helpers
+ getUpdateCount() {
+ return this.updateCount;
+ }
+
+ getUpdatingCheckboxIds() {
+ return [...this.updatingCheckboxIds];
+ }
+
+ // Private method to notify listeners
+ notifyListeners() {
+ const isUpdating = this.isUpdating();
+
+ this.listeners.forEach((listener) => {
+ try {
+ listener(isUpdating);
+ } catch (error) {
+ console.error('Checkbox update state listener error:', error);
+ }
+ });
+ }
+}
+
+const checkboxUpdateState = new CheckboxUpdateState();
+export default checkboxUpdateState;
diff --git a/src-migrate/validations/tempo.ts b/src-migrate/validations/tempo.ts
index cf5914b5..46ac1ef1 100644
--- a/src-migrate/validations/tempo.ts
+++ b/src-migrate/validations/tempo.ts
@@ -122,8 +122,12 @@ export const TempoSchemaPengiriman = z.object({
zipInvoice: z.string().min(1, { message: 'Kode pos harus diisi' }),
isSameAddrees: z.string(),
isSameAddreesStreet: z.string(),
- tukarInvoiceInput: z.string().optional(),
- tukarInvoiceInputPembayaran: z.string().optional(),
+ tukarInvoiceInput: z
+ .string()
+ .min(1, { message: 'Jadwal Penukaran Invoice Harus Diisi' }),
+ tukarInvoiceInputPembayaran: z
+ .string()
+ .min(1, { message: 'Jadwal Pembayaran Harus Diisi' }),
dokumenPengiriman: z.string().optional(),
dokumenPengirimanInput: z.string().optional(),
dokumenKirimInput: z.string().optional(),
diff --git a/src/api/productApi.js b/src/api/productApi.js
index 4a29b59d..dc96a77e 100644
--- a/src/api/productApi.js
+++ b/src/api/productApi.js
@@ -1,13 +1,15 @@
-import axios from 'axios'
+import axios from 'axios';
export const popularProductApi = () => {
return async () => {
const today = new Date();
- const dayOfYear = Math.floor((today - new Date(today.getFullYear(), 0, 0)) / 86400000);
+ const dayOfYear = Math.floor(
+ (today - new Date(today.getFullYear(), 0, 0)) / 86400000
+ );
const page = (dayOfYear % 24) + 1;
const dataPopularProducts = await axios(
`${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/search?q=*&page=${page}&orderBy=stock&priceFrom=1`
- )
- return dataPopularProducts.data.response
- }
-}
+ );
+ return dataPopularProducts.data.response;
+ };
+};
diff --git a/src/lib/auth/components/CompanyProfile.jsx b/src/lib/auth/components/CompanyProfile.jsx
index 6d4da1d1..d404ebe7 100644
--- a/src/lib/auth/components/CompanyProfile.jsx
+++ b/src/lib/auth/components/CompanyProfile.jsx
@@ -59,7 +59,11 @@ const CompanyProfile = () => {
useEffect(() => {
const loadProfile = async () => {
const dataProfile = await addressApi({
- id: auth?.company ? (auth.parentId ? auth.parentId : auth.partnerId) : auth.partnerId,
+ id: auth?.company
+ ? auth.parentId
+ ? auth.parentId
+ : auth.partnerId
+ : auth.partnerId,
});
setCompany_type(dataProfile?.companyType);
setValue('name', dataProfile?.name);
@@ -311,9 +315,19 @@ const CompanyProfile = () => {
<input
{...register('npwp')}
type='text'
+ disabled
className='form-input mt-3'
maxLength={16}
/>
+ <span className='text-xs opacity-60 text-red-500'>
+ *Untuk mengganti NPWP bisa menghubungi kami{' '}
+ <a
+ href='https://wa.me/6281717181922' target='_blank' rel='noreferrer'
+ style={{ textDecoration: 'underline' }}
+ >
+ disini.
+ </a>
+ </span>
<div className='text-caption-2 text-danger-500 mt-1'>
{errors.npwp?.message}
</div>
diff --git a/src/lib/checkout/components/Checkout.jsx b/src/lib/checkout/components/Checkout.jsx
index 637706a2..0b386c30 100644
--- a/src/lib/checkout/components/Checkout.jsx
+++ b/src/lib/checkout/components/Checkout.jsx
@@ -31,12 +31,12 @@ import addressesApi from '@/lib/address/api/addressesApi';
import { MapPinIcon } from 'lucide-react';
import CartItem from '~/modules/cart/components/Item.tsx';
import ExpedisiList from '../api/ExpedisiList';
-import { getVoucher } from '../api/getVoucher';
+import { findVoucher, getVoucher, getVoucherNew } from '../api/getVoucher';
+
import { useAddress } from '../stores/useAdress';
import SectionExpedition from './SectionExpedition';
import { useCheckout } from '../stores/stateCheckout';
import { formatShipmentRange, getToDate } from '../utils/functionCheckouit';
-
const SELF_PICKUP_ID = 32;
const { checkoutApi } = require('../api/checkoutApi');
@@ -55,7 +55,9 @@ function convertToInternational(number) {
}
const Checkout = () => {
- const PPN = process.env.NEXT_PUBLIC_PPN ? parseFloat(process.env.NEXT_PUBLIC_PPN) : 0;
+ const PPN = process.env.NEXT_PUBLIC_PPN
+ ? parseFloat(process.env.NEXT_PUBLIC_PPN)
+ : 0;
const router = useRouter();
const query = router.query.source ?? null;
const qVoucher = router.query.voucher ?? null;
@@ -72,11 +74,18 @@ const Checkout = () => {
voucher: activeVoucher,
voucher_shipping: activeVoucherShipping,
}),
+ //biteship
{
keepPreviousData: true, // Menjaga data sebelumnya sampai data baru tersedia
}
);
+ // const [selectedAddress, setSelectedAddress] = useState({
+ // shipping: null,
+ // invoicing: null,
+ // });
+ // const [addresses, setAddresses] = useState(null);
+
const {
selectedAddress,
setSelectedAddress,
@@ -131,12 +140,17 @@ const Checkout = () => {
}
}, [addresses]);
+ // const [products, setProducts] = useState(null);
const [totalWeight, setTotalWeight] = useState(0);
const [priceCheck, setPriceCheck] = useState(false);
+ // const [listExpedisi, setExpedisi] = useState([]);
const [listserviceExpedisi, setListServiceExpedisi] = useState([]);
const [selectedExpedisi, setSelectedExpedisi] = useState(0);
const [selectedCarrierId, setselectedCarrierId] = useState(0);
const [selectedCarrier, setselectedCarrier] = useState(0);
+ //new release
+ // const [biayaKirim, setBiayaKirim] = useState(0);
+ // const [checkWeigth, setCheckWeight] = useState(false);
const [selectedServiceType, setSelectedServiceType] = useState(null);
const [selectedExpedisiService, setselectedExpedisiService] = useState(null);
// const [etd, setEtd] = useState(null);
@@ -151,9 +165,11 @@ const Checkout = () => {
const [findCodeVoucher, SetFindVoucher] = useState(null);
const [selisihHargaCode, SetSelisihHargaCode] = useState(null);
const [buttonTerapkan, SetButtonTerapkan] = useState(false);
+ // const [checkoutValidation, setCheckoutValidation] = useState(false);
const [loadingVoucher, setLoadingVoucher] = useState(true);
const [loadingRajaOngkir, setLoadingRajaOngkir] = useState(false);
const [grandTotal, setGrandTotal] = useState(0);
+ const [hasFlashSale, setHasFlashSale] = useState(false);
const {
checkWeigth,
@@ -174,23 +190,37 @@ const Checkout = () => {
setExpedisi,
productSla
} = useCheckout();
-
const expedisiValidation = useRef(null);
const voucher = async () => {
if (!listVouchers) {
try {
setLoadingVoucher(true);
+ const productCategories = products
+ ?.reduce((categories, product) => {
+ if (product.categories && Array.isArray(product.categories)) {
+ product.categories.forEach((category) => {
+ if (category.id && !categories.includes(category.id)) {
+ categories.push(category.id);
+ }
+ });
+ }
+ return categories;
+ }, [])
+ .join(',');
+
let dataVoucher = await getVoucher(auth?.id, {
source: query,
type: 'all,brand',
- partner_id : auth?.partnerId,
+ partner_id: auth?.partnerId,
+ voucher_category: productCategories, // Add the product categories
});
SetListVoucher(dataVoucher);
let dataVoucherShipping = await getVoucher(auth?.id, {
source: query,
type: 'shipping',
+ voucher_category: productCategories, // Add the product categories
});
SetListVoucherShipping(dataVoucherShipping);
} finally {
@@ -200,16 +230,28 @@ const Checkout = () => {
};
const VoucherCode = async (code) => {
- // let dataVoucher = await findVoucher(code, auth.id, query);
+ const productCategories = products
+ ?.reduce((categories, product) => {
+ if (product.categories && Array.isArray(product.categories)) {
+ product.categories.forEach((category) => {
+ if (category.id && !categories.includes(category.id)) {
+ categories.push(category.id);
+ }
+ });
+ }
+ return categories;
+ }, [])
+ .join(',');
+
let dataVoucher = await getVoucher(auth?.id, {
source: query,
code: code,
+ voucher_category: productCategories, // Add the product categories
});
if (dataVoucher.length <= 0) {
SetFindVoucher(1);
return;
}
-
dataVoucher.forEach((addNewLine) => {
if (addNewLine.applyType !== 'shipping') {
@@ -320,6 +362,58 @@ const Checkout = () => {
};
}, []);
+ const hitungDiscountVoucher = (code, source) => {
+ let countDiscount = 0;
+ if (source === 'voucher') {
+ let dataVoucherIndex = listVouchers.findIndex(
+ (voucher) => voucher.code == code
+ );
+ let dataActiveVoucher = listVouchers[dataVoucherIndex];
+
+ countDiscount = dataActiveVoucher.discountVoucher;
+ } else {
+ let dataVoucherIndex = listVoucherShippings.findIndex(
+ (voucher) => voucher.code == code
+ );
+ let dataActiveVoucher = listVoucherShippings[dataVoucherIndex];
+
+ countDiscount = dataActiveVoucher.discountVoucher;
+ }
+
+ /*if (dataActiveVoucher.discountType === 'percentage') {
+ countDiscount = cartCheckout?.subtotal * (dataActiveVoucher.discountAmount / 100)
+ if (
+ dataActiveVoucher.maxDiscountAmount > 0 &&
+ countDiscount > dataActiveVoucher.maxDiscountAmount
+ ) {
+ countDiscount = dataActiveVoucher.maxDiscountAmount
+ }
+ } else {
+ countDiscount = dataActiveVoucher.discountAmount
+ }*/
+
+ return countDiscount;
+ };
+
+ // useEffect(() => {
+ // if (!listVouchers) return;
+ // if (!activeVoucher) return;
+
+ // console.log('voucher')
+ // const countDiscount = hitungDiscountVoucher(activeVoucher, 'voucher');
+
+ // SetDiscountVoucher(countDiscount);
+ // }, [activeVoucher, listVouchers]);
+
+ // useEffect(() => {
+ // if (!listVoucherShippings) return;
+ // if (!activeVoucherShipping) return;
+
+ // const countDiscount = hitungDiscountVoucher(activeVoucherShipping, 'voucher_shipping');
+
+ // SetDiscountVoucherOngkir(countDiscount);
+ // }, [activeVoucherShipping, listVoucherShippings]);
+
useEffect(() => {
if (qVoucher === 'PASTIHEMAT' && listVouchers) {
let code = qVoucher;
@@ -337,6 +431,72 @@ const Checkout = () => {
setHasFlashSale(hasFlashSale);
}, [cartCheckout]);
+ // useEffect(() => {
+ // setCheckoutValidation(false);
+ // const loadServiceRajaOngkir = async () => {
+ // setLoadingRajaOngkir(true);
+ // const body = {
+ // origin: 2127,
+ // destination: selectedAddress.shipping.rajaongkirCityId,
+ // weight: totalWeight,
+ // courier: selectedCarrier,
+ // originType: 'subdistrict',
+ // destinationType: 'subdistrict',
+ // };
+ // setBiayaKirim(0);
+ // const dataService = await axios(
+ // '/api/rajaongkir-service?body=' + JSON.stringify(body)
+ // );
+ // setLoadingRajaOngkir(false);
+ // setListServiceExpedisi(dataService.data[0].costs);
+ // if (dataService.data[0].costs[0]) {
+ // setBiayaKirim(dataService.data[0].costs[0]?.cost[0].value);
+ // setselectedExpedisiService(
+ // dataService.data[0].costs[0]?.description +
+ // '-' +
+ // dataService.data[0].costs[0]?.service
+ // );
+ // setEtd(dataService.data[0].costs[0]?.cost[0].etd);
+ // toast.success('Harap pilih tipe layanan pengiriman');
+ // } else {
+ // toast.error('Maaf, layanan tidak tersedia. Mohon pilih expedisi lain.');
+ // }
+ // };
+ // if (selectedCarrier != 0 && selectedCarrier != 1 && totalWeight > 0) {
+ // loadServiceRajaOngkir();
+ // } else {
+ // setListServiceExpedisi();
+ // setBiayaKirim(0);
+ // setselectedExpedisiService();
+ // setEtd();
+ // }
+ // }, [selectedCarrier, selectedAddress, totalWeight]);
+ //
+ // useEffect(() => {
+ // if (selectedServiceType) {
+ // let serviceType = selectedServiceType.split(',');
+ // setBiayaKirim(serviceType[0]);
+ // setselectedExpedisiService(serviceType[1]);
+ // setEtd(serviceType[2]);
+ // }
+ // }, [selectedServiceType]);
+ //
+ // useEffect(() => {
+ // if (etd) setEtdFix(calculateEstimatedArrival(etd));
+ // }, [etd]);
+ //
+ // useEffect(() => {
+ // if (selectedExpedisi) {
+ // let serviceType = selectedExpedisi.split(',');
+ // if (serviceType[0] === 0) return;
+ //
+ // setselectedCarrier(serviceType[0]);
+ // setselectedCarrierId(serviceType[1]);
+ // setListServiceExpedisi([]);
+ // }
+ // }, [selectedExpedisi]);
+ //
+
const poNumber = useRef(null);
const poFile = useRef(null);
@@ -374,10 +534,15 @@ const Checkout = () => {
}
return;
}
- if (!selectedService) {
- toast.error('Harap pilih tipe layanan pengiriman');
- return;
- }
+ //new release
+ // if (selectedCarrier != 1 && biayaKirim == 0) {
+ // toast.error('Maaf, layanan tidak tersedia. Mohon pilih expedisi lain.');
+ // return;
+ // }
+ // if (!selectedService) {
+ // toast.error('Harap pilih tipe layanan pengiriman');
+ // return;
+ // }
if (selectedCourier != 1 && biayaKirim == 0) {
toast.error('Maaf, layanan tidak tersedia. Mohon pilih expedisi lain.');
return;
@@ -404,11 +569,16 @@ const Checkout = () => {
estimated_arrival_days_start : parseInt(eta_courier_start) + parseInt(productSla),
estimated_arrival_days: parseInt(eta_courier) + parseInt(productSla),
delivery_service_type: selectedService?.service_type,
+ // New release
+ // carrier_id: selectedCarrierId,
+ // estimated_arrival_days: splitDuration(etd),
+ // delivery_service_type: selectedExpedisiService,
flash_sale: hasFlashSale, // dibuat negasi untuk ngetest kebalikan nilai false
voucher: activeVoucher,
voucher_shipping: activeVoucherShipping,
type: 'sale_order',
};
+
if (query) {
data.source = 'buy';
}
@@ -469,6 +639,24 @@ const Checkout = () => {
)}`;
}
}
+
+ /* const midtrans = async () => {
+ for (const product of products) deleteItemCart({ productId: product.id });
+ if (grandTotal > 0) {
+ 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;
+ } else {
+ window.location.href = `${
+ process.env.NEXT_PUBLIC_SELF_HOST
+ }/shop/checkout/success?order_id=${isCheckouted.name.replace(
+ /\//g,
+ '-'
+ )}`;
+ }
+ };*/
};
const handlingActivateCode = async () => {
@@ -642,6 +830,19 @@ const Checkout = () => {
)}
<hr className='mt-8 mb-4 border-gray_r-8' />
+ {/* {!loadingVoucher &&
+ listVouchers?.length === 1 &&
+ listVoucherShippings?.length === 1}
+ {
+ <div className='flex items-center justify-center mt-4 mb-4'>
+ <div className='text-center'>
+ <h1 className='font-bold mb-4'>Tidak ada voucher tersedia</h1>
+ <p className='text-gray-500'>
+ Maaf, saat ini tidak ada voucher yang tersedia.
+ </p>
+ </div>
+ </div>
+ } */}
{listVoucherShippings && listVoucherShippings?.length > 0 && (
<div>
@@ -1027,21 +1228,22 @@ const Checkout = () => {
</Skeleton>
)}
<Divider />
- <SectionValidation address={selectedAddress.invoicing} />
- {/* <SectionExpedisi
- address={selectedAddress.shipping}
- listExpedisi={listExpedisi}
- setSelectedExpedisi={setSelectedExpedisi}
- checkWeigth={checkWeigth}
- checkoutValidation={checkoutValidation}
- expedisiValidation={expedisiValidation}
- loadingRajaOngkir={loadingRajaOngkir}
- />
- <Divider />
- <SectionListService
- listserviceExpedisi={listserviceExpedisi}
- setSelectedServiceType={setSelectedServiceType}
- /> */}
+ <SectionValidation address={selectedAddress.shipping} />
+ {/*new-relase*/}
+ {/*<SectionExpedisi*/}
+ {/* address={selectedAddress.shipping}*/}
+ {/* listExpedisi={listExpedisi}*/}
+ {/* setSelectedExpedisi={setSelectedExpedisi}*/}
+ {/* checkWeigth={checkWeigth}*/}
+ {/* checkoutValidation={checkoutValidation}*/}
+ {/* expedisiValidation={expedisiValidation}*/}
+ {/* loadingRajaOngkir={loadingRajaOngkir}*/}
+ {/* />*/}
+ {/* <Divider />*/}
+ {/* <SectionListService*/}
+ {/* listserviceExpedisi={listserviceExpedisi}*/}
+ {/* setSelectedServiceType={setSelectedServiceType}*/}
+ {/* />*/}
<div className='p-4 flex flex-col gap-y-4'>
{!!products &&
@@ -1123,7 +1325,9 @@ const Checkout = () => {
<div>{currencyFormat(cartCheckout?.subtotal)}</div>
</div>
<div className='flex gap-x-2 justify-between'>
- <div className='text-gray_r-11'>PPN {((PPN - 1) * 100).toFixed(0)}%</div>
+ <div className='text-gray_r-11'>
+ PPN {((PPN - 1) * 100).toFixed(0)}%
+ </div>
<div>{currencyFormat(cartCheckout?.tax)}</div>
</div>
<div className='flex gap-x-2 justify-between'>
@@ -1255,6 +1459,8 @@ const Checkout = () => {
className='flex-1 btn-yellow'
onClick={checkout}
disabled={
+ //new release
+ // isLoading ||
!products ||
products?.length == 0 ||
priceCheck ||
@@ -1328,7 +1534,7 @@ const Checkout = () => {
)}
{products && <SectionExpedition products={products} />}
<Divider />
- <SectionValidation address={selectedAddress.invoicing} />
+ <SectionValidation address={selectedAddress.shipping} />
{/* <SectionExpedisi
address={selectedAddress.shipping}
listExpedisi={listExpedisi}
@@ -1430,7 +1636,9 @@ const Checkout = () => {
<div>{currencyFormat(cartCheckout?.subtotal)}</div>
</div>
<div className='flex gap-x-2 justify-between'>
- <div className='text-gray_r-11'>PPN {((PPN - 1) * 100).toFixed(0)}%</div>
+ <div className='text-gray_r-11'>
+ PPN {((PPN - 1) * 100).toFixed(0)}%
+ </div>
<div>{currencyFormat(cartCheckout?.tax)}</div>
</div>
<div className='flex gap-x-2 justify-between'>
@@ -1560,6 +1768,8 @@ const Checkout = () => {
className='w-full btn-yellow mt-4'
onClick={checkout}
disabled={
+ // new-relase
+ // isLoading ||
!products ||
products?.length == 0 ||
priceCheck ||
@@ -1630,6 +1840,9 @@ const SectionAddress = ({ address, label, url }) => (
);
const SectionValidation = ({ address }) =>
+ //new release
+ // address?.stateId == 0 ||
+ // (address?.rajaongkirCityId == 0 && (
address?.stateId == 0 && (
<BottomPopup active={true} title='Update Alamat'>
<div className='leading-7 text-gray_r-12/80'>
@@ -1644,7 +1857,9 @@ const SectionValidation = ({ address }) =>
</Link>
</div>
</BottomPopup>
- );
+ )
+// )
+;
const SectionExpedisi = ({
address,
diff --git a/src/lib/checkout/components/CheckoutSection.jsx b/src/lib/checkout/components/CheckoutSection.jsx
index c82e15c7..c6be9056 100644
--- a/src/lib/checkout/components/CheckoutSection.jsx
+++ b/src/lib/checkout/components/CheckoutSection.jsx
@@ -33,7 +33,7 @@ export const SectionAddress = ({ address, label, url }) => {
};
export const SectionValidation = ({ address }) =>
- address?.stateId == 0 && (
+ address?.stateId == 0 || address?.rajaongkirCityId == 0 && (
<BottomPopup active={true} title='Update Alamat'>
<div className='leading-7 text-gray_r-12/80'>
Mohon untuk memperbarui alamat Anda dengan mengklik tombol di bawah ini.{' '}
diff --git a/src/lib/checkout/components/FinishCheckout.jsx b/src/lib/checkout/components/FinishCheckout.jsx
index 4a67b252..d533325e 100644
--- a/src/lib/checkout/components/FinishCheckout.jsx
+++ b/src/lib/checkout/components/FinishCheckout.jsx
@@ -53,13 +53,12 @@ const FinishCheckout = ({ query }) => {
height={isMobile ? 300 : 450}
/>
<div className='text-title-sm md:text-title-lg text-center font-semibold'>
- Terima Kasih atas Pembelian Kamu
+ Terima Kasih atas Pembelian di Indoteknik.com
</div>
+ <p className='text-title-sm md:text-title-lg font-semibold my-2'>No. Transaksi: <span className='text-red-500'>{query?.order_id?.replaceAll('-', '/')}</span></p>
<div className='flex flex-col justify-center items-center text-body-2 md:text-body-1 text-center mt-3 px-24 md:px-36 py-4 border-2 gap-y-2 rounded'>
- <p className='font-bold'>No. Transaksi</p>
- <p className='mb-2 font-medium text-red-500 text-xl'>
- {query?.order_id?.replaceAll('-', '/')}
- </p>
+ <p className="text-title-sm md:text-title-xl text-gray-500 mt-1">Estimasi Barang Siap pada Tanggal</p>
+ <p className="text-title-sm md:text-title-xl text-red-500 font-semibold my-2">{data?.expectedReadyToShip}</p>
<Link
href={`/my/quotations/${data?.id}`}
className='btn-solid-red rounded-md text-base'
diff --git a/src/lib/flashSale/api/flashSaleApi.js b/src/lib/flashSale/api/flashSaleApi.js
index 115b07dc..410b720c 100644
--- a/src/lib/flashSale/api/flashSaleApi.js
+++ b/src/lib/flashSale/api/flashSaleApi.js
@@ -1,8 +1,9 @@
import odooApi from '@/core/api/odooApi'
-const flashSaleApi = async () => {
- const flashSale = await odooApi('GET', '/api/v1/flashsale/header')
+const flashSaleApi = async ({isShow = true}) => {
+ const flashSale = await odooApi('GET', '/api/v1/flashsale/header?is_show_program='+isShow)
return flashSale
}
export default flashSaleApi
+ \ No newline at end of file
diff --git a/src/lib/flashSale/components/FlashSaleNonDisplay.jsx b/src/lib/flashSale/components/FlashSaleNonDisplay.jsx
index 4b420fac..adcc7ba0 100644
--- a/src/lib/flashSale/components/FlashSaleNonDisplay.jsx
+++ b/src/lib/flashSale/components/FlashSaleNonDisplay.jsx
@@ -13,14 +13,14 @@ const FlashSaleNonDisplay = () => {
const router = useRouter();
useEffect(() => {
const loadFlashSales = async () => {
- const dataFlashSales = await flashSaleApi();
+ const dataFlashSales = await flashSaleApi({isShow: false});
setFlashSales(dataFlashSales);
setIsLoading(false);
};
loadFlashSales();
}, []);
- const handleSubmit = () => {
- router.push(`/shop/search?penawaran=${flashSales[0]?.pricelistId}`);
+ const handleSubmit = (flashSale) => {
+ router.push(`/shop/search?penawaran=${flashSale?.pricelistId}`);
};
if (isLoading) {
return <FlashSaleSkeleton />;
@@ -33,10 +33,10 @@ const FlashSaleNonDisplay = () => {
<div key={index}>
<div className='flex items-center mb-4 justify-between '>
<div className='font-medium sm:text-h-lg mt-1.5'>
- Penawaran Terbatas
+ {flashSale.name}
</div>
<div
- onClick={handleSubmit}
+ onClick={() => handleSubmit(flashSale)}
className='!text-red-500 font-semibold cursor-pointer'
>
Lihat Semua
@@ -56,7 +56,7 @@ const FlashSaleProduct = ({ flashSaleId }) => {
useEffect(() => {
const loadProducts = async () => {
const dataProducts = await productSearchApi({
- query: `fq=-flashsale_id_i:${flashSaleId}&fq=flashsale_price_f:[1 TO *]&limit=25&orderBy=flashsale-discount-desc&source=similar`,
+ query: `fq=flashsale_id_i:${flashSaleId}&fq=flashsale_price_f:[1 TO *]&limit=25&orderBy=flashsale-discount-desc&source=similar`,
operation: 'AND',
});
setProducts(dataProducts.response);
diff --git a/src/lib/invoice/components/Invoice.jsx b/src/lib/invoice/components/Invoice.jsx
index 15bfa746..a26b231f 100644
--- a/src/lib/invoice/components/Invoice.jsx
+++ b/src/lib/invoice/components/Invoice.jsx
@@ -1,60 +1,66 @@
-import Spinner from '@/core/components/elements/Spinner/Spinner'
-import useInvoice from '../hooks/useInvoice'
-import { downloadInvoice, downloadTaxInvoice } from '../utils/invoices'
-import Divider from '@/core/components/elements/Divider/Divider'
-import VariantGroupCard from '@/lib/variant/components/VariantGroupCard'
-import currencyFormat from '@/core/utils/currencyFormat'
-import MobileView from '@/core/components/views/MobileView'
-import DesktopView from '@/core/components/views/DesktopView'
-import Menu from '@/lib/auth/components/Menu'
-import Link from '@/core/components/elements/Link/Link'
-import Image from '@/core/components/elements/Image/Image'
-import { createSlug } from '@/core/utils/slug'
-import { useEffect, useState } from 'react'
+import Spinner from '@/core/components/elements/Spinner/Spinner';
+import useInvoice from '../hooks/useInvoice';
+import { downloadInvoice, downloadTaxInvoice } from '../utils/invoices';
+import Divider from '@/core/components/elements/Divider/Divider';
+import VariantGroupCard from '@/lib/variant/components/VariantGroupCard';
+import currencyFormat from '@/core/utils/currencyFormat';
+import MobileView from '@/core/components/views/MobileView';
+import DesktopView from '@/core/components/views/DesktopView';
+import Menu from '@/lib/auth/components/Menu';
+import Link from '@/core/components/elements/Link/Link';
+import Image from '@/core/components/elements/Image/Image';
+import { createSlug } from '@/core/utils/slug';
+import { useEffect, useState } from 'react';
const Invoice = ({ id }) => {
- const PPN = process.env.NEXT_PUBLIC_PPN
- const { invoice } = useInvoice({ id })
+ const PPN = process.env.NEXT_PUBLIC_PPN;
+ const { invoice } = useInvoice({ id });
- const [totalAmount, setTotalAmount] = useState(0)
- const [totalDiscountAmount, setTotalDiscountAmount] = useState(0)
+ const [totalAmount, setTotalAmount] = useState(0);
+ const [totalDiscountAmount, setTotalDiscountAmount] = useState(0);
+
+ const amountBeforePPN = invoice.data?.amountTotal / PPN;
+ const taxAmount = invoice.data?.amountTotal - amountBeforePPN;
useEffect(() => {
if (invoice?.data?.products) {
- let calculateTotalAmount = 0
- let calculateTotalDiscountAmount = 0
+ let calculateTotalAmount = 0;
+ let calculateTotalDiscountAmount = 0;
invoice.data.products.forEach((product) => {
- calculateTotalAmount += product.price.price * product.quantity
+ calculateTotalAmount += product.price.price * product.quantity;
calculateTotalDiscountAmount +=
- (product.price.price - product.price.priceDiscount) * product.quantity
- })
- setTotalAmount(calculateTotalAmount)
- setTotalDiscountAmount(calculateTotalDiscountAmount)
+ (product.price.price - product.price.priceDiscount) *
+ product.quantity;
+ });
+ setTotalAmount(calculateTotalAmount);
+ setTotalDiscountAmount(calculateTotalDiscountAmount);
}
- }, [invoice])
+ }, [invoice]);
if (invoice.isLoading) {
return (
<div className='flex justify-center my-6'>
<Spinner className='w-6 text-gray_r-12/50 fill-gray_r-12' />
</div>
- )
+ );
}
- const address = invoice.data?.customer
- let fullAddress = []
- if (address?.street) fullAddress.push(address.street)
- if (address?.subDistrict?.name) fullAddress.push(address.subDistrict.name)
- if (address?.district?.name) fullAddress.push(address.district.name)
- if (address?.city?.name) fullAddress.push(address.city.name)
- fullAddress = fullAddress.join(', ')
+ const address = invoice.data?.customer;
+ let fullAddress = [];
+ if (address?.street) fullAddress.push(address.street);
+ if (address?.subDistrict?.name) fullAddress.push(address.subDistrict.name);
+ if (address?.district?.name) fullAddress.push(address.district.name);
+ if (address?.city?.name) fullAddress.push(address.city.name);
+ fullAddress = fullAddress.join(', ');
return (
invoice.data?.name && (
<>
<MobileView>
<div className='flex flex-col gap-y-4 p-4'>
- <DescriptionRow label='No Invoice'>{invoice.data?.name}</DescriptionRow>
+ <DescriptionRow label='No Invoice'>
+ {invoice.data?.name}
+ </DescriptionRow>
<DescriptionRow label='Status Transaksi'>
{invoice.data?.amountResidual > 0 ? (
<span className='badge-solid-red'>Belum Lunas</span>
@@ -68,13 +74,18 @@ const Invoice = ({ id }) => {
<DescriptionRow label='Ketentuan Pembayaran'>
{invoice.data?.paymentTerm}
</DescriptionRow>
- {invoice.data?.amountResidual > 0 && invoice.invoiceDate != invoice.invoiceDateDue && (
- <DescriptionRow label='Tanggal Jatuh Tempo'>
- {invoice.data?.invoiceDateDue}
- </DescriptionRow>
- )}
- <DescriptionRow label='Nama Sales'>{invoice.data?.sales}</DescriptionRow>
- <DescriptionRow label='Tanggal Invoice'>{invoice.data?.invoiceDate}</DescriptionRow>
+ {invoice.data?.amountResidual > 0 &&
+ invoice.invoiceDate != invoice.invoiceDateDue && (
+ <DescriptionRow label='Tanggal Jatuh Tempo'>
+ {invoice.data?.invoiceDateDue}
+ </DescriptionRow>
+ )}
+ <DescriptionRow label='Nama Sales'>
+ {invoice.data?.sales}
+ </DescriptionRow>
+ <DescriptionRow label='Tanggal Invoice'>
+ {invoice.data?.invoiceDate}
+ </DescriptionRow>
<div className='flex items-center'>
<p className='text-gray_r-11 leading-none'>Invoice</p>
<button
@@ -104,8 +115,12 @@ const Invoice = ({ id }) => {
<div className='flex flex-col gap-y-4 p-4 border-t border-gray_r-6'>
<DescriptionRow label='Nama'>{address?.name}</DescriptionRow>
- <DescriptionRow label='Email'>{address?.email || '-'}</DescriptionRow>
- <DescriptionRow label='No Telepon'>{address?.mobile || '-'}</DescriptionRow>
+ <DescriptionRow label='Email'>
+ {address?.email || '-'}
+ </DescriptionRow>
+ <DescriptionRow label='No Telepon'>
+ {address?.mobile || '-'}
+ </DescriptionRow>
<DescriptionRow label='Alamat'>{fullAddress}</DescriptionRow>
</div>
@@ -128,10 +143,14 @@ const Invoice = ({ id }) => {
<Menu />
</div>
<div className='w-9/12 p-4 bg-white border border-gray_r-6 rounded'>
- <h1 className='text-title-sm font-semibold mb-6'>Invoice & Faktur Pajak</h1>
+ <h1 className='text-title-sm font-semibold mb-6'>
+ Invoice & Faktur Pajak
+ </h1>
<div className='flex items-center gap-x-2 mb-3'>
- <span className='text-h-sm font-medium'>{invoice?.data?.name}</span>
+ <span className='text-h-sm font-medium'>
+ {invoice?.data?.name}
+ </span>
{invoice?.data?.amountResidual > 0 ? (
<div className='badge-solid-red h-fit'>Belum Lunas</div>
) : (
@@ -180,14 +199,16 @@ const Invoice = ({ id }) => {
</div>
</div>
- <div className='text-h-sm font-semibold mt-6 mb-4'>Rincian Pembelian</div>
+ <div className='text-h-sm font-semibold mt-6 mb-4'>
+ Rincian Pembelian
+ </div>
<table className='table-data'>
<thead>
<tr>
<th>Nama Produk</th>
<th>Jumlah</th>
<th>Harga</th>
- <th>Diskon</th>
+ {/* <th>Diskon</th> */}
<th>Subtotal</th>
</tr>
</thead>
@@ -229,13 +250,22 @@ const Invoice = ({ id }) => {
</div>
</td>
<td>{product.quantity}</td>
- <td>{currencyFormat(product.price.price)}</td>
<td>
+ {currencyFormat(
+ product.price.priceDiscount - taxAmount
+ )}
+ </td>
+ {/* <td>
{product.price.discountPercentage > 0
? `${product.price.discountPercentage}%`
: ''}
+ </td> */}
+ <td>
+ {currencyFormat(
+ product.price.priceDiscount * product.quantity -
+ taxAmount
+ )}
</td>
- <td>{currencyFormat(product.price.priceDiscount * product.quantity)}</td>
</tr>
))}
</tbody>
@@ -244,20 +274,30 @@ const Invoice = ({ id }) => {
<div className='flex justify-end mt-4'>
<div className='w-1/4 grid grid-cols-2 gap-y-2 text-gray_r-12/80'>
<div className='text-right'>Subtotal</div>
- <div className='text-right font-medium'>{currencyFormat(totalAmount)}</div>
+ <div className='text-right font-medium'>
+ {currencyFormat(
+ totalAmount - totalDiscountAmount - taxAmount
+ )}
+ </div>
- <div className='text-right'>Total Diskon</div>
+ {/* <div className='text-right'>Total Diskon</div>
<div className='text-right font-medium'>
- {currencyFormat(-totalDiscountAmount)}
+ {currencyFormat(totalDiscountAmount)}
+ </div> */}
+ <div className='text-right'>
+ PPN {((PPN - 1) * 100).toFixed(0)}%
+ </div>
+ <div className='text-right font-medium'>
+ {currencyFormat(
+ invoice.data?.amountTotal -
+ invoice.data?.amountTotal / PPN
+ )}
</div>
<div className='text-right'>Grand Total</div>
<div className='text-right font-medium text-gray_r-12'>
{currencyFormat(invoice.data?.amountTotal)}
</div>
-
- <div className='text-right'>PPN {((PPN - 1) * 100).toFixed(0)}% (Incl.)</div>
- <div className='text-right font-medium'>{currencyFormat(invoice.data?.amountTotal - totalAmount)}</div>
</div>
</div>
</div>
@@ -265,14 +305,14 @@ const Invoice = ({ id }) => {
</DesktopView>
</>
)
- )
-}
+ );
+};
const DescriptionRow = ({ children, label }) => (
<div className='grid grid-cols-2'>
<span className='text-gray_r-11'>{label}</span>
<span className='text-right'>{children}</span>
</div>
-)
+);
-export default Invoice
+export default Invoice;
diff --git a/src/lib/merchant/api/createMerchantApi.js b/src/lib/merchant/api/createMerchantApi.js
new file mode 100644
index 00000000..878ab7c0
--- /dev/null
+++ b/src/lib/merchant/api/createMerchantApi.js
@@ -0,0 +1,14 @@
+// import odooApi from '@/core/api/odooApi';
+// import { getAuth } from '@/core/utils/auth';
+
+// const createMerchantApi = async ({ data }) => {
+// const auth = getAuth();
+// const lead = await odooApi(
+// 'POST',
+// `/api/v1/merchant/${auth.partnerId}`,
+// data
+// );
+// return lead;
+// };
+
+// export default createMerchantApi;
diff --git a/src/lib/merchant/api/getMerchantApi.js b/src/lib/merchant/api/getMerchantApi.js
new file mode 100644
index 00000000..be27e947
--- /dev/null
+++ b/src/lib/merchant/api/getMerchantApi.js
@@ -0,0 +1,13 @@
+// import odooApi from '@/core/api/odooApi';
+// import { getAuth } from '@/core/utils/auth';
+
+// const createMerchantApi = async () => {
+// const auth = getAuth();
+// const lead = await odooApi(
+// 'GET',
+// `/api/v1/detail-merchant/${auth.partnerId}`
+// );
+// return lead;
+// };
+
+// export default createMerchantApi;
diff --git a/src/lib/merchant/api/getMerchantProgresApi.js b/src/lib/merchant/api/getMerchantProgresApi.js
new file mode 100644
index 00000000..3bec15fe
--- /dev/null
+++ b/src/lib/merchant/api/getMerchantProgresApi.js
@@ -0,0 +1,10 @@
+// import odooApi from '@/core/api/odooApi';
+// import { getAuth } from '@/core/utils/auth';
+
+// const createMerchantApi = async () => {
+// const auth = getAuth();
+// const lead = await odooApi('GET', `/api/v1/check-merchant/${auth.partnerId}`);
+// return lead;
+// };
+
+// export default createMerchantApi;
diff --git a/src/lib/merchant/components/AccountSwitch.jsx b/src/lib/merchant/components/AccountSwitch.jsx
new file mode 100644
index 00000000..abbdcc92
--- /dev/null
+++ b/src/lib/merchant/components/AccountSwitch.jsx
@@ -0,0 +1,60 @@
+// import Link from 'next/link';
+// import Image from '~/components/ui/image';
+// import whatsappUrl from '@/core/utils/whatsappUrl';
+// import { useEffect, useState } from 'react';
+// import odooApi from '@/core/api/odooApi';
+// import useDevice from '@/core/hooks/useDevice';
+// import useAuth from '@/core/hooks/useAuth';
+// import axios from 'axios';
+// import { toast } from 'react-hot-toast';
+// import { ChevronRightIcon, ChevronLeftIcon } from '@heroicons/react/24/outline';
+
+// const FinishTempo = ({ query }) => {
+// const [data, setData] = useState();
+// const [transactionData, setTransactionData] = useState();
+// const { isDesktop, isMobile } = useDevice();
+// const auth = useAuth();
+
+// return (
+// <div className='container flex flex-col items-center gap-4'>
+// <div
+// className={`flex ${
+// isMobile ? 'w-full' : 'w-2/3'
+// } justify-center items-center`}
+// >
+// <h1
+// className={`text-red-500 text-center py-4 font-semibold ${
+// isMobile ? 'text-lg' : 'text-3xl'
+// }`}
+// >
+// Form Merchant Kamu Gagal Dilakukan
+// </h1>
+// </div>
+// <Image
+// src='/images/ICON_TEMPO.png'
+// alt='Registrasi Tempo'
+// width={isMobile ? 300 : 550}
+// height={isMobile ? 300 : 550}
+// />
+
+// <div
+// className={`mt-2 text-center opacity-75 leading-6 p-4 md:p-0 ${
+// isMobile ? 'w-full text-sm' : 'w-4/5 text-base'
+// }`}
+// >
+// Terima kasih atas minat anda untuk mendaftar merchant, namun sayangnya
+// akun anda bukan merupakan akun bisnis. Segera ubah akun anda menjadi
+// Bisnis untuk menggunakan fitur ini
+// </div>
+// <Link
+// href={'/my/profile'}
+// className='btn-solid-red rounded-md text-base flex flex-row items-center justify-center'
+// >
+// Ubah Akun
+// <ChevronRightIcon className='w-5' />
+// </Link>
+// </div>
+// );
+// };
+
+// export default FinishTempo;
diff --git a/src/lib/merchant/components/Dokumen.jsx b/src/lib/merchant/components/Dokumen.jsx
new file mode 100644
index 00000000..42a9426b
--- /dev/null
+++ b/src/lib/merchant/components/Dokumen.jsx
@@ -0,0 +1,1252 @@
+// import HookFormSelect from '@/core/components/elements/Select/HookFormSelect';
+// import cityApi from '@/lib/address/api/cityApi';
+// import stateApi from '@/lib/address/api/stateApi.js';
+// import districtApi from '@/lib/address/api/districtApi';
+// import subDistrictApi from '@/lib/address/api/subDistrictApi';
+// import { yupResolver } from '@hookform/resolvers/yup';
+// import React, {
+// useEffect,
+// useRef,
+// useState,
+// forwardRef,
+// useImperativeHandle,
+// } from 'react';
+// import ReCAPTCHA from 'react-google-recaptcha';
+// import { Controller, useForm } from 'react-hook-form';
+// import { toast } from 'react-hot-toast';
+// import * as Yup from 'yup';
+// import createMerchantApi from '../api/createMerchantApi';
+// import getMerchantApi from '../api/getMerchantApi';
+// import addressApi from '@/lib/address/api/addressApi';
+// import PageContent from '@/lib/content/components/PageContent';
+// import { useRouter } from 'next/router';
+// import useAuth from '@/core/hooks/useAuth';
+// import { Radio, RadioGroup, Stack, Divider, Button } from '@chakra-ui/react';
+// import { EyeIcon } from '@heroicons/react/24/outline';
+// import BottomPopup from '@/core/components/elements/Popup/BottomPopup';
+// import Image from 'next/image';
+// import ImageBanner from '~/components/ui/image';
+// import { ChevronRightIcon } from '@heroicons/react/24/outline';
+// import MobileView from '@/core/components/views/MobileView';
+// import DesktopView from '@/core/components/views/DesktopView';
+// import getFileBase64 from '@/core/utils/getFileBase64';
+// import odooApi from '~/libs/odooApi';
+// const Dokumen = forwardRef(
+// ({ handleIsError, isKonfirmasi, buttonSubmitClick }, ref) => {
+// const isError = (value) => {
+// // Logika menentukan error
+// const result = value ? true : false;
+// handleIsError(result); // Panggil handleIsError dari Merchant
+// return result;
+// };
+// const {
+// register,
+// handleSubmit,
+// formState: { errors },
+// control,
+// reset,
+// watch,
+// setValue,
+// getValues,
+// } = useForm({
+// resolver: yupResolver(validationSchema),
+// defaultValues,
+// });
+
+// const list_unit = [
+// {
+// value: 'Manufacturing',
+// label: 'Manufacturing',
+// },
+// {
+// value: 'Hospitality',
+// label: 'Hospitality',
+// },
+// {
+// value: 'Automotive',
+// label: 'Automotive',
+// },
+// {
+// value: 'Retail',
+// label: 'Retail',
+// },
+// {
+// value: 'Maining',
+// label: 'Maining',
+// },
+// {
+// value: 'Lain - Lain',
+// label: 'Lain - Lain',
+// },
+// ];
+// const [fileNames, setFileNames] = useState({});
+// const [DeatailFile, setDetailFile] = useState({});
+// const [isExample, setIsExample] = useState(false);
+// const [isPkp, setIsPkp] = useState(false);
+
+// const npwpRef = useRef(null);
+// const sppkpRef = useRef(null);
+// const ktpDirutRef = useRef(null);
+// const kartuNamaRef = useRef(null);
+// const suratPernyataanRef = useRef(null);
+// const fotoKantorRef = useRef(null);
+// const dataProdukRef = useRef(null);
+// const pricelistRef = useRef(null);
+// const router = useRouter();
+
+// const auth = useAuth();
+// if (auth == false) {
+// router.push(`/login?next=${encodeURIComponent('/daftar-merchant')}`);
+// }
+// useEffect(() => {
+// const loadData = async () => {
+// try {
+// const data = await getMerchantApi();
+// if (data) {
+// setFileNames((prev) => ({
+// ...prev,
+// ['npwp']: data.fileNpwp ? data.fileNpwp.name : '',
+// ['sppkp']: data.fileSppkp ? data.fileSppkp.name : '',
+// ['dokumenKtpDirut']: data.fileDokumenKtpDirut
+// ? data.fileDokumenKtpDirut.name
+// : '',
+// ['kartuNama']: data.fileKartuNama ? data.fileKartuNama.name : '',
+// ['suratPernyataan']: data.fileSuratPernyataan
+// ? data.fileSuratPernyataan.name
+// : '',
+// ['fotoKantor']: data.fileFotoKantor
+// ? data.fileFotoKantor.name
+// : '',
+// ['dataProduk']: data.fileDataProduk
+// ? data.fileDataProduk.name
+// : '',
+// ['pricelist']: data.filePricelist ? data.filePricelist.name : '',
+// }));
+// }
+// } catch (error) {
+// console.error('Error loading profile:', error);
+// handleIsError(true); // Jika ada error, panggil fungsi error handler
+// }
+// };
+
+// loadData();
+// }, [reset, handleIsError]);
+
+// useEffect(() => {
+// if (!isKonfirmasi) {
+// window.scrollTo({
+// top: 0,
+// behavior: 'smooth',
+// });
+// }
+// }, []);
+// useImperativeHandle(ref, () => () => {
+// handleSubmit(onSubmitHandler)();
+// });
+// useEffect(() => {
+// const loadProfile = async () => {
+// try {
+// const dataProfile = await addressApi({
+// id: auth.parentId ? auth.parentId : auth.partnerId,
+// });
+// if (dataProfile.companyType == 'pkp') {
+// setIsPkp(true);
+// }
+// setValue('company', dataProfile?.name);
+// setValue('address', dataProfile?.alamatBisnis);
+// setValue('state', parseInt(dataProfile.stateId.id));
+// setValue('city', parseInt(dataProfile.city.id));
+// setValue('district', parseInt(dataProfile.district.id));
+// setValue('subDistrict', parseInt(dataProfile.subDistrict.id));
+// setValue('zip', parseInt(dataProfile.zip));
+// } catch (error) {
+// console.error('Error loading profile:', error);
+// }
+// };
+
+// loadProfile();
+// }, [auth?.parentId]);
+
+// const onSubmitHandler = async (values) => {
+// const options = {
+// behavior: 'smooth',
+// block: 'center',
+// };
+// const npwp = { name: fileNames.npwp, format: DeatailFile.npwp };
+// const sppkp = { name: fileNames.sppkp, format: DeatailFile.sppkp };
+// const dokumenKtpDirut = {
+// name: fileNames.dokumenKtpDirut,
+// format: DeatailFile.dokumenKtpDirut,
+// };
+// const kartuNama = {
+// name: fileNames.kartuNama,
+// format: DeatailFile.kartuNama,
+// };
+// const suratPernyataan = {
+// name: fileNames.suratPernyataan,
+// format: DeatailFile.suratPernyataan,
+// };
+// const fotoKantor = {
+// name: fileNames.fotoKantor,
+// format: DeatailFile.fotoKantor,
+// };
+// const dataProduk = {
+// name: fileNames.dataProduk,
+// format: DeatailFile.dataProduk,
+// };
+// const pricelist = {
+// name: fileNames.pricelist,
+// format: DeatailFile.pricelist,
+// };
+
+// if (!npwp.name && isPkp) {
+// if (npwpRef?.current) {
+// npwpRef.current.scrollIntoView(options);
+// }
+// toast.error('NPWP wajib di tambahkan');
+// return;
+// }
+// if (!sppkp.name && isPkp) {
+// toast.error('SPPKP wajib di tambahkan');
+// if (sppkpRef?.current) {
+// sppkpRef.current.scrollIntoView(options);
+// }
+// return;
+// }
+// if (!dokumenKtpDirut.name && !isPkp) {
+// toast.error('KTP Dirut/Direktur wajib di tambahkan');
+// if (ktpDirutRef?.current) {
+// ktpDirutRef.current.scrollIntoView(options);
+// }
+// return;
+// }
+// if (!fotoKantor.name) {
+// toast.error('Foto Gudang / Kantor Bagian Depan wajib di tambahkan');
+// if (fotoKantorRef?.current) {
+// fotoKantorRef.current.scrollIntoView(options);
+// }
+// return;
+// }
+// if (!pricelist.name) {
+// toast.error('Pricelist wajib di tambahkan');
+// if (pricelistRef?.current) {
+// pricelistRef.current.scrollIntoView(options);
+// }
+// return;
+// }
+// const toastId = toast.loading('Mengirimkan formulir merchant...');
+// const dokumen = {
+// file_npwp: { details: npwp.format ? npwp : '' },
+// file_sppkp: { details: sppkp.format ? sppkp : '' },
+// file_dokumenKtpDirut: {
+// details: dokumenKtpDirut.format ? dokumenKtpDirut : '',
+// },
+// file_kartuNama: { details: kartuNama.format ? kartuNama : '' },
+// file_suratPernyataan: {
+// details: suratPernyataan.format ? suratPernyataan : '',
+// },
+// file_fotoKantor: { details: fotoKantor.format ? fotoKantor : '' },
+// file_dataProduk: { details: dataProduk.format ? dataProduk : '' },
+// file_pricelist: { details: pricelist.format ? pricelist : '' },
+// };
+// let data = {
+// file_dokumen: JSON.stringify(dokumen),
+// };
+// const create_leads = await createMerchantApi({ data });
+// if (create_leads) {
+// toast.dismiss(toastId);
+// toast.success('Berhasil menambahkan data');
+// isError(false);
+// reset();
+// } else {
+// toast.dismiss(toastId);
+// toast.error('Gagal menambahkan data');
+// }
+// };
+
+// if (!auth) {
+// return;
+// }
+
+// const handleFileChange = async (event) => {
+// let fileBase64 = '';
+// const file = event.target.files[0];
+
+// if (file.size > 2000000) {
+// // try {
+// // const toastId = toast.loading('mencoba mengompresi file...');
+// // // Compress image file
+// // const options = {
+// // maxSizeMB: 0.5, // Target size in MB
+// // maxWidthOrHeight: 1920, // Adjust as needed
+// // useWebWorker: true,
+// // };
+// // const compressedFile = await imageCompression(file, options);
+// // toast.success('berhasil mengompresi file', { duration: 4000 });
+// // // Convert compressed file to Base64
+// // fileBase64 = await getFileBase64(compressedFile);
+// // } catch (error) {
+// // toast.error('Gagal mengompresi file', { duration: 4000 });
+// // }
+// toast.error('Maks file size 2MB', { duration: 4000 });
+// } else {
+// // Convert file to Base64
+// fileBase64 = await getFileBase64(file);
+// }
+// const fieldName = event.target.name; // Nama input file
+// setDetailFile((prev) => ({
+// ...prev,
+// [fieldName]: file ? fileBase64 : '', // Tambahkan atau perbarui file di state
+// }));
+// setFileNames((prev) => ({
+// ...prev,
+// [fieldName]: file ? file.name : '', // Tambahkan atau perbarui file di state
+// }));
+// };
+// return (
+// <>
+// <BottomPopup
+// className=''
+// title='Contoh SPPKP'
+// active={isExample}
+// close={() => setIsExample(false)}
+// >
+// <div className='flex p-2'>
+// <Image
+// src='/images/NO-SPPKP-FORMAT-TEMPLATE.jpg'
+// alt='Contoh SPPKP'
+// className='w-full h-full '
+// width={800}
+// height={800}
+// quality={85}
+// />
+// </div>
+// </BottomPopup>
+// <DesktopView>
+// <div className='container flex flex-col items-star py-4 '>
+// <h2 className='text-xs md:text-title-sm font-semibold mb-6'>
+// Dokumen
+// </h2>
+
+// <div className='w-full mt-4'>
+// <form
+// onSubmit={handleSubmit(onSubmitHandler)}
+// className='flex flex-col gap-4'
+// >
+// <div className='w-full flex flex-row'>
+// <div className='w-2/5'>
+// <label className='form-label text-nowrap'>
+// NPWP{' '}
+// {!isPkp && (
+// <span className=' opacity-60'>(Opsional)</span>
+// )}
+// </label>
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// Pastikan dokumen yang anda upload sudah benar
+// </span>
+// )}
+// </div>
+// <div
+// className={`w-3/5 flex flex-col justify-between ${
+// isKonfirmasi ? 'items-end' : 'items-start'
+// }`}
+// ref={npwpRef}
+// >
+// <div
+// className={`flex flex-row items-start gap-2 w-full ${
+// isKonfirmasi ? 'justify-end' : 'justify-start'
+// }`}
+// >
+// {isKonfirmasi && (
+// <span className='mt-2 text-gray-600 line-clamp-2'>
+// {fileNames.npwp}
+// </span>
+// )}
+// <label
+// htmlFor='npwp'
+// className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+// >
+// {fileNames.npwp ? 'Ubah Dokumen' : 'Upload Dokumen'}
+// </label>
+// <input
+// {...register('npwp')}
+// type='file'
+// className='form-input hidden'
+// accept='.pdf,.png,.jpg,.jpeg'
+// id='npwp'
+// onChange={(e) => {
+// handleFileChange(e); // Untuk update UI (opsional)
+// }}
+// aria-invalid={errors.npwp?.message}
+// />
+// {!isKonfirmasi && (
+// <span className='mt-2 text-gray-600 line-clamp-2'>
+// {fileNames.npwp}
+// </span>
+// )}
+// </div>
+// {!isKonfirmasi && (
+// <span className='text-xs opacity-60 text-red-500'>
+// Format: pdf, jpeg, jpg, png. max file size 2MB
+// </span>
+// )}
+
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.npwp?.message}
+// </div>
+// </div>
+// </div>
+// <div
+// className={`w-full flex flex-row items-start ${
+// isKonfirmasi && 'gap-2'
+// } `}
+// >
+// <div className='w-2/5 flex flex-row justify-between items-center '>
+// <div>
+// <label className='form-label text-nowrap' ref={sppkpRef}>
+// SPPKP{' '}
+// {!isPkp && (
+// <span className=' opacity-60'>(Opsional)</span>
+// )}
+// </label>
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// Pastikan dokumen yang anda upload sudah benar
+// </span>
+// )}
+// </div>
+// <div
+// onClick={() => setIsExample(!isExample)}
+// className={`h-fit ${
+// !isKonfirmasi && 'mr-8'
+// } rounded text-white p-2 flex flex-row items-center bg-red-500 hover:cursor-pointer hover:bg-red-400`}
+// >
+// <EyeIcon
+// className={`${isKonfirmasi ? 'w-4' : 'w-4 mr-2'} `}
+// />
+// {!isKonfirmasi && (
+// <p className='font-light text-xs '>Lihat Contoh</p>
+// )}
+// </div>
+// </div>
+// <div
+// className={`w-3/5 flex flex-col justify-between ${
+// isKonfirmasi ? 'items-end' : 'items-start'
+// }`}
+// >
+// <div className='flex flex-row items-start justify-start gap-2'>
+// {isKonfirmasi && (
+// <span className='mt-2 text-gray-600 line-clamp-2 '>
+// {fileNames.sppkp}
+// </span>
+// )}
+// <label
+// htmlFor='sppkp'
+// className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+// >
+// {fileNames.sppkp ? 'Ubah Dokumen' : 'Upload Dokumen'}
+// </label>
+// <input
+// {...register('sppkp')}
+// type='file'
+// className='form-input hidden'
+// accept='.pdf,.png,.jpg,.jpeg'
+// id='sppkp'
+// onChange={handleFileChange}
+// aria-invalid={errors.sppkp?.message}
+// />
+// </div>
+// {!isKonfirmasi && (
+// <span className='text-xs opacity-60 text-red-500'>
+// Format: pdf, jpeg, jpg, png. max file size 2MB
+// </span>
+// )}
+
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.sppkp?.message}
+// </div>
+// </div>
+// </div>
+
+// <div className='w-full flex flex-row'>
+// <div className='w-2/5'>
+// <label className='form-label text-nowrap'>
+// KTP Dirut/Direktur{' '}
+// {isPkp && <span className=' opacity-60'>(Opsional)</span>}
+// </label>
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// Pastikan dokumen yang anda upload sudah benar
+// </span>
+// )}
+// </div>
+// <div
+// className={`w-3/5 flex flex-col justify-between ${
+// isKonfirmasi ? 'items-end' : 'items-start'
+// }`}
+// ref={ktpDirutRef}
+// >
+// <div
+// className={`flex flex-row items-start ${
+// isKonfirmasi ? 'justify-end' : 'justify-start'
+// } gap-2 w-full`}
+// >
+// {isKonfirmasi && (
+// <span className='mt-2 text-gray-600 line-clamp-2'>
+// {fileNames.dokumenKtpDirut}
+// </span>
+// )}
+// <label
+// htmlFor='dokumenKtpDirut'
+// className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+// >
+// {fileNames.dokumenKtpDirut
+// ? 'Ubah Dokumen'
+// : 'Upload Dokumen'}
+// </label>
+// <input
+// {...register('dokumenKtpDirut')}
+// type='file'
+// className='form-input hidden'
+// accept='.pdf,.png,.jpg,.jpeg'
+// id='dokumenKtpDirut'
+// onChange={handleFileChange}
+// aria-invalid={errors.dokumenKtpDirut?.message}
+// />
+// {!isKonfirmasi && (
+// <span className='mt-2 text-gray-600 line-clamp-2'>
+// {fileNames.dokumenKtpDirut}
+// </span>
+// )}
+// </div>
+// {!isKonfirmasi && (
+// <span className='text-xs opacity-60 text-red-500'>
+// Format: pdf, jpeg, jpg, png. max file size 2MB
+// </span>
+// )}
+
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.dokumenKtpDirut?.message}
+// </div>
+// </div>
+// </div>
+
+// <div className='w-full flex flex-row'>
+// <div className='w-2/5'>
+// <label
+// className='form-label text-nowrap'
+// ref={kartuNamaRef}
+// >
+// Kartu Nama <span className=' opacity-60'>(Opsional)</span>{' '}
+// </label>
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// Pastikan dokumen yang anda upload sudah benar
+// </span>
+// )}
+// </div>
+// <div
+// className={`w-3/5 flex flex-col justify-between ${
+// isKonfirmasi ? 'items-end' : 'items-start'
+// }`}
+// >
+// <div className='flex flex-row items-start justify-start gap-2'>
+// {isKonfirmasi && (
+// <span className='mt-2 text-gray-600 line-clamp-2'>
+// {fileNames.kartuNama}
+// </span>
+// )}
+// <label
+// htmlFor='kartuNama'
+// className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+// >
+// {fileNames.kartuNama
+// ? 'Ubah Dokumen'
+// : 'Upload Dokumen'}
+// </label>
+// <input
+// {...register('kartuNama')}
+// type='file'
+// className='form-input hidden'
+// accept='.pdf,.png,.jpg,.jpeg'
+// id='kartuNama'
+// onChange={handleFileChange}
+// aria-invalid={errors.kartuNama?.message}
+// />
+// {!isKonfirmasi && (
+// <span className='mt-2 text-gray-600 line-clamp-2'>
+// {fileNames.kartuNama}
+// </span>
+// )}
+// </div>
+// {!isKonfirmasi && (
+// <span className='text-xs opacity-60 text-red-500'>
+// Format: pdf, jpeg, jpg, png. max file size 2MB
+// </span>
+// )}
+
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.kartuNama?.message}
+// </div>
+// </div>
+// </div>
+
+// <div className='w-full flex flex-row items-start '>
+// <div className='w-2/5 flex flex-row justify-between items-center '>
+// <div>
+// <label
+// className={`form-label ${
+// isKonfirmasi ? 'text-wrap' : ' text-nowrap'
+// }`}
+// ref={suratPernyataanRef}
+// >
+// Surat Pernyataan Nomor Rekening{' '}
+// <span className=' opacity-60'>(Opsional)</span>
+// </label>
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// Wajib diisi jika nomor rekening berbeda dengan nama
+// perusahaan
+// </span>
+// )}
+// </div>
+// <a
+// href='/file/Surat Pernyataan Nomor Rekening.docx'
+// download='Surat Pernyataan Nomor Rekening.docx'
+// className='h-fit mr-8 rounded text-white p-2 flex flex-row items-center bg-red-500 hover:cursor-pointer hover:bg-red-400'
+// >
+// <p className='font-light text-xs'>Download Template</p>
+// </a>
+// </div>
+// <div
+// className={`w-3/5 flex flex-col justify-between ${
+// isKonfirmasi ? 'items-end' : 'items-start'
+// }`}
+// >
+// <div className='flex flex-row items-start justify-start gap-2'>
+// {isKonfirmasi && (
+// <span className='mt-2 text-gray-600 line-clamp-2'>
+// {fileNames.suratPernyataan}
+// </span>
+// )}
+// <label
+// htmlFor='suratPernyataan'
+// className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+// >
+// {fileNames.suratPernyataan
+// ? 'Ubah Dokumen'
+// : 'Upload Dokumen'}
+// </label>
+// <input
+// {...register('suratPernyataan')}
+// type='file'
+// className='form-input hidden'
+// accept='.pdf,.png,.jpg,.jpeg'
+// id='suratPernyataan'
+// onChange={handleFileChange}
+// aria-invalid={errors.suratPernyataan?.message}
+// />
+// {!isKonfirmasi && (
+// <span className='mt-2 text-gray-600 line-clamp-2'>
+// {fileNames.suratPernyataan}
+// </span>
+// )}
+// </div>
+// {!isKonfirmasi && (
+// <span className='text-xs opacity-60 text-red-500'>
+// Format: pdf, jpeg, jpg, png. max file size 2MB
+// </span>
+// )}
+
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.suratPernyataan?.message}
+// </div>
+// </div>
+// </div>
+// <div className='w-full flex flex-row items-start '>
+// <div className='w-2/5 flex flex-col justify-start items-start '>
+// <label
+// className={`form-label ${
+// isKonfirmasi ? 'text-wrap' : 'text-nowrap'
+// }`}
+// ref={fotoKantorRef}
+// >
+// Foto Gudang / Kantor Bagian Depan
+// </label>
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// Pastikan dokumen yang anda upload sudah benar
+// </span>
+// )}
+// </div>
+// <div
+// className={`w-3/5 flex flex-col justify-between ${
+// isKonfirmasi ? 'items-end' : 'items-start'
+// }`}
+// >
+// <div className='flex flex-row items-start justify-start gap-2'>
+// {isKonfirmasi && (
+// <span className='mt-2 text-gray-600 line-clamp-2'>
+// {fileNames.fotoKantor}
+// </span>
+// )}
+// <label
+// htmlFor='fotoKantor'
+// className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+// >
+// {fileNames.fotoKantor
+// ? 'Ubah Dokumen'
+// : 'Upload Dokumen'}
+// </label>
+// <input
+// {...register('fotoKantor')}
+// type='file'
+// className='form-input hidden'
+// accept='.pdf,.png,.jpg,.jpeg'
+// id='fotoKantor'
+// onChange={handleFileChange}
+// aria-invalid={errors.fotoKantor?.message}
+// />
+// {!isKonfirmasi && (
+// <span className='mt-2 text-gray-600 line-clamp-2'>
+// {fileNames.fotoKantor}
+// </span>
+// )}
+// </div>
+// {!isKonfirmasi && (
+// <span className='text-xs opacity-60 text-red-500'>
+// Format: pdf, jpeg, jpg, png. max file size 2MB
+// </span>
+// )}
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.fotoKantor?.message}
+// </div>
+// </div>
+// </div>
+// <div className='w-full flex flex-row items-start '>
+// <div className='w-2/5 flex flex-col justify-start items-start '>
+// <label
+// className={`form-label ${
+// isKonfirmasi ? 'text-wrap' : 'text-nowrap'
+// }`}
+// ref={dataProdukRef}
+// >
+// Data Produk (Item Name, Gambar, Deskripsi){' '}
+// <span className=' opacity-60'>(Opsional)</span>{' '}
+// </label>
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// Pastikan dokumen yang anda upload sudah benar
+// </span>
+// )}
+// </div>
+// <div
+// className={`w-3/5 flex flex-col justify-between ${
+// isKonfirmasi ? 'items-end' : 'items-start'
+// }`}
+// >
+// <div className='flex flex-row items-start justify-start gap-2'>
+// {isKonfirmasi && (
+// <span className='mt-2 text-gray-600 line-clamp-2'>
+// {fileNames.dataProduk}
+// </span>
+// )}
+// <label
+// htmlFor='dataProduk'
+// className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+// >
+// {fileNames.dataProduk
+// ? 'Ubah Dokumen'
+// : 'Upload Dokumen'}
+// </label>
+// <input
+// {...register('dataProduk')}
+// type='file'
+// className='form-input hidden'
+// accept='.pdf,.png,.jpg,.jpeg'
+// id='dataProduk'
+// onChange={handleFileChange}
+// aria-invalid={errors.dataProduk?.message}
+// />
+// {!isKonfirmasi && (
+// <span className='mt-2 text-gray-600 line-clamp-2'>
+// {fileNames.dataProduk}
+// </span>
+// )}
+// </div>
+// {!isKonfirmasi && (
+// <span className='text-xs opacity-60 text-red-500'>
+// Format: pdf, jpeg, jpg, png. max file size 2MB
+// </span>
+// )}
+
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.dataProduk?.message}
+// </div>
+// </div>
+// </div>
+// <div className='w-full flex flex-row items-start '>
+// <div className='w-2/5 flex flex-col justify-start items-start '>
+// <label
+// className='form-label text-nowrap'
+// ref={pricelistRef}
+// >
+// Pricelist
+// </label>
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// Pastikan dokumen yang anda upload sudah benar
+// </span>
+// )}
+// </div>
+// <div
+// className={`w-3/5 flex flex-col justify-between ${
+// isKonfirmasi ? 'items-end' : 'items-start'
+// }`}
+// >
+// <div className='flex flex-row items-start justify-start gap-2'>
+// {isKonfirmasi && (
+// <span className='mt-2 text-gray-600 line-clamp-2'>
+// {fileNames.pricelist}
+// </span>
+// )}
+// <label
+// htmlFor='pricelist'
+// className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+// >
+// {fileNames.pricelist
+// ? 'Ubah Dokumen'
+// : 'Upload Dokumen'}
+// </label>
+// <input
+// {...register('pricelist')}
+// type='file'
+// className='form-input hidden'
+// accept='.pdf,.png,.jpg,.jpeg'
+// id='pricelist'
+// onChange={handleFileChange}
+// aria-invalid={errors.pricelist?.message}
+// />
+// {!isKonfirmasi && (
+// <span className='mt-2 text-gray-600 line-clamp-2'>
+// {fileNames.pricelist}
+// </span>
+// )}
+// </div>
+// {!isKonfirmasi && (
+// <span className='text-xs opacity-60 text-red-500'>
+// Format: pdf, jpeg, jpg, png. max file size 2MB
+// </span>
+// )}
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.pricelist?.message}
+// </div>
+// </div>
+// </div>
+// <div className=''>
+// {/* <div>
+// <ReCAPTCHA
+// ref={recaptchaRef}
+// sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE}
+// />
+// </div> */}
+// </div>
+// <div className='flex justify-end'>
+// {/* <Button
+// colorScheme='red'
+// className='w-full md:w-fit'
+// type='submit'
+// >
+// Daftar Merchant{' '}
+// {<ChevronRightIcon className='w-5' color='white' />}
+// </Button> */}
+// {!isKonfirmasi && (
+// <div>
+// <span className='text-xs opacity-60'>
+// *Pastikan data yang anda masukan sudah benar dan sesuai
+// </span>
+// <button
+// type='submit'
+// className='btn-light bg-red-500 rounded-lg text-white w-fit py-2 px-4 md:w-fit mt-2 ml-0 md:ml-auto flex justify-between hover:bg-red-400'
+// >
+// <span className={` `}>Langkah Selanjutnya</span>
+// {<ChevronRightIcon className='w-5' />}
+// </button>
+// </div>
+// )}
+// </div>
+// </form>
+// <PageContent path='/daftar-merchant' />
+// </div>
+// </div>
+// </DesktopView>
+// <MobileView>
+// <div className='container flex flex-col items-star py-4'>
+// {!isKonfirmasi && (
+// <h2 className='font-semibold mb-6 text-xl'>Dokumen</h2>
+// )}
+
+// <div className='w-full mt-4'>
+// <form
+// onSubmit={handleSubmit(onSubmitHandler)}
+// className='flex flex-col gap-4'
+// >
+// <div className='w-full flex flex-col gap-2'>
+// <label className='form-label text-nowrap'>
+// NPWP{' '}
+// {!isPkp && <span className=' opacity-60'>(Opsional)</span>}
+// </label>
+// <div className='flex flex-row items-center justify-start gap-2'>
+// <label
+// htmlFor='npwp'
+// className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+// >
+// {fileNames.npwp ? 'Ubah Dokumen' : 'Upload Dokumen'}
+// </label>
+// <input
+// {...register('npwp')}
+// type='file'
+// className='form-input hidden'
+// accept='.pdf,.png,.jpg,.jpeg'
+// id='npwp'
+// onChange={(e) => {
+// handleFileChange(e); // Untuk update UI (opsional)
+// }}
+// aria-invalid={errors.npwp?.message}
+// />
+// <span className=' text-gray-600 line-clamp-2'>
+// {fileNames.npwp}
+// </span>
+// </div>
+// <span className='text-xs opacity-60 text-red-500'>
+// Format: pdf, jpeg, jpg, png. max file size 2MB
+// </span>
+
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.npwp?.message}
+// </div>
+// </div>
+// <div className='w-full flex flex-col gap-2 items-start '>
+// <div className='flex flex-row w-full justify-between items-center '>
+// <label className='form-label text-nowrap'>
+// SPPKP{' '}
+// {!isPkp && (
+// <span className=' opacity-60'>(Opsional)</span>
+// )}
+// </label>
+// <div
+// onClick={() => setIsExample(!isExample)}
+// className='h-fit rounded text-white p-2 flex flex-row items-center bg-red-500 hover:cursor-pointer hover:bg-red-400'
+// >
+// <EyeIcon className={`w-4 mr-2 `} />
+
+// <p className='font-light text-xs '>Lihat Contoh</p>
+// </div>
+// </div>
+// <div className='flex flex-row items-center justify-start gap-2'>
+// <label
+// htmlFor='sppkp'
+// className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+// >
+// {fileNames.sppkp ? 'Ubah Dokumen' : 'Upload Dokumen'}
+// </label>
+// <input
+// {...register('sppkp')}
+// type='file'
+// className='form-input hidden'
+// accept='.pdf,.png,.jpg,.jpeg'
+// id='sppkp'
+// onChange={handleFileChange}
+// aria-invalid={errors.sppkp?.message}
+// />
+// <span className=' text-gray-600 line-clamp-2'>
+// {fileNames.sppkp}
+// </span>
+// </div>
+// <span className='text-xs opacity-60 text-red-500'>
+// Format: pdf, jpeg, jpg, png. max file size 2MB
+// </span>
+
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.sppkp?.message}
+// </div>
+// </div>
+
+// <div className='w-full flex flex-col gap-2'>
+// <label className='form-label text-nowrap'>
+// KTP Dirut/Direktur{' '}
+// {isPkp && <span className=' opacity-60'>(Opsional)</span>}
+// </label>
+// <div className='flex flex-row items-center justify-start gap-2 '>
+// <label
+// htmlFor='dokumenKtpDirut'
+// className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+// >
+// {fileNames.dokumenKtpDirut
+// ? 'Ubah Dokumen'
+// : 'Upload Dokumen'}
+// </label>
+// <input
+// {...register('dokumenKtpDirut')}
+// type='file'
+// className='form-input hidden'
+// accept='.pdf,.png,.jpg,.jpeg'
+// id='dokumenKtpDirut'
+// onChange={handleFileChange}
+// aria-invalid={errors.dokumenKtpDirut?.message}
+// />
+// <span className=' text-gray-600 line-clamp-2'>
+// {fileNames.dokumenKtpDirut}
+// </span>
+// </div>
+// <span className='text-xs opacity-60 text-red-500'>
+// Format: pdf, jpeg, jpg, png. max file size 2MB
+// </span>
+
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.dokumenKtpDirut?.message}
+// </div>
+// </div>
+
+// <div className='w-full flex flex-col gap-2'>
+// <label className='form-label text-nowrap'>
+// Kartu Nama <span className=' opacity-60'>(Opsional)</span>{' '}
+// </label>
+// <div className='flex flex-row items-center justify-start gap-2'>
+// <label
+// htmlFor='kartuNama'
+// className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+// >
+// {fileNames.kartuNama ? 'Ubah Dokumen' : 'Upload Dokumen'}
+// </label>
+// <input
+// {...register('kartuNama')}
+// type='file'
+// className='form-input hidden'
+// accept='.pdf,.png,.jpg,.jpeg'
+// id='kartuNama'
+// onChange={handleFileChange}
+// aria-invalid={errors.kartuNama?.message}
+// />
+// <span className=' text-gray-600 line-clamp-2'>
+// {fileNames.kartuNama}
+// </span>
+// </div>
+// <span className='text-xs opacity-60 text-red-500'>
+// Format: pdf, jpeg, jpg, png. max file size 2MB
+// </span>
+
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.kartuNama?.message}
+// </div>
+// </div>
+
+// <div className='w-full flex flex-col gap-2 items-start '>
+// <div className='flex flex-row w-full justify-between items-center'>
+// <label className='form-label text-wrap'>
+// Surat Pernyataan Nomor Rekening{' '}
+// <span className=' opacity-60'>(Opsional)</span>
+// </label>
+// <a
+// href='/file/Surat Pernyataan Nomor Rekening.docx'
+// download='Surat Pernyataan Nomor Rekening.docx'
+// className='h-fit rounded text-white p-2 flex flex-row items-center bg-red-500 hover:cursor-pointer hover:bg-red-400'
+// >
+// <p className='font-light text-xs text-nowrap'>
+// Download Template
+// </p>
+// </a>
+// </div>
+// <div className='flex flex-row items-center justify-start gap-2'>
+// <label
+// htmlFor='suratPernyataan'
+// className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+// >
+// {fileNames.suratPernyataan
+// ? 'Ubah Dokumen'
+// : 'Upload Dokumen'}
+// </label>
+// <input
+// {...register('suratPernyataan')}
+// type='file'
+// className='form-input hidden'
+// accept='.pdf,.png,.jpg,.jpeg'
+// id='suratPernyataan'
+// onChange={handleFileChange}
+// aria-invalid={errors.suratPernyataan?.message}
+// />
+// <span className=' text-gray-600 line-clamp-2'>
+// {fileNames.suratPernyataan}
+// </span>
+// </div>
+// <span className='text-xs opacity-60 text-red-500'>
+// Format: pdf, jpeg, jpg, png. max file size 2MB
+// </span>
+
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.suratPernyataan?.message}
+// </div>
+// </div>
+// <div className='w-full flex flex-col gap-2 items-start '>
+// <label className='form-label text-nowrap'>
+// Foto Gudang / Kantor Bagian Depan
+// </label>
+// <div className='flex flex-row items-center justify-start gap-2 '>
+// <label
+// htmlFor='fotoKantor'
+// className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+// >
+// {fileNames.fotoKantor ? 'Ubah Dokumen' : 'Upload Dokumen'}
+// </label>
+// <input
+// {...register('fotoKantor')}
+// type='file'
+// className='form-input hidden'
+// accept='.pdf,.png,.jpg,.jpeg'
+// id='fotoKantor'
+// onChange={handleFileChange}
+// aria-invalid={errors.fotoKantor?.message}
+// />
+// <span className=' text-gray-600 line-clamp-2'>
+// {fileNames.fotoKantor}
+// </span>
+// </div>
+// <span className='text-xs opacity-60 text-red-500'>
+// Format: pdf, jpeg, jpg, png. max file size 2MB
+// </span>
+
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.fotoKantor?.message}
+// </div>
+// </div>
+// <div className='w-full flex flex-col gap-2 items-start '>
+// <label className='form-label text-wrap'>
+// Data Produk (Item Name, Gambar, Deskripsi){' '}
+// <span className=' opacity-60'>(Opsional)</span>{' '}
+// </label>
+// <div className='flex flex-row items-center justify-start gap-2'>
+// <label
+// htmlFor='dataProduk'
+// className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+// >
+// {fileNames.dataProduk ? 'Ubah Dokumen' : 'Upload Dokumen'}
+// </label>
+// <input
+// {...register('dataProduk')}
+// type='file'
+// className='form-input hidden'
+// accept='.pdf,.png,.jpg,.jpeg'
+// id='dataProduk'
+// onChange={handleFileChange}
+// aria-invalid={errors.dataProduk?.message}
+// />
+// <span className=' text-gray-600 line-clamp-2'>
+// {fileNames.dataProduk}
+// </span>
+// </div>
+// <span className='text-xs opacity-60 text-red-500'>
+// Format: pdf, jpeg, jpg, png. max file size 2MB
+// </span>
+
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.fotoKantor?.message}
+// </div>
+// </div>
+// <div className='w-full flex flex-col gap-2 items-start '>
+// <label className='form-label text-nowrap'>Pricelist</label>
+// <div className='flex flex-row items-center justify-start gap-2'>
+// <label
+// htmlFor='pricelist'
+// className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+// >
+// {fileNames.pricelist ? 'Ubah Dokumen' : 'Upload Dokumen'}
+// </label>
+// <input
+// {...register('pricelist')}
+// type='file'
+// className='form-input hidden'
+// accept='.pdf,.png,.jpg,.jpeg'
+// id='pricelist'
+// onChange={handleFileChange}
+// aria-invalid={errors.pricelist?.message}
+// />
+// <span className=' text-gray-600 line-clamp-2'>
+// {fileNames.pricelist}
+// </span>
+// </div>
+// <span className='text-xs opacity-60 text-red-500'>
+// Format: pdf, jpeg, jpg, png. max file size 2MB
+// </span>
+
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.pricelist?.message}
+// </div>
+// </div>
+// <div className=''>
+// {/* <div>
+// <ReCAPTCHA
+// ref={recaptchaRef}
+// sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE}
+// />
+// </div> */}
+// </div>
+// <div className='flex justify-center w-full '>
+// {/* <Button
+// colorScheme='red'
+// className='w-full md:w-fit'
+// type='submit'
+// >
+// Daftar Merchant{' '}
+// {<ChevronRightIcon className='w-5' color='white' />}
+// </Button> */}
+// {!isKonfirmasi && (
+// <div className='w-full'>
+// <span className='text-xs opacity-60'>
+// *Pastikan data yang anda masukan sudah benar dan sesuai
+// </span>
+// <button
+// type='submit'
+// className='btn-light bg-red-500 rounded-lg text-white w-full py-2 px-4 md:w-fit mt-2 ml-0 md:ml-auto flex justify-center hover:bg-red-400'
+// >
+// <span className={` `}>Langkah Selanjutnya</span>
+// {<ChevronRightIcon className='w-5' />}
+// </button>
+// </div>
+// )}
+// </div>
+// </form>
+// <PageContent path='/daftar-merchant' />
+// </div>
+// </div>
+// </MobileView>
+// </>
+// );
+// }
+// );
+// const validationSchema = Yup.object().shape({
+// npwp: Yup.mixed().required('File is required'),
+// pricelist: Yup.mixed().required('File is required'),
+// });
+// const defaultValues = {
+// company: '',
+// pejabatName: '',
+// PICName: '',
+// PICPosition: '',
+// email: '',
+// emailSales: '',
+// emailFinance: '',
+// phone: '',
+// state: '',
+// city: '',
+// district: '',
+// subDistrict: '',
+// zip: '',
+// bank: '',
+// rekening: '',
+// accountNumber: '',
+// address: '',
+// mobile: '',
+// };
+
+// export default Dokumen;
diff --git a/src/lib/merchant/components/InformasiPerusahaan.jsx b/src/lib/merchant/components/InformasiPerusahaan.jsx
new file mode 100644
index 00000000..e9486214
--- /dev/null
+++ b/src/lib/merchant/components/InformasiPerusahaan.jsx
@@ -0,0 +1,1399 @@
+// import HookFormSelect from '@/core/components/elements/Select/HookFormSelect';
+// import cityApi from '@/lib/address/api/cityApi';
+// import stateApi from '@/lib/address/api/stateApi.js';
+// import districtApi from '@/lib/address/api/districtApi';
+// import subDistrictApi from '@/lib/address/api/subDistrictApi';
+// import { yupResolver } from '@hookform/resolvers/yup';
+// import React, {
+// useEffect,
+// useRef,
+// useState,
+// forwardRef,
+// useImperativeHandle,
+// } from 'react';
+// import ReCAPTCHA from 'react-google-recaptcha';
+// import { Controller, useForm } from 'react-hook-form';
+// import { toast } from 'react-hot-toast';
+// import * as Yup from 'yup';
+// import createMerchantApi from '../api/createMerchantApi';
+// import getMerchantApi from '../api/getMerchantApi';
+// import addressApi from '@/lib/address/api/addressApi';
+// import PageContent from '@/lib/content/components/PageContent';
+// import { useRouter } from 'next/router';
+// import useAuth from '@/core/hooks/useAuth';
+// import { Radio, RadioGroup, Stack, Divider, Button } from '@chakra-ui/react';
+// import { EyeIcon } from '@heroicons/react/24/outline';
+// import BottomPopup from '@/core/components/elements/Popup/BottomPopup';
+// import Image from 'next/image';
+// import ImageBanner from '~/components/ui/image';
+// import { ChevronRightIcon, ChevronLeftIcon } from '@heroicons/react/24/outline';
+// import MobileView from '@/core/components/views/MobileView';
+// import DesktopView from '@/core/components/views/DesktopView';
+// import getFileBase64 from '@/core/utils/getFileBase64';
+// import odooApi from '~/libs/odooApi';
+
+// const CreateMerchant = forwardRef(
+// ({ handleIsError, isKonfirmasi, buttonSubmitClick }, ref) => {
+// const isError = (value) => {
+// // Logika menentukan error
+// const result = value ? true : false;
+// handleIsError(result); // Panggil handleIsError dari Merchant
+// return result;
+// };
+// // React.useEffect(() => {
+// // handleIsError(isError());
+// // }, [handleIsError]);
+// const {
+// register,
+// handleSubmit,
+// formState: { errors },
+// control,
+// reset,
+// watch,
+// setValue,
+// getValues,
+// } = useForm({
+// resolver: yupResolver(validationSchema),
+// defaultValues,
+// });
+// const [state, setState] = useState([]);
+// const [cities, setCities] = useState([]);
+// const [districts, setDistricts] = useState([]);
+// const [subDistricts, setSubDistricts] = useState([]);
+// const [zips, setZips] = useState([]);
+// const [isExample, setIsExample] = useState(false);
+// const [isPkp, setIsPkp] = useState(false);
+
+// useEffect(() => {
+// window.scrollTo({
+// top: 0,
+// behavior: 'smooth',
+// });
+// }, []);
+
+
+// useImperativeHandle(ref, () => () => {
+// handleSubmit(onSubmitHandler)();
+// });
+
+// const recaptchaRef = useRef(null);
+// const router = useRouter();
+
+// const auth = useAuth();
+// if (auth == false) {
+// router.push(`/login?next=${encodeURIComponent('/daftar-merchant')}`);
+// }
+// const dataBisnisType = [
+// { value: 1, label: 'PT' },
+// { value: 2, label: 'CV' },
+// { value: 3, label: 'Perorangan' },
+// ];
+// const dataCategoryPerusahaan = [
+// { value: 1, label: 'Principal (Pemegang merk/Produsen)' },
+// { value: 2, label: 'Sole Distributor (Distributor Tunggal)' },
+// { value: 3, label: 'Authorized Distributor (Distributor Resmi)' },
+// { value: 4, label: 'Importer (Pengimpor Barang)' },
+// { value: 5, label: 'Wholesaler (Pedagang Besar)' },
+// ];
+
+// useEffect(() => {
+// const loadData = async () => {
+// try {
+// const data = await getMerchantApi();
+// if (data) {
+// reset({
+// pejabatName: data.pejabatName ? data.pejabatName : '',
+// PICName: data.picMerchant || '',
+// PICPosition: data.picPosition || '',
+// address: data.address ? data.address : '',
+// state: data.state ? data.state : '',
+// city: data.city || '',
+// district: data.district || '',
+// subDistrict: data.subDistrict || '',
+// zip: parseInt(data.zip) || '',
+// email: data.emailCompany || '',
+// emailSales: data.emailSales || '',
+// emailFinance: data.emailFinance || '',
+// bank: data.bankName || '',
+// rekening: data.rekeningName || '',
+// accountNumber: data.accountNumber || '',
+// phone: data.phone || '',
+// mobile: data.mobile || '',
+// bisnisType: data.bisnisType ? parseInt(data.bisnisType) : null,
+// categoryPerusahaan: data.categoryPerusahaan
+// ? parseInt(data.categoryPerusahaan)
+// : null,
+// website: data.website || '',
+// });
+// }
+// } catch (error) {
+// console.error('Error loading profile:', error);
+// handleIsError(true); // Jika ada error, panggil fungsi error handler
+// }
+// };
+
+// loadData();
+// }, [reset, handleIsError]);
+
+// useEffect(() => {
+// const loadProfile = async () => {
+// try {
+// const dataProfile = await addressApi({
+// id: auth.parentId ? auth.parentId : auth.partnerId,
+// });
+// if (dataProfile.companyType == 'pkp') {
+// setIsPkp(true);
+// }
+// setValue('company', dataProfile?.name);
+// setValue('address', dataProfile?.alamatBisnis);
+// setValue('state', parseInt(dataProfile.stateId.id));
+// setValue('city', parseInt(dataProfile.city.id));
+// setValue('district', parseInt(dataProfile.district.id));
+// setValue('subDistrict', parseInt(dataProfile.subDistrict.id));
+// setValue('zip', parseInt(dataProfile.zip));
+// } catch (error) {
+// console.error('Error loading profile:', error);
+// }
+// };
+
+// loadProfile();
+// }, [auth?.parentId]);
+
+// useEffect(() => {
+// const loadState = async () => {
+// let dataState = await stateApi({ tempo: false });
+// dataState = dataState.map((state) => ({
+// value: state.id,
+// label: state.name,
+// }));
+// setState(dataState);
+// };
+// loadState();
+// }, []);
+
+// const watchState = watch('state');
+// useEffect(() => {
+// if (auth == false) {
+// return;
+// }
+// if (watchState) {
+// // setValue('city', '');
+// const loadCities = async () => {
+// let dataCities = await cityApi({ stateId: watchState });
+// dataCities = dataCities?.map((city) => ({
+// value: city.id,
+// label: city.name,
+// }));
+// setCities(dataCities);
+// };
+// loadCities();
+// }
+// }, [auth, watchState]);
+
+// const watchCity = watch('city');
+// useEffect(() => {
+// if (watchCity) {
+// // setValue('district', '');
+// const loadDistricts = async () => {
+// let dataDistricts = await districtApi({ cityId: watchCity });
+// dataDistricts = dataDistricts.map((district) => ({
+// value: district.id,
+// label: district.name,
+// }));
+// setDistricts(dataDistricts);
+// };
+// loadDistricts();
+// }
+// }, [watchCity]);
+
+// const watchDistrict = watch('district');
+// useEffect(() => {
+// if (watchDistrict) {
+// // setValue('subDistrict', '');
+// const loadSubDistricts = async () => {
+// let dataSubDistricts = await subDistrictApi({
+// districtId: watchDistrict,
+// });
+// dataSubDistricts = dataSubDistricts.map((district) => ({
+// value: district.id,
+// label: district.name,
+// }));
+// setSubDistricts(dataSubDistricts);
+// };
+// loadSubDistricts();
+// }
+// }, [watchDistrict]);
+
+// const watchsubDistrict = watch('subDistrict');
+
+// useEffect(() => {
+// let kelurahan = '';
+// let kecamatan = '';
+
+// if (watchDistrict) {
+// // setValue('zip', '');
+// for (const data in districts) {
+// if (districts[data].value == watchDistrict) {
+// kecamatan = districts[data].label.toLowerCase();
+// }
+// }
+// }
+
+// if (watchsubDistrict) {
+// for (const data in subDistricts) {
+// if (subDistricts[data].value == watchsubDistrict) {
+// kelurahan = subDistricts[data].label.toLowerCase();
+// }
+// }
+// const loadZip = async () => {
+// const response = await fetch(
+// `https://alamat.thecloudalert.com/api/cari/index/?keyword=${kelurahan}`
+// );
+
+// let dataMasuk = []; // Array untuk menyimpan kode pos yang sudah diproses
+
+// const result = await response.json();
+
+// // Filter dan map data
+// const dataZips = result.result
+// .filter((zip) => zip.kecamatan.toLowerCase() === kecamatan) // Filter berdasarkan kecamatan
+// .filter((zip) => {
+// // Pastikan zip.kodepos belum ada di dataMasuk
+// if (dataMasuk.includes(zip.kodepos)) {
+// return false; // Jika sudah ada, maka skip (tidak akan ditambahkan)
+// } else {
+// dataMasuk.push(zip.kodepos); // Tambahkan ke dataMasuk
+// return true; // Tambahkan zip ke hasil
+// }
+// })
+// .map((zip) => ({
+// value: parseInt(zip.kodepos),
+// label: zip.kodepos,
+// }));
+
+// setZips(dataZips); // Set hasil ke state
+// };
+
+// loadZip();
+// }
+// }, [watchsubDistrict, subDistricts]);
+// const onSubmitHandler = async (values) => {
+// const toastId = toast.loading('Mengirimkan formulir merchant...');
+// const data = {
+// name_merchant: 'Form Merchant - ' + values.company,
+// pejabat_name: values.pejabatName,
+// pic_merchant: values.PICName,
+// pic_position: values.PICPosition,
+// address: values.address,
+// state: parseInt(values.state),
+// city: parseInt(values.city),
+// district: parseInt(values.district),
+// subDistrict: parseInt(values.subDistrict),
+// zip: values.zip,
+// bank_name: values.bank,
+// rekening_name: values.rekening,
+// account_number: values.accountNumber,
+// email_company: values.email,
+// email_sales: values.emailSales,
+// email_finance: values.emailFinance,
+// phone: values.phone,
+// mobile: values.mobile,
+// bisnis_type: values.bisnisType,
+// category_perusahaan: values.categoryPerusahaan,
+// website: values.website,
+// description:
+// 'Nama Perusahaan : ' +
+// values.company +
+// ' \r\n Alamat : ' +
+// values.address +
+// ' \r\n Kota : ' +
+// values.city +
+// ' \r\n Telepon: ' +
+// values.phone +
+// ' \r\n Email : ' +
+// values.email +
+// ' \r\n No Hp : ' +
+// values.mobile,
+// };
+// const create_leads = await createMerchantApi({ data });
+// if (create_leads) {
+// toast.dismiss(toastId);
+// toast.success('Berhasil menambahkan data');
+// isError(false);
+// reset();
+// // router.push('/');
+// } else {
+// toast.dismiss(toastId);
+// toast.error('Gagal menambahkan data');
+// }
+// };
+
+// // const DownLoadSurat = () => {
+// // download surat dari /public/file/Surat Pernyataan Nomor Rekening.docx
+// // };
+
+// if (!auth) {
+// return;
+// }
+// // Tetap di bagian atas, tidak boleh ada kondisi sebelum hook
+
+
+
+// return (
+// <>
+// <BottomPopup
+// className=''
+// title='Contoh SPPKP'
+// active={isExample}
+// close={() => setIsExample(false)}
+// >
+// <div className='flex p-2'>
+// <Image
+// src='/images/NO-SPPKP-FORMAT-TEMPLATE.jpg'
+// alt='Contoh SPPKP'
+// className='w-full h-full '
+// width={800}
+// height={800}
+// quality={85}
+// />
+// </div>
+// </BottomPopup>
+// <DesktopView>
+// <div className='container flex flex-col items-star py-4 '>
+// <h2 className='text-xs md:text-title-sm font-semibold mb-6'>
+// Informasi Perusahaan
+// </h2>
+
+// <div className='w-full mt-4'>
+// <form
+// onSubmit={handleSubmit(onSubmitHandler)}
+// className='flex flex-col gap-4'
+// >
+// <div className='w-full flex flex-row'>
+// <div className='w-2/5'>
+// <label className='form-label text-nowrap'>
+// Nama Perusahaan
+// </label>
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// Isi detail perusahaan sesuai dengan nama yang terdaftar{' '}
+// </span>
+// )}
+// </div>
+// <div className='w-3/5'>
+// <input
+// {...register('company')}
+// placeholder='Masukkan nama perusahaan'
+// type='text'
+// className='form-input'
+// />
+// <span className='opacity-65 text-xs'>
+// Format: PT. INDOTEKNIK DOTCOM GEMILANG
+// </span>
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.company?.message}
+// </div>
+// </div>
+// </div>
+// <div className='w-full flex flex-row'>
+// <div className='w-2/5'>
+// <label className='form-label text-nowrap'>
+// Pejabat Berwenang
+// </label>
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// isi dengan nama pejabat yang berwewenang di perusahaan
+// anda
+// </span>
+// )}
+// </div>
+// <div className='w-3/5'>
+// <input
+// {...register('pejabatName')}
+// placeholder='Masukkan nama pejabat berwenang'
+// type='text'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.pejabatName?.message}
+// </div>
+// </div>
+// </div>
+// <div className='w-full flex flex-row'>
+// <div className='w-2/5'>
+// <label className='form-label text-nowrap'>Nama PIC</label>
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// isi dengan nama sales / penanggung jawab
+// </span>
+// )}
+// </div>
+// <div className='w-3/5'>
+// <input
+// {...register('PICName')}
+// placeholder='Masukkan nama PIC'
+// type='text'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.PICName?.message}
+// </div>
+// </div>
+// </div>
+// <div className='w-full flex flex-row'>
+// <div className='w-2/5'>
+// <label className='form-label text-nowrap'>
+// Jabatan PIC
+// </label>
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// isi dengan jabatan sales / penanggung jawab
+// </span>
+// )}
+// </div>
+// <div className='w-3/5'>
+// <input
+// {...register('PICPosition')}
+// placeholder='Masukkan jabatan PIC'
+// type='text'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.PICPosition?.message}
+// </div>
+// </div>
+// </div>
+// <div className='w-full flex flex-row'>
+// <div className='w-2/5'>
+// <label className='form-label text-nowrap'>
+// Alamat Perusahaan
+// </label>
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// Alamat sesuai dengan alamat perusahaan
+// </span>
+// )}
+// </div>
+// <div className='w-3/5 flex flex-col'>
+// <div>
+// <input
+// {...register('address')}
+// placeholder='Masukkan alamat lengkap perusahaan'
+// type='text'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.address?.message}
+// </div>
+// </div>
+// <div
+// className={` sub-alamat flex ${
+// isKonfirmasi ? 'flex-col' : 'flex-row'
+// } w-full gap-3`}
+// >
+// <div
+// className={`flex ${
+// isKonfirmasi
+// ? ' flex-row gap-3 w-full'
+// : 'flex-row gap-3 w-2/5'
+// }`}
+// >
+// <div className='provinsi w-full'>
+// <Controller
+// name='state'
+// control={control}
+// render={(props) => (
+// <HookFormSelect
+// {...props}
+// options={state}
+// placeholder='Provinsi'
+// />
+// )}
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.state?.message}
+// </div>
+// </div>
+// <div className='kab w-full'>
+// <Controller
+// name='city'
+// control={control}
+// render={(props) => (
+// <HookFormSelect
+// {...props}
+// options={cities}
+// disabled={!watchState}
+// placeholder='Kab/Kota'
+// />
+// )}
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.city?.message}
+// </div>
+// </div>
+// </div>
+// <div
+// className={`flex-row flex gap-2 justify-between ${
+// isKonfirmasi ? 'w-full' : 'w-3/5'
+// }`}
+// >
+// <div className='kec w-full'>
+// <Controller
+// name='district'
+// control={control}
+// render={(props) => (
+// <HookFormSelect
+// {...props}
+// options={districts}
+// disabled={!watchState || !watchCity}
+// placeholder='Kecamatan'
+// />
+// )}
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.district?.message}
+// </div>
+// </div>
+// <div className='kel w-full'>
+// <Controller
+// name='subDistrict'
+// control={control}
+// render={(props) => (
+// <HookFormSelect
+// {...props}
+// options={subDistricts}
+// disabled={!watchDistrict}
+// placeholder='Kelurahan'
+// />
+// )}
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.subDistrict?.message}
+// </div>
+// </div>
+// <div className='zip w-full'>
+// <Controller
+// name='zip'
+// control={control}
+// render={(props) => (
+// <>
+// {/* Jika zips tidak kosong, tampilkan dropdown */}
+// {zips.length < 0 ? (
+// // Jika zips kosong, tampilkan input manual
+// <input
+// {...register('zip')}
+// placeholder='Kode Pos'
+// type='number'
+// className='form-input'
+// disabled={!watchsubDistrict}
+// />
+// ) : (
+// <HookFormSelect
+// {...props}
+// options={zips}
+// disabled={!watchsubDistrict}
+// placeholder='Zip'
+// />
+// )}
+// </>
+// )}
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.zip?.message}
+// </div>
+// </div>
+// </div>
+// </div>
+// </div>
+// </div>
+// <div className='w-full flex flex-row'>
+// <div className='w-2/5'>
+// <label className='form-label text-nowrap'>Data Bank</label>
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// Isi detail data bank perusahaan anda
+// </span>
+// )}
+// </div>
+// <div className='w-3/5 flex flex-row gap-2'>
+// <div>
+// <input
+// {...register('bank')}
+// placeholder='Nama bank'
+// type='text'
+// className='form-input'
+// />
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// Format: BCA, Mandiri, CIMB, BNI dll
+// </span>
+// )}
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.bank?.message}
+// </div>
+// </div>
+// <div>
+// <input
+// {...register('rekening')}
+// placeholder='Nama Rekening'
+// type='text'
+// className='form-input'
+// />
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// Format: John Doe
+// </span>
+// )}
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.rekening?.message}
+// </div>
+// </div>
+// <div>
+// <input
+// {...register('accountNumber')}
+// placeholder='Nomor Rekening Bank'
+// type='number'
+// className='form-input'
+// />
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// Format: 01234567896
+// </span>
+// )}
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.accountNumber?.message}
+// </div>
+// </div>
+// </div>
+// </div>
+// <div className='w-full flex flex-row'>
+// <div className='w-2/5'>
+// <label className='form-label text-nowrap'>
+// Email Perusahaan
+// </label>
+// </div>
+// <div className='w-3/5'>
+// <input
+// {...register('email')}
+// placeholder='contoh@email.com'
+// type='email'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.email?.message}
+// </div>
+// </div>
+// </div>
+// <div className='w-full flex flex-row'>
+// <div className='w-2/5'>
+// <label className='form-label text-nowrap'>
+// Email Sales
+// </label>
+// </div>
+// <div className='w-3/5'>
+// <input
+// {...register('emailSales')}
+// placeholder='contoh@email.com'
+// type='email'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.emailSales?.message}
+// </div>
+// </div>
+// </div>
+// <div className='w-full flex flex-row'>
+// <div className='w-2/5'>
+// <label className='form-label text-nowrap'>
+// Email Finance
+// </label>
+// </div>
+// <div className='w-3/5'>
+// <input
+// {...register('emailFinance')}
+// placeholder='contoh@email.com'
+// type='email'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.emailFinance?.message}
+// </div>
+// </div>
+// </div>
+// <div className='w-full flex flex-row'>
+// <div className='w-2/5'>
+// <label className='form-label text-nowrap'>
+// No. Telepon Perusahaan
+// </label>
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// Isi no telepon perusahaan yang sesuai
+// </span>
+// )}
+// </div>
+// <div className='w-3/5'>
+// <input
+// {...register('phone', {
+// required: 'Nomor telepon wajib diisi.',
+// pattern: {
+// value: /^\+?[0-9]{10,15}$/,
+// message: 'Nomor telepon tidak valid.',
+// },
+// })}
+// placeholder='Masukkan nomor telepon perusahaan'
+// type='tel'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.phone?.message}
+// </div>
+// </div>
+// </div>
+// <div className='w-full flex flex-row'>
+// <div className='w-2/5'>
+// <label className='form-label text-nowrap'>
+// No. Handphone
+// </label>
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// Isi no handphone perusahaan yang sesuai
+// </span>
+// )}
+// </div>
+// <div className='w-3/5'>
+// <input
+// {...register('mobile')}
+// placeholder='Masukkan nomor handphone'
+// type='tel'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.mobile?.message}
+// </div>
+// </div>
+// </div>
+
+// <div className='flex flex-row justify-between items-center'>
+// <div className='w-2/5'>
+// <label className='form-label text-nowrap'>
+// Tipe Bisnis
+// </label>
+// {!isKonfirmasi && (
+// <span className='text-xs opacity-60'>
+// Pilih tipe bisnis yang sesuai
+// </span>
+// )}
+// </div>
+// <div className='w-3/5 flex flex-col '>
+// <div className='flex flex-row items-center gap-3'>
+// <div
+// // className={`${isKonfirmasi ? 'w-[75%]' : 'w-[25%]'}`}
+// // ref={tempoDurationRef}
+// >
+// <Controller
+// name='bisnisType'
+// control={control}
+// render={(props) => (
+// <HookFormSelect
+// {...props}
+// options={dataBisnisType}
+// placeholder={'Pilih tipe bisnis'}
+// />
+// )}
+// />
+
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.bisnisType?.message}
+// </div>
+// </div>
+// </div>
+// </div>
+// </div>
+// <div className='flex flex-row justify-between items-center'>
+// <div className='w-2/5'>
+// <label className='form-label text-nowrap'>
+// Kategori Perusahaan
+// </label>
+// {!isKonfirmasi && (
+// <span className='text-xs opacity-60'>
+// Pilih kategori perusahaan yang sesuai
+// </span>
+// )}
+// </div>
+// <div className='w-3/5 flex flex-col '>
+// <div className='flex flex-row items-center gap-3'>
+// <div
+// // className={`${isKonfirmasi ? 'w-[75%]' : 'w-[25%]'}`}
+// className='w-[55%]'
+// // ref={tempoDurationRef}
+// >
+// <Controller
+// name='categoryPerusahaan'
+// control={control}
+// render={(props) => (
+// <HookFormSelect
+// {...props}
+// options={dataCategoryPerusahaan}
+// placeholder={'Pilih category perusahaan'}
+// />
+// )}
+// />
+
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.categoryPerusahaan?.message}
+// </div>
+// </div>
+// </div>
+// </div>
+// </div>
+
+// <div className='w-full flex flex-row'>
+// <div className='w-2/5'>
+// <label className='form-label text-nowrap'>Website</label>
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// isi dengan website perusahaan anda
+// </span>
+// )}
+// </div>
+// <div className='w-3/5'>
+// <input
+// {...register('website')}
+// placeholder='Masukkan website'
+// type='text'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.website?.message}
+// </div>
+// </div>
+// </div>
+
+// <div className=''>
+// {/* <div>
+// <ReCAPTCHA
+// ref={recaptchaRef}
+// sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE}
+// />
+// </div> */}
+// </div>
+// <div className='flex justify-end'>
+// {/* <Button
+// colorScheme='red'
+// className='w-full md:w-fit'
+// type='submit'
+// >
+// Daftar Merchant{' '}
+// {<ChevronRightIcon className='w-5' color='white' />}
+// </Button> */}
+// {!isKonfirmasi && (
+// <div>
+// <span className='text-xs opacity-60'>
+// *Pastikan data yang anda masukan sudah benar dan sesuai
+// </span>
+// <button
+// type='submit'
+// className='btn-light bg-red-500 rounded-lg text-white w-fit py-2 px-4 md:w-fit mt-2 ml-0 md:ml-auto flex justify-between hover:bg-red-400'
+// >
+// <span className={` `}>Langkah Selanjutnya</span>
+// {<ChevronRightIcon className='w-5' />}
+// </button>
+// </div>
+// )}
+// </div>
+// </form>
+// <PageContent path='/daftar-merchant' />
+// </div>
+// </div>
+// </DesktopView>
+// <MobileView>
+// <div className='container flex flex-col items-star py-4'>
+// {!isKonfirmasi && (
+// <h2 className='font-semibold mb-6 text-xl'>
+// Informasi Perusahaan
+// </h2>
+// )}
+
+// <div className='w-full mt-4'>
+// <form
+// onSubmit={handleSubmit(onSubmitHandler)}
+// className='flex flex-col gap-4'
+// >
+// <div className='w-full flex flex-col'>
+// <div className='w-full'>
+// <label className='form-label text-nowrap'>
+// Nama Perusahaan
+// </label>
+// <input
+// {...register('company')}
+// placeholder='Format: PT. INDOTEKNIK DOTCOM GEMILANG'
+// type='text'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.company?.message}
+// </div>
+// <span className='opacity-65 text-xs'>
+// Isi detail perusahaan sesuai dengan nama yang terdaftar{' '}
+// </span>
+// </div>
+// </div>
+// <div className='w-full flex flex-col'>
+// <div className='w-full'>
+// <label className='form-label text-nowrap'>
+// Pejabat Berwenang
+// </label>
+// <input
+// {...register('pejabatName')}
+// placeholder='Masukkan nama pejabat berwenang'
+// type='text'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.pejabatName?.message}
+// </div>
+// <span className='opacity-65 text-xs'>
+// isi dengan nama pejabat yang berwewenang di perusahaan
+// anda{' '}
+// </span>
+// </div>
+// </div>
+// <div className='w-full flex flex-col'>
+// <div className='w-full'>
+// <label className='form-label text-nowrap'>Nama PIC</label>
+// <input
+// {...register('PICName')}
+// placeholder='Masukkan Nama PIC '
+// type='text'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.PICName?.message}
+// </div>
+// <span className='opacity-65 text-xs'>
+// Isi dengan nama sales / penanggung jawab
+// </span>
+// </div>
+// </div>
+// <div className='w-full flex flex-col'>
+// <div className='w-full'>
+// <label className='form-label text-nowrap'>
+// Jabatan PIC
+// </label>
+// <input
+// {...register('PICPosition')}
+// placeholder='Masukkan jabatan PIC '
+// type='text'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.PICPosition?.message}
+// </div>
+// <span className='opacity-65 text-xs'>
+// isi dengan jabatan sales / penanggung jawab
+// </span>
+// </div>
+// </div>
+// <div className='w-full flex flex-col'>
+// <div className='w-full'>
+// <label className='form-label text-nowrap'>
+// Alamat Perusahaan
+// </label>
+// <input
+// {...register('address')}
+// placeholder='Masukkan alamat lengkap perusahaan'
+// type='text'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.address?.message}
+// </div>
+// <div className='flex flex-row w-full justify-between gap-2'>
+// <div className='provinsi w-full'>
+// <Controller
+// name='state'
+// control={control}
+// render={(props) => (
+// <HookFormSelect
+// {...props}
+// options={state}
+// placeholder='Provinsi'
+// />
+// )}
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.state?.message}
+// </div>
+// </div>
+// <div className='kab w-full'>
+// <Controller
+// name='city'
+// control={control}
+// render={(props) => (
+// <HookFormSelect
+// {...props}
+// options={cities}
+// disabled={!watchState}
+// placeholder='Kab/Kota'
+// />
+// )}
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.city?.message}
+// </div>
+// </div>
+// </div>
+// <div className='flex flex-row w-full justify-between gap-2'>
+// <div className='kec w-full'>
+// <Controller
+// name='district'
+// control={control}
+// render={(props) => (
+// <HookFormSelect
+// {...props}
+// options={districts}
+// disabled={!watchState || !watchCity}
+// placeholder='Kecamatan'
+// />
+// )}
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.district?.message}
+// </div>
+// </div>
+// <div className='kel w-full'>
+// <Controller
+// name='subDistrict'
+// control={control}
+// render={(props) => (
+// <HookFormSelect
+// {...props}
+// options={subDistricts}
+// disabled={!watchDistrict}
+// placeholder='Kelurahan'
+// />
+// )}
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.subDistrict?.message}
+// </div>
+// </div>
+// <div className='zip w-full'>
+// <Controller
+// name='zip'
+// control={control}
+// render={(props) => (
+// <>
+// {zips.length > 0 ? (
+// <HookFormSelect
+// {...props}
+// options={zips}
+// disabled={!watchsubDistrict}
+// placeholder='Zip'
+// />
+// ) : (
+// <input
+// {...register('zip')}
+// placeholder='Kode Pos'
+// type='number'
+// className='form-input'
+// disabled={!watchsubDistrict}
+// />
+// )}
+// </>
+// )}
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.zip?.message}
+// </div>
+// </div>
+// </div>
+// </div>
+// <span className='opacity-65 text-xs'>
+// Alamat sesuai dengan alamat perusahaan
+// </span>
+// </div>
+// <div className='w-full flex flex-col'>
+// <label className='form-label text-nowrap'>Data Bank</label>
+// <div className='w-full flex flex-row gap-2'>
+// <div>
+// <input
+// {...register('bank')}
+// placeholder='Nama bank'
+// type='text'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.bank?.message}
+// </div>
+// </div>
+// <div>
+// <input
+// {...register('rekening')}
+// placeholder='Nama Rekening'
+// type='text'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.rekening?.message}
+// </div>
+// </div>
+// <div>
+// <input
+// {...register('accountNumber')}
+// placeholder='Nomor Rekening Bank'
+// type='number'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.accountNumber?.message}
+// </div>
+// </div>
+// </div>
+// <span className='opacity-65 text-xs'>
+// Isi detail data bank perusahaan anda
+// </span>
+// </div>
+// <div className='w-full flex flex-col'>
+// <label className='form-label text-nowrap'>
+// Email Perusahaan
+// </label>
+// <input
+// {...register('email')}
+// placeholder='contoh@email.com'
+// type='email'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.email?.message}
+// </div>
+// <span className='opacity-65 text-xs'>
+// Isi detail perusahaan sesuai dengan data yang terdaftar
+// </span>
+// </div>
+// <div className='w-full flex flex-col'>
+// <label className='form-label text-nowrap'>Email Sales</label>
+// <input
+// {...register('emailSales')}
+// placeholder='contoh@email.com'
+// type='email'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.emailSales?.message}
+// </div>
+// <span className='opacity-65 text-xs'>
+// Isi detail perusahaan sesuai dengan data yang terdaftar
+// </span>
+// </div>
+// <div className='w-full flex flex-col'>
+// <label className='form-label text-nowrap'>
+// Email Finance
+// </label>
+// <input
+// {...register('emailFinance')}
+// placeholder='contoh@email.com'
+// type='email'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.emailFinance?.message}
+// </div>
+// <span className='opacity-65 text-xs'>
+// Isi detail perusahaan sesuai dengan data yang terdaftar
+// </span>
+// </div>
+// <div className='w-full flex flex-col'>
+// <label className='form-label text-nowrap'>
+// No. Telepon Perusahaan
+// </label>
+// <input
+// {...register('phone')}
+// placeholder='Format: 08123456789 / (021) 123 4567'
+// type='tel'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.phone?.message}
+// </div>
+// <span className='opacity-65 text-xs'>
+// Isi detail perusahaan sesuai dengan data yang terdaftar
+// </span>
+// </div>
+// <div className='w-full flex flex-col'>
+// <label className='form-label text-nowrap'>
+// No. Handphone
+// </label>
+// <input
+// {...register('mobile')}
+// placeholder='Masukkan nomor handphone'
+// type='tel'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.mobile?.message}
+// </div>
+// <span className='opacity-65 text-xs'>
+// Isi detail perusahaan sesuai dengan data yang terdaftar
+// </span>
+// </div>
+// <div className='flex flex-col'>
+// <label className='form-label text-nowrap'>Tipe Bisnis</label>
+// <div className='flex flex-col '>
+// <Controller
+// name='bisnisType'
+// control={control}
+// render={(props) => (
+// <HookFormSelect
+// {...props}
+// options={dataBisnisType}
+// placeholder={'Pilih tipe bisnis'}
+// />
+// )}
+// />
+// {!isKonfirmasi && (
+// <span className='text-xs opacity-60'>
+// Pilih tipe bisnis yang sesuai
+// </span>
+// )}
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.bisnisType?.message}
+// </div>
+// </div>
+// </div>
+// <div className='flex flex-col'>
+// <label className='form-label text-nowrap'>
+// Kategori Perusahaan
+// </label>
+// <div className='flex flex-col '>
+// <Controller
+// name='categoryPerusahaan'
+// control={control}
+// render={(props) => (
+// <HookFormSelect
+// {...props}
+// options={dataCategoryPerusahaan}
+// placeholder={'Pilih category perusahaan'}
+// />
+// )}
+// />
+// {!isKonfirmasi && (
+// <span className='text-xs opacity-60'>
+// Pilih kategori perusahaan yang sesuai
+// </span>
+// )}
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.categoryPerusahaan?.message}
+// </div>
+// </div>
+// </div>
+// <div className='w-full flex flex-col'>
+// <label className='form-label text-nowrap'>Website</label>
+// <input
+// {...register('website')}
+// placeholder='Masukkan website'
+// type='text'
+// className='form-input'
+// />
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// isi dengan website perusahaan anda
+// </span>
+// )}
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.website?.message}
+// </div>
+// </div>
+
+// <div className=''>
+// {/* <div>
+// <ReCAPTCHA
+// ref={recaptchaRef}
+// sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE}
+// />
+// </div> */}
+// </div>
+// <div className='flex justify-center w-full '>
+// {/* <Button
+// colorScheme='red'
+// className='w-full md:w-fit'
+// type='submit'
+// >
+// Daftar Merchant{' '}
+// {<ChevronRightIcon className='w-5' color='white' />}
+// </Button> */}
+// {!isKonfirmasi && (
+// <div className='w-full'>
+// <span className='text-xs opacity-60'>
+// *Pastikan data yang anda masukan sudah benar dan sesuai
+// </span>
+// <button
+// type='submit'
+// className='btn-light bg-red-500 rounded-lg text-white w-full py-2 px-4 md:w-fit mt-2 ml-0 md:ml-auto flex justify-center hover:bg-red-400'
+// >
+// <span className={` `}>Langkah Selanjutnya</span>
+// {<ChevronRightIcon className='w-5' />}
+// </button>
+// </div>
+// )}
+// </div>
+// </form>
+// <PageContent path='/daftar-merchant' />
+// </div>
+// </div>
+// </MobileView>
+// </>
+// );
+// }
+// );
+
+// const validationSchema = Yup.object().shape({
+// company: Yup.string().required('Harus di-isi'),
+// pejabatName: Yup.string().required('Harus di-isi'),
+// PICName: Yup.string().required('Harus di-isi'),
+// PICPosition: Yup.string().required('Harus di-isi'),
+// address: Yup.string().required('Harus di-isi'),
+// state: Yup.string().required('Harus dipilih'),
+// city: Yup.string().required('Harus dipilih'),
+// district: Yup.string().required('Harus dipilih'),
+// subDistrict: Yup.string().required('Harus dipilih'),
+// zip: Yup.string().required('Harus di-isi'),
+// bank: Yup.string().required('Harus di-isi'),
+// rekening: Yup.string().required('Harus di-isi'),
+// accountNumber: Yup.string().required('Harus di-isi'),
+// email: Yup.string()
+// .email('Format harus seperti contoh@email.com')
+// .required('Harus di-isi'),
+// emailSales: Yup.string()
+// .email('Format harus seperti contoh@email.com')
+// .required('Harus di-isi'),
+// emailFinance: Yup.string()
+// .email('Format harus seperti contoh@email.com')
+// .required('Harus di-isi'),
+// phone: Yup.string().required('Harus di-isi'),
+// mobile: Yup.string().required('Harus di-isi'),
+// bisnisType: Yup.string().required('Harus dipilih'),
+// categoryPerusahaan: Yup.string().required('Harus dipilih'),
+// });
+// const defaultValues = {
+// company: '',
+// pejabatName: '',
+// PICName: '',
+// PICPosition: '',
+// address: '',
+// state: '',
+// city: '',
+// district: '',
+// subDistrict: '',
+// zip: '',
+// email: '',
+// emailSales: '',
+// emailFinance: '',
+// bank: '',
+// rekening: '',
+// accountNumber: '',
+// phone: '',
+// mobile: '',
+// };
+
+// export default CreateMerchant;
diff --git a/src/lib/merchant/components/InformasiVendor.jsx b/src/lib/merchant/components/InformasiVendor.jsx
new file mode 100644
index 00000000..90763029
--- /dev/null
+++ b/src/lib/merchant/components/InformasiVendor.jsx
@@ -0,0 +1,748 @@
+// import HookFormSelect from '@/core/components/elements/Select/HookFormSelect';
+// import cityApi from '@/lib/address/api/cityApi';
+// import stateApi from '@/lib/address/api/stateApi.js';
+// import districtApi from '@/lib/address/api/districtApi';
+// import subDistrictApi from '@/lib/address/api/subDistrictApi';
+// import { yupResolver } from '@hookform/resolvers/yup';
+// import React, {
+// useEffect,
+// useRef,
+// useState,
+// forwardRef,
+// useImperativeHandle,
+// } from 'react';
+// import ReCAPTCHA from 'react-google-recaptcha';
+// import { Controller, useForm } from 'react-hook-form';
+// import { toast } from 'react-hot-toast';
+// import * as Yup from 'yup';
+// import createMerchantApi from '../api/createMerchantApi';
+// import getMerchantApi from '../api/getMerchantApi';
+// import addressApi from '@/lib/address/api/addressApi';
+// import PageContent from '@/lib/content/components/PageContent';
+// import { useRouter } from 'next/router';
+// import useAuth from '@/core/hooks/useAuth';
+// import { Radio, RadioGroup, Stack, Checkbox, Button } from '@chakra-ui/react';
+// import { EyeIcon } from '@heroicons/react/24/outline';
+// import BottomPopup from '@/core/components/elements/Popup/BottomPopup';
+// import Image from 'next/image';
+// import ImageBanner from '~/components/ui/image';
+// import { ChevronRightIcon } from '@heroicons/react/24/outline';
+// import MobileView from '@/core/components/views/MobileView';
+// import DesktopView from '@/core/components/views/DesktopView';
+// import getFileBase64 from '@/core/utils/getFileBase64';
+// import odooApi from '~/libs/odooApi';
+// import { formatValue } from 'react-currency-input-field';
+// const InformasiVendor = forwardRef(({ handleIsError, isKonfirmasi }, ref) => {
+// const isError = (value) => {
+// // Logika menentukan error
+// const result = value ? true : false;
+// handleIsError(result); // Panggil handleIsError dari Merchant
+// return result;
+// };
+// const {
+// register,
+// handleSubmit,
+// formState: { errors },
+// control,
+// reset,
+// watch,
+// setValue,
+// getValues,
+// } = useForm({
+// resolver: yupResolver(validationSchema),
+// defaultValues,
+// });
+// const [categoryProduk, setCategoryProduk] = useState([]);
+// const [isExample, setIsExample] = useState(false);
+
+// const router = useRouter();
+
+// const auth = useAuth();
+// if (auth == false) {
+// router.push(`/login?next=${encodeURIComponent('/daftar-merchant')}`);
+// }
+// const dataTerhitungSejak = [
+// { value: 1, label: 'Terima PO' },
+// { value: 2, label: 'Barang Dikirimkan' },
+// { value: 3, label: 'Tukar Faktur' },
+// ];
+
+// const dataTempo = [
+// { value: 24, label: 'Tempo 14 Hari' },
+// { value: 25, label: 'Tempo 30 Hari' },
+// { value: 28, label: 'Tempo 60 Hari' },
+// { value: 31, label: 'Tempo 90 Hari' },
+// ];
+
+// const midIndex = Math.ceil(categoryProduk.length / 2);
+// const firstColumn = categoryProduk.slice(0, midIndex);
+// const secondColumn = categoryProduk.slice(midIndex);
+// const [kreditLimitFormat, setKreditLimitFormat] = useState();
+
+// useEffect(() => {
+// const loadData = async () => {
+// const data = await getMerchantApi();
+// if (data) {
+// reset({
+// hargaTayang: data.hargaTayang || '',
+// categoryProduk: data.categoryProduk || '',
+// merkDagang: data.merkDagang || '',
+// isPengajuanTempo: data.isPengajuanTempo || '',
+// tempoDuration: parseInt(data.tempoDuration) || '',
+// // kreditLimit: parseInt(data.kreditLimit) || '',
+// waktuPengiriman: data.waktuPengiriman || '',
+// terhitungSejak: parseInt(data.terhitungSejak) || '',
+// });
+// handleKreditLimitChange(data.kreditLimit || '');
+// setSelectedIds(watch('categoryProduk').split(',').map(Number));
+// }
+// };
+
+// loadData();
+// }, []);
+
+// useImperativeHandle(ref, () => () => {
+// handleSubmit(onSubmitHandler)();
+// });
+
+// const handleKreditLimitChange = (e) => {
+// let value = e.target?.value ? e.target.value : e;
+
+// // Hapus semua karakter non-numeric
+// value = value.replace(/[^\d]/g, '');
+
+// // Format angka sebagai Rupiah tanpa mengubah nilai sebenarnya
+// const formattedValue1 = formatValue({
+// value: value,
+// groupSeparator: '.',
+// decimalSeparator: ',',
+// prefix: 'Rp ',
+// });
+
+// setKreditLimitFormat(formattedValue1);
+// setValue('kreditLimit', formattedValue1);
+// };
+
+// const [selectedIds, setSelectedIds] = useState(
+// watch('categoryProduk')
+// ? watch('categoryProduk').split(',').map(Number)
+// : []
+// // form.categoryProduk ? form.categoryProduk.split(',').map(Number) : [] // Parse string menjadi array angka
+// // [] // Parse string menjadi array angka
+// );
+// const handleCheckboxChange = (id) => {
+// const updatedSelected = selectedIds.includes(id)
+// ? selectedIds.filter((selectedId) => selectedId !== id)
+// : [...selectedIds, id];
+
+// setSelectedIds(updatedSelected);
+
+// // Mengubah array kembali menjadi string yang dipisahkan oleh koma
+// setValue('categoryProduk', updatedSelected.join(','));
+// };
+
+// const isChecked = (id) => selectedIds.includes(id);
+
+// const handleCheckboxPortalChange = (value) => {
+// setValue('isPengajuanTempo', `${value}`);
+// };
+
+// useEffect(() => {
+// if (!isKonfirmasi) {
+// window.scrollTo({
+// top: 0,
+// behavior: 'smooth',
+// });
+// }
+// }, []);
+
+// useEffect(() => {
+// const loadCategories = async () => {
+// let dataCategories = await odooApi('GET', '/api/v1/category/tree');
+// const formattedCategories = dataCategories.map((category) => ({
+// id: category.id,
+// name: category.name,
+// }));
+// // Simpan hasil ke state
+// setCategoryProduk(formattedCategories);
+// };
+// loadCategories();
+// }, []);
+
+// const onSubmitHandler = async (values) => {
+// const toastId = toast.loading('Mengirimkan formulir merchant...');
+// const data = {
+// harga_tayang: values.hargaTayang,
+// categoryProduk: values.categoryProduk,
+// merk_dagang: values.merkDagang,
+// is_pengajuan_tempo: values.isPengajuanTempo,
+// tempo_duration: values.tempoDuration,
+// kredit_limit: values.kreditLimit,
+// waktu_pengiriman: values.waktuPengiriman,
+// terhitung_sejak: values.terhitungSejak,
+// };
+// const create_leads = await createMerchantApi({ data });
+// if (create_leads) {
+// toast.dismiss(toastId);
+// isError(false);
+// toast.success('Berhasil menambahkan data');
+// reset();
+// // router.push('/+');
+// } else {
+// toast.dismiss(toastId);
+// toast.error('Gagal menambahkan data');
+// }
+// };
+
+// // const DownLoadSurat = () => {
+// // download surat dari /public/file/Surat Pernyataan Nomor Rekening.docx
+// // };
+
+// if (!auth) {
+// return;
+// }
+
+// return (
+// <>
+// <BottomPopup
+// className=''
+// title='Contoh SPPKP'
+// active={isExample}
+// close={() => setIsExample(false)}
+// >
+// <div className='flex p-2'>
+// <Image
+// src='/images/NO-SPPKP-FORMAT-TEMPLATE.jpg'
+// alt='Contoh SPPKP'
+// className='w-full h-full '
+// width={800}
+// height={800}
+// quality={85}
+// />
+// </div>
+// </BottomPopup>
+// <DesktopView>
+// <div className='container flex flex-col items-star py-4 '>
+// <h2 className='text-xs md:text-title-sm font-semibold mb-6'>
+// Informasi Vendor
+// </h2>
+
+// <div className='w-full mt-4'>
+// <form
+// onSubmit={handleSubmit(onSubmitHandler)}
+// className='flex flex-col gap-4'
+// >
+// <div className='w-full flex flex-row'>
+// <div className='w-2/5'>
+// <label className='form-label text-nowrap'>
+// Harga Tayang (HET){' '}
+// <span className=' opacity-60'>(Opsional)</span>
+// </label>
+// </div>
+// <div className='w-3/5'>
+// <textarea
+// {...register('hargaTayang')}
+// placeholder='Jelaskan detail HET yang anda miliki'
+// type='textarea'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.hargaTayang?.message}
+// </div>
+// </div>
+// </div>
+
+// <div className={`flex flex-row justify-between items-start`}>
+// <div className='w-2/5 text-nowrap'>
+// <label
+// className={`form-label ${isKonfirmasi && 'text-wrap'}`}
+// >
+// Tipe Kategori Produk yang Digunakan
+// </label>
+// {!isKonfirmasi && (
+// <span className='text-xs opacity-60'>
+// Pilih kategori produk bisa lebih dari 1
+// </span>
+// )}
+// </div>
+// <div className='w-3/5 flex flex-col'>
+// <div className='flex flex-row justify-between gap-2'>
+// <div
+// className='flex flex-col gap-2'
+// // ref={categoryProdukRef}
+// >
+// {firstColumn.map((item) => (
+// <Checkbox
+// colorScheme='red'
+// key={item.id}
+// onChange={() => handleCheckboxChange(item.id)}
+// isChecked={isChecked(item.id)}
+// >
+// {item.name}
+// </Checkbox>
+// ))}
+// </div>
+// <div className='flex flex-col gap-2 '>
+// {secondColumn.map((item) => (
+// <Checkbox
+// colorScheme='red'
+// key={item.id}
+// isChecked={isChecked(item.id)}
+// onChange={() => handleCheckboxChange(item.id)}
+// >
+// {item.name}
+// </Checkbox>
+// ))}
+// </div>
+// </div>
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.categoryProduk?.message}
+// </div>
+// </div>
+// </div>
+
+// <div className='w-full flex flex-row'>
+// <div className='w-2/5'>
+// <label className='form-label text-nowrap'>Merk Dagang</label>
+// </div>
+// <div className='w-3/5'>
+// <input
+// {...register('merkDagang')}
+// placeholder='Merk 1, Merk 2, Merk 3'
+// type='text'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.merkDagang?.message}
+// </div>
+// </div>
+// </div>
+
+// <div className='flex flex-row justify-between items-start'>
+// <div className='w-2/5'>
+// <label className='form-label text-wrap '>
+// Apakah anda memiliki Form Pengajuan Tempo?
+// </label>
+// </div>
+// <div className='w-3/5 flex flex-col justify-start'>
+// <div className='flex gap-x-4'>
+// <RadioGroup
+// onChange={handleCheckboxPortalChange}
+// value={watch('isPengajuanTempo')}
+// >
+// <Stack direction='row'>
+// <Radio colorScheme='red' value='ada'>
+// Ya, ada
+// </Radio>
+// <Radio colorScheme='red' value='tidak'>
+// Tidak ada
+// </Radio>
+// </Stack>
+// </RadioGroup>
+// </div>
+
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.isPengajuanTempo?.message}
+// </div>
+// </div>
+// </div>
+
+// <div className='flex flex-row justify-between items-center'>
+// <div className='w-2/5'>
+// <label className='form-label text-nowrap'>Durasi Tempo</label>
+// {!isKonfirmasi && (
+// <span className='text-xs opacity-60'>
+// Pilih durasi tempo yang anda inginkan
+// </span>
+// )}
+// </div>
+// <div className='w-3/5 flex flex-col '>
+// <div className='flex flex-row items-center gap-3'>
+// <div className={`${!isKonfirmasi && 'w-[25%]'}`}>
+// <Controller
+// name='tempoDuration'
+// control={control}
+// render={(props) => (
+// <HookFormSelect
+// {...props}
+// options={dataTempo}
+// placeholder={'Pilih Durasi Tempo'}
+// />
+// )}
+// />
+
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.tempoDuration?.message}
+// </div>
+// </div>
+// </div>
+// </div>
+// </div>
+
+// <div className='w-full flex flex-row'>
+// <div className='w-2/5'>
+// <label className='form-label text-nowrap'>
+// Jumlah Kredit Limit
+// </label>
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// isi dengan kredit limit perusahaan anda
+// </span>
+// )}
+// </div>
+// <div className='w-3/5'>
+// <input
+// value={kreditLimitFormat}
+// onChange={handleKreditLimitChange}
+// placeholder='Masukkan jumlah kredit limit'
+// type='text'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.kreditLimit?.message}
+// </div>
+// </div>
+// </div>
+
+// <div className='w-full flex flex-row'>
+// <div className='w-2/5'>
+// <label className='form-label text-nowrap'>
+// Waktu Pengiriman
+// </label>
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// isi dengan waktu pengiriman anda
+// </span>
+// )}
+// </div>
+// <div className='w-3/5 flex flex-row gap-2'>
+// <div className='w-1/3'>
+// <input
+// {...register('waktuPengiriman')}
+// placeholder='Masukkan waktu pengiriman'
+// type='text'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.waktuPengiriman?.message}
+// </div>
+// </div>
+// <div className='w-2/3 '>
+// <div className='flex flex-row items-center gap-2'>
+// <label className=' text-nowrap text-sm opacity-70 italic'>
+// terhitung sejak
+// </label>
+
+// <Controller
+// name='terhitungSejak'
+// control={control}
+// render={(props) => (
+// <HookFormSelect
+// {...props}
+// options={dataTerhitungSejak}
+// placeholder={'waktu pengiriman'}
+// />
+// )}
+// />
+// </div>
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.terhitungSejak?.message}
+// </div>
+// </div>
+// </div>
+// </div>
+
+// <div className=''>
+// {/* <div>
+// <ReCAPTCHA
+// ref={recaptchaRef}
+// sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE}
+// />
+// </div> */}
+// </div>
+// <div className='flex justify-end'>
+// {/* <Button
+// colorScheme='red'
+// className='w-full md:w-fit'
+// type='submit'
+// >
+// Daftar Merchant{' '}
+// {<ChevronRightIcon className='w-5' color='white' />}
+// </Button> */}
+// {!isKonfirmasi && (
+// <div>
+// <span className='text-xs opacity-60'>
+// *Pastikan data yang anda masukan sudah benar dan sesuai
+// </span>
+// <button
+// type='submit'
+// className='btn-light bg-red-500 rounded-lg text-white w-fit py-2 px-4 md:w-fit mt-2 ml-0 md:ml-auto flex justify-between hover:bg-red-400'
+// >
+// <span className={` `}>Langkah Selanjutnya</span>
+// {<ChevronRightIcon className='w-5' />}
+// </button>
+// </div>
+// )}
+// </div>
+// </form>
+// <PageContent path='/daftar-merchant' />
+// </div>
+// </div>
+// </DesktopView>
+// <MobileView>
+// <div className='container flex flex-col items-star py-4'>
+// {!isKonfirmasi && (
+// <h2 className='font-semibold mb-6 text-xl'>Informasi Vendor</h2>
+// )}
+
+// <div className='w-full mt-4'>
+// <form
+// onSubmit={handleSubmit(onSubmitHandler)}
+// className='flex flex-col gap-4'
+// >
+// <div className='w-full flex flex-col'>
+// <div className='w-full'>
+// <label className='form-label text-nowrap'>
+// Harga Tayang (HET){' '}
+// <span className=' opacity-60'>(Opsional)</span>
+// </label>
+// <input
+// {...register('hargaTayang')}
+// placeholder='Jelaskan detail HET yang anda miliki'
+// type='textarea'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.hargaTayang?.message}
+// </div>
+// </div>
+// </div>
+// <div
+// className={`flex flex-col gap-2 justify-between ${
+// isKonfirmasi ? 'items-start' : 'items-start'
+// }`}
+// >
+// <label className='form-label '>
+// Tipe Kategori Produk yang Digunakan
+// </label>
+// <div className='flex flex-col justify-between gap-2 '>
+// <div className='flex flex-col gap-2'>
+// {firstColumn.map((item) => (
+// <Checkbox
+// size='sm'
+// colorScheme='red'
+// key={item.id}
+// onChange={() => handleCheckboxChange(item.id)}
+// isChecked={isChecked(item.id)}
+// >
+// {item.name}
+// </Checkbox>
+// ))}
+// </div>
+// <div className='flex flex-col gap-2'>
+// {secondColumn.map((item) => (
+// <Checkbox
+// size='sm'
+// colorScheme='red'
+// key={item.id}
+// isChecked={isChecked(item.id)}
+// onChange={() => handleCheckboxChange(item.id)}
+// >
+// {item.name}
+// </Checkbox>
+// ))}
+// </div>
+// </div>
+
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.categoryProduk?.message}
+// </div>
+// </div>
+// <div className='w-full flex flex-col'>
+// <div className='w-full'>
+// <label className='form-label text-nowrap'>Merk Dagang</label>
+// <input
+// {...register('merkDagang')}
+// placeholder='Merk 1, Merk 2, Merk 3'
+// type='text'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.merkDagang?.message}
+// </div>
+// </div>
+// </div>
+// <div className='w-full flex flex-col'>
+// <label className='form-label text-nowrap'>
+// Apakah anda memiliki Form Pengajuan Tempo?
+// </label>
+// <div className='flex gap-x-4'>
+// <RadioGroup
+// onChange={handleCheckboxPortalChange}
+// value={watch('isPengajuanTempo')}
+// >
+// <Stack direction='row'>
+// <Radio colorScheme='red' value='ada'>
+// Ya, ada
+// </Radio>
+// <Radio colorScheme='red' value='tidak'>
+// Tidak ada
+// </Radio>
+// </Stack>
+// </RadioGroup>
+// </div>
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.isPengajuanTempo?.message}
+// </div>
+// </div>
+
+// <div className='w-full flex flex-col'>
+// <label className='form-label text-nowrap'>Durasi Tempo</label>
+// <Controller
+// name='tempoDuration'
+// control={control}
+// render={(props) => (
+// <HookFormSelect
+// {...props}
+// options={dataTempo}
+// placeholder={'Pilih Durasi Tempo'}
+// />
+// )}
+// />
+
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.tempoDuration?.message}
+// </div>
+// {!isKonfirmasi && (
+// <span className='text-xs opacity-60'>
+// Pilih durasi tempo yang anda inginkan
+// </span>
+// )}
+// </div>
+// <div className='w-full flex flex-col'>
+// <label className='form-label text-nowrap'>
+// Jumlah Kredit Limit
+// </label>
+// <input
+// value={kreditLimitFormat}
+// onChange={handleKreditLimitChange}
+// placeholder='Masukkan jumlah kredit limit'
+// type='text'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.kreditLimit?.message}
+// </div>
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// isi dengan kredit limit perusahaan anda
+// </span>
+// )}
+// </div>
+// <div className='w-full flex flex-col'>
+// <label className='form-label text-nowrap'>
+// {' '}
+// Waktu Pengiriman
+// </label>
+// <div className='w-full flex flex-row gap-2'>
+// <div className='w-1/3'>
+// <input
+// {...register('waktuPengiriman')}
+// placeholder='Masukkan waktu pengiriman'
+// type='text'
+// className='form-input'
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.waktuPengiriman?.message}
+// </div>
+// </div>
+// <div className='w-2/3 '>
+// <div className='flex flex-row items-center gap-2'>
+// <label className=' text-nowrap text-sm opacity-70 italic'>
+// terhitung sejak
+// </label>
+
+// <Controller
+// name='terhitungSejak'
+// control={control}
+// render={(props) => (
+// <HookFormSelect
+// {...props}
+// options={dataTerhitungSejak}
+// placeholder={'waktu pengiriman'}
+// />
+// )}
+// />
+// </div>
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.terhitungSejak?.message}
+// </div>
+// </div>
+// </div>
+// {!isKonfirmasi && (
+// <span className='opacity-65 text-xs'>
+// isi dengan waktu pengiriman anda
+// </span>
+// )}
+// </div>
+
+// <div className=''>
+// {/* <div>
+// <ReCAPTCHA
+// ref={recaptchaRef}
+// sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE}
+// />
+// </div> */}
+// </div>
+// <div className='flex justify-center w-full '>
+// {/* <Button
+// colorScheme='red'
+// className='w-full md:w-fit'
+// type='submit'
+// >
+// Daftar Merchant{' '}
+// {<ChevronRightIcon className='w-5' color='white' />}
+// </Button> */}
+// {!isKonfirmasi && (
+// <div className='w-full'>
+// <span className='text-xs opacity-60'>
+// *Pastikan data yang anda masukan sudah benar dan sesuai
+// </span>
+// <button
+// type='submit'
+// className='btn-light bg-red-500 rounded-lg text-white w-full py-2 px-4 md:w-fit mt-2 ml-0 md:ml-auto flex justify-center hover:bg-red-400'
+// >
+// <span className={` `}>Langkah Selanjutnya</span>
+// {<ChevronRightIcon className='w-5' />}
+// </button>
+// </div>
+// )}
+// </div>
+// </form>
+// <PageContent path='/daftar-merchant' />
+// </div>
+// </div>
+// </MobileView>
+// </>
+// );
+// });
+// const validationSchema = Yup.object().shape({
+// categoryProduk: Yup.string().required('Harus di-pilih'),
+// merkDagang: Yup.string().required('Harus di-isi'),
+// isPengajuanTempo: Yup.string().required('Harus di-pilih'),
+// tempoDuration: Yup.string().required('Harus di-pilih'),
+// kreditLimit: Yup.string().required('Harus di-isi'),
+// waktuPengiriman: Yup.string().required('Harus di-isi'),
+// terhitungSejak: Yup.string().required('Harus di-pilih'),
+// });
+// const defaultValues = {
+// categoryProduk: '',
+// merkDagang: '',
+// isPengajuanTempo: '',
+// tempoDuration: '',
+// kreditLimit: '',
+// waktuPengiriman: '',
+// terhitungSejak: '',
+// };
+
+// export default InformasiVendor;
diff --git a/src/lib/merchant/components/Konfirmasi.jsx b/src/lib/merchant/components/Konfirmasi.jsx
new file mode 100644
index 00000000..f7d552ac
--- /dev/null
+++ b/src/lib/merchant/components/Konfirmasi.jsx
@@ -0,0 +1,224 @@
+// import React, { useState, useEffect, useMemo, useRef } from 'react';
+// import { Controller, set, useForm } from 'react-hook-form';
+// import HookFormSelect from '@/core/components/elements/Select/HookFormSelect';
+// // import ProgressBar from '@ramonak/react-progress-bar';
+// import {
+// Button,
+// Checkbox,
+// Spinner,
+// Tooltip,
+// UseToastOptions,
+// } from '@chakra-ui/react';
+// import odooApi from '~/libs/odooApi';
+// import { toast } from 'react-hot-toast';
+// import getFileBase64 from '@/core/utils/getFileBase64';
+// import { CheckCircleIcon } from '@heroicons/react/24/outline';
+// import InformasiPerusahaan from './InformasiPerusahaan';
+// import InformasiVendor from './InformasiVendor';
+// import SyaratDagang from './SyaratDagang';
+// import Dokumen from './Dokumen';
+// import createMerchantApi from '../api/createMerchantApi';
+// import useDevice from '@/core/hooks/useDevice';
+// import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/24/outline';
+// import { useRouter } from 'next/router';
+// const Konfirmasi = ({ chekValid, buttonSubmitClick }) => {
+// const { control, watch, setValue, getValues, reset } = useForm();
+// const { isDesktop, isMobile } = useDevice();
+// const [isOpenInformasi, setIsOpenInformasi] = useState(true);
+// const [isOpenKontak, setIsOpenKontak] = useState(false);
+// const [isOpenPengiriman, setIsOpenPengiriman] = useState(false);
+// const [isOpenKonfirmasi, setIsOpenKonfirmasi] = useState(false);
+// const formRef = useRef(null);
+// const router = useRouter();
+// const handleDaftarMerchant = () => {
+// if (formRef.current) {
+// formRef.current(); // Memicu submit form di InformasiPerusahaan
+// }
+// };
+// const handleIsError = async (value) => {
+// if (!value) {
+// // goToNextStep();
+// const toastId = toast.loading('Mengirimkan formulir merchant...');
+// const data = {
+// merchant_request: true,
+// };
+// const create_leads = await createMerchantApi({ data });
+// if (create_leads) {
+// toast.dismiss(toastId);
+// toast.success('Berhasil medaftarkan merchant');
+// reset();
+// // router.push('/+');
+// } else {
+// toast.dismiss(toastId);
+// toast.error('Gagal menambahkan data');
+// }
+// }
+// reset();
+// router.push('/');
+// };
+
+// return (
+// <>
+// {isDesktop && (
+// <>
+// <form className='flex mt-4 flex-col w-full '>
+// <div className='w-full grid grid-cols-[1fr_auto_1fr] gap-5'>
+// <div className='w-full flex flex-col gap-5 '>
+// <div className=''>
+// <InformasiPerusahaan
+// isKonfirmasi={true}
+// ref={formRef}
+// handleIsError={handleIsError}
+// />
+// </div>
+// <div className='h-px bg-gray-300'></div>
+// <div className=''>
+// <SyaratDagang
+// isKonfirmasi={true}
+// ref={formRef}
+// handleIsError={handleIsError}
+// />
+// </div>
+// </div>
+
+// <div className='w-px bg-gray-300'></div>
+// <div className='w-full grid grid-rows-[1fc_auto_1fc] gap-5'>
+// <div className=''>
+// <InformasiVendor
+// isKonfirmasi={true}
+// ref={formRef}
+// handleIsError={handleIsError}
+// />
+// </div>
+// <div className='h-px bg-gray-300'></div>
+// <div>
+// <Dokumen
+// isKonfirmasi={true}
+// ref={formRef}
+// handleIsError={handleIsError}
+// />
+// </div>
+// </div>
+// </div>
+// </form>
+
+// <div className='flex flex-col items-end justify-end gap-2'>
+// <span className='text-xs opacity-60'>
+// *Pastikan data yang anda masukan sudah benar dan sesuai
+// </span>
+// <Button
+// colorScheme='red'
+// w='36'
+// onClick={handleDaftarMerchant} // Memicu form submit
+// >
+// Daftar Merchant
+// </Button>
+// </div>
+// </>
+// )}
+// {isMobile && (
+// <form className='flex mt-8 py-4 flex-col w-full gap-4'>
+// <div className='flex flex-col gap-4'>
+// <div className='flex flex-row justify-between items-center'>
+// <div className='flex flex-col justify-center items-start'>
+// <p className='font-semibold text-lg'>Informasi Perusahaan</p>
+// {/* <span className='text-xs opacity-70'>
+// Pastikan informasi usaha yang anda masukkan sudah sesuai
+// dengan data perusahaan anda
+// </span> */}
+// </div>
+// <div className='p-2 bg-gray-200'>
+// {isOpenInformasi ? (
+// <ChevronUpIcon
+// className='w-4'
+// onClick={() => setIsOpenInformasi(!isOpenInformasi)}
+// />
+// ) : (
+// <ChevronDownIcon
+// className='w-4'
+// onClick={() => setIsOpenInformasi(!isOpenInformasi)}
+// />
+// )}
+// </div>
+// </div>
+// {isOpenInformasi && <InformasiPerusahaan isKonfirmasi={true} />}
+// <div className='h-[2px] bg-gray-300 w-[120%] inset-0 mt-4 mb-4 relative transform -translate-x-5'></div>
+// </div>
+// <div className='flex flex-col gap-4'>
+// <div className='flex flex-row justify-between'>
+// <p className='font-semibold text-lg'>Informasi Vendor</p>
+// <div className='p-2 bg-gray-200'>
+// {isOpenKontak ? (
+// <ChevronUpIcon
+// className='w-4'
+// onClick={() => setIsOpenKontak(!isOpenKontak)}
+// />
+// ) : (
+// <ChevronDownIcon
+// className='w-4'
+// onClick={() => setIsOpenKontak(!isOpenKontak)}
+// />
+// )}
+// </div>
+// </div>
+// {isOpenKontak && <InformasiVendor isKonfirmasi={true} />}
+// <div className='h-[2px] bg-gray-300 w-[120%] inset-0 mt-4 mb-4 relative transform -translate-x-5'></div>
+// </div>
+// <div className='flex flex-col gap-4'>
+// <div className='flex flex-row justify-between'>
+// <p className='font-semibold text-lg'>Syarat Perdagangan</p>
+// <div className='p-2 bg-gray-200'>
+// {isOpenPengiriman ? (
+// <ChevronUpIcon
+// className='w-4'
+// onClick={() => setIsOpenPengiriman(!isOpenPengiriman)}
+// />
+// ) : (
+// <ChevronDownIcon
+// className='w-4'
+// onClick={() => setIsOpenPengiriman(!isOpenPengiriman)}
+// />
+// )}
+// </div>
+// </div>
+// {isOpenPengiriman && <SyaratDagang isKonfirmasi={true} />}
+// <div className='h-[2px] bg-gray-300 w-[120%] inset-0 mt-4 mb-4 relative transform -translate-x-5'></div>
+// </div>
+// <div className='flex flex-col gap-4'>
+// <div className='flex flex-row justify-between'>
+// <p className='font-semibold text-lg'>Dokumen</p>
+// <div className='p-2 bg-gray-200'>
+// {isOpenKonfirmasi ? (
+// <ChevronUpIcon
+// className='w-4'
+// onClick={() => setIsOpenKonfirmasi(!isOpenKonfirmasi)}
+// />
+// ) : (
+// <ChevronDownIcon
+// className='w-4'
+// onClick={() => setIsOpenKonfirmasi(!isOpenKonfirmasi)}
+// />
+// )}
+// </div>
+// </div>
+// {isOpenKonfirmasi && <Dokumen isKonfirmasi={true} />}
+// </div>
+// <div className='flex flex-col items-end justify-end gap-2'>
+// <span className='text-xs opacity-60'>
+// *Pastikan data yang anda masukan sudah benar dan sesuai
+// </span>
+// <Button
+// colorScheme='red'
+// w='full'
+// onClick={handleDaftarMerchant} // Memicu form submit
+// >
+// Daftar Merchant
+// </Button>
+// </div>
+// </form>
+// )}
+// </>
+// );
+// };
+
+// export default Konfirmasi;
diff --git a/src/lib/merchant/components/Merchant.jsx b/src/lib/merchant/components/Merchant.jsx
new file mode 100644
index 00000000..382db064
--- /dev/null
+++ b/src/lib/merchant/components/Merchant.jsx
@@ -0,0 +1,147 @@
+// import React from 'react';
+// import { useMemo, useState, useEffect, useRef } from 'react';
+// import Image from '~/components/ui/image';
+// import InformasiPerusahaan from './InformasiPerusahaan';
+// import InformasiVendor from './InformasiVendor';
+// import SyaratDagang from './SyaratDagang';
+// import Dokumen from './Dokumen';
+// import Konfirmasi from './Konfirmasi';
+// import { getAuth } from '~/libs/auth';
+// import { setAuth } from '@/core/utils/auth';
+// import useAuth from '@/core/hooks/useAuth';
+// import { useRouter } from 'next/router';
+// import { Controller, useForm } from 'react-hook-form';
+// import { ChevronRightIcon, ChevronLeftIcon } from '@heroicons/react/24/outline';
+// import { Button, Checkbox, Spinner, Tooltip } from '@chakra-ui/react';
+// import clsxm from '~/libs/clsxm';
+// import { toast } from 'react-hot-toast';
+// import useDevice from '@/core/hooks/useDevice';
+// import odooApi from '~/libs/odooApi';
+// import BottomPopup from '@/core/components/elements/Popup/BottomPopup';
+// import PageContent from '@/lib/content/components/PageContent';
+// const Merchant = () => {
+// const { isDesktop, isMobile } = useDevice();
+// const [currentStep, setCurrentStep] = React.useState(0);
+// const NUMBER_OF_STEPS = 5;
+// const [isLoading, setIsLoading] = useState(false);
+// const [bigData, setBigData] = useState();
+// const [idTempo, setIdTempo] = useState(0);
+// const { control, watch, setValue } = useForm();
+// const auth = useAuth();
+// const router = useRouter();
+// const [BannerTempo, setBannerTempo] = useState();
+// const [notValid, setNotValid] = useState(false);
+// const [buttonSubmitClick, setButtonSubmitClick] = useState(false);
+
+// const [error, setError] = useState(false);
+
+// const handleIsError = (value) => {
+// if (!value) {
+// goToNextStep();
+// }
+// setError(value); // Memperbarui state berdasarkan isError
+// };
+// const stepDivs = [
+// <InformasiPerusahaan
+// handleIsError={handleIsError}
+// buttonSubmitClick={buttonSubmitClick}
+// />,
+// <InformasiVendor
+// handleIsError={handleIsError}
+// buttonSubmitClick={buttonSubmitClick}
+// />,
+// <SyaratDagang
+// handleIsError={handleIsError}
+// buttonSubmitClick={buttonSubmitClick}
+// />,
+// <Dokumen
+// handleIsError={handleIsError}
+// buttonSubmitClick={buttonSubmitClick}
+// />,
+// <Konfirmasi handleIsError={handleIsError} />,
+// ];
+
+// const stepLabels = [
+// 'informasi_perusahaan',
+// 'kontak_person',
+// 'Pengiriman',
+// 'Referensi',
+// 'Dokumen',
+// 'Konfirmasi',
+// ];
+
+// useEffect(() => {
+// window.scrollTo({
+// top: 0,
+// behavior: 'smooth',
+// });
+// }, [currentStep]);
+
+// useEffect(() => {
+// <InformasiPerusahaan buttonSubmitClick={buttonSubmitClick} />;
+// }, [buttonSubmitClick]);
+
+// const goToNextStep = () => {
+// setCurrentStep((prev) => (prev === NUMBER_OF_STEPS - 1 ? prev : prev + 1));
+// };
+
+// const goPrevStep = () => {
+// setCurrentStep((prev) => (prev === NUMBER_OF_STEPS - 1 ? prev : prev - 1));
+// };
+
+// useEffect(() => {
+// const getBanner = async () => {
+// const get = await odooApi('GET', '/api/v1/banner?type=banner-form-tempo');
+// // setBannerTempo(get[0].image);
+// setBannerTempo(
+// 'https://erp.indoteknik.com/api/image/x_banner.banner/x_banner_image/431'
+// );
+// };
+// getBanner();
+// }, []);
+// return (
+// <>
+// <div className='container flex flex-col items-center '>
+// {BannerTempo && (
+// <Image
+// src={BannerTempo}
+// alt='FORM Tempo'
+// width={500}
+// height={160}
+// className='w-full mt-6'
+// />
+// )}
+// <h1 className=' font-semibold text-center mb-6'>Form Merchant</h1>
+// <p
+// className={`text-center ${
+// isMobile ? 'w-full text-sm' : 'w-3/4 mb-4'
+// }`}
+// >
+// Pembayaran tempo adalah layanan pembayaran berjangka yang difasilitasi
+// indoteknik.com untuk konsumen akun bisnis yang terdaftar dengan waktu
+// pembayaran mulai dari 7, 14, 21 hingga 30 Hari.
+// </p>
+// </div>
+// <div
+// className={`h-[2px] w-full ${isMobile ? 'mt-4' : 'mb-4'} bg-gray_r-3`}
+// />
+
+// <div className={`container ${isMobile ? 'mt-4' : ''} flex flex-col`}>
+// <div>{stepDivs[currentStep]}</div>
+// {isDesktop && <section className='flex gap-10 mt-10'></section>}
+// {isMobile && (
+// <div className='h-[2px] bg-gray-300 w-[120%] inset-0 mt-4 mb-4 relative transform -translate-x-5'></div>
+// )}
+// <div
+// className={`flex ${
+// isMobile
+// ? 'flex-col justify-start items-start'
+// : 'flex-col justify-end items-end'
+// } mb-8 gap-2`}
+// ></div>
+// </div>
+// </>
+// );
+// };
+
+// export default Merchant;
diff --git a/src/lib/merchant/components/SyaratDagang.jsx b/src/lib/merchant/components/SyaratDagang.jsx
new file mode 100644
index 00000000..a3060e23
--- /dev/null
+++ b/src/lib/merchant/components/SyaratDagang.jsx
@@ -0,0 +1,822 @@
+// import HookFormSelect from '@/core/components/elements/Select/HookFormSelect';
+// import cityApi from '@/lib/address/api/cityApi';
+// import stateApi from '@/lib/address/api/stateApi.js';
+// import districtApi from '@/lib/address/api/districtApi';
+// import subDistrictApi from '@/lib/address/api/subDistrictApi';
+// import { yupResolver } from '@hookform/resolvers/yup';
+// import React, {
+// useEffect,
+// useRef,
+// useState,
+// forwardRef,
+// useImperativeHandle,
+// } from 'react';
+// import ReCAPTCHA from 'react-google-recaptcha';
+// import { Controller, useForm } from 'react-hook-form';
+// import { toast } from 'react-hot-toast';
+// import * as Yup from 'yup';
+// import createMerchantApi from '../api/createMerchantApi';
+// import getMerchantApi from '../api/getMerchantApi';
+// import addressApi from '@/lib/address/api/addressApi';
+// import PageContent from '@/lib/content/components/PageContent';
+// import { useRouter } from 'next/router';
+// import useAuth from '@/core/hooks/useAuth';
+// import { Radio, RadioGroup, Stack, Checkbox, Button } from '@chakra-ui/react';
+// import { EyeIcon } from '@heroicons/react/24/outline';
+// import BottomPopup from '@/core/components/elements/Popup/BottomPopup';
+// import Image from 'next/image';
+// import ImageBanner from '~/components/ui/image';
+// import { ChevronRightIcon } from '@heroicons/react/24/outline';
+// import MobileView from '@/core/components/views/MobileView';
+// import DesktopView from '@/core/components/views/DesktopView';
+// import getFileBase64 from '@/core/utils/getFileBase64';
+// import odooApi from '~/libs/odooApi';
+// const SyaratDagang = forwardRef(({ handleIsError, isKonfirmasi }, ref) => {
+// const isError = (value) => {
+// // Logika menentukan error
+// const result = value ? true : false;
+// handleIsError(result); // Panggil handleIsError dari Merchant
+// return result;
+// };
+// const {
+// register,
+// handleSubmit,
+// formState: { errors },
+// control,
+// reset,
+// watch,
+// setValue,
+// getValues,
+// } = useForm({
+// resolver: yupResolver(validationSchema),
+// defaultValues,
+// });
+// const [bigData, setbigData] = useState([]);
+// const [isExample, setIsExample] = useState(false);
+
+// const recaptchaRef = useRef(null);
+// const router = useRouter();
+
+// useEffect(() => {
+// const loadData = async () => {
+// const data = await getMerchantApi();
+// setbigData(data);
+// if (data) {
+// reset({
+// isKembaliBarang: data.isKembaliBarang || '',
+// textReturn: data.textReturn || '',
+// tenggatWaktu: (() => {
+// const waktu = data.tenggatWaktu
+// ? data.tenggatWaktu.split(' ')[0]
+// : '';
+// if (waktu === '14') return '14';
+// if (waktu === '30') return '30';
+// return 'custom';
+// })(),
+// customTenggatWaktu: (() => {
+// if (watch('tenggatWaktu') === 'custom')
+// return data.tenggatWaktu ? data.tenggatWaktu : '';
+// return '';
+// })(),
+// sertifikatProduk: data.sertifikatProduk || '',
+// customSertifikatProduk: data.customSertifikatProduk || '',
+// tempoGaransi: parseInt(data.tempoGaransi) || '',
+// explainGaransi: data.explainGaransi || '',
+// minimumPembelian:
+// data.isOrderQuantity == 'tidak' ? '' : data.minimumPembelian || '',
+// isOrderQuantity: data.isOrderQuantity || '',
+// });
+// // handleKreditLimitChange(data.kreditLimit);
+// if (watch('sertifikatProduk') != false) {
+// setSelectedIds(watch('sertifikatProduk').split(',').map(Number));
+// }
+// if (watch('customSertifikatProduk')) {
+// // setSelectedIds([...selectedIds, 4]);
+// }
+// }
+// };
+
+// loadData();
+// }, []);
+// useEffect(() => {
+// if (!isKonfirmasi) {
+// window.scrollTo({
+// top: 0,
+// behavior: 'smooth',
+// });
+// }
+// }, []);
+// useImperativeHandle(ref, () => () => {
+// handleSubmit(onSubmitHandler)();
+// });
+// const auth = useAuth();
+// if (auth == false) {
+// router.push(`/login?next=${encodeURIComponent('/daftar-merchant')}`);
+// }
+
+// const dataGaransi = [
+// { value: 1, label: '6 Bulan Garansi' },
+// { value: 2, label: '1 Tahun Garansi' },
+// { value: 3, label: '2 Tahun Garansi' },
+// ];
+// const dataMinimumOrderQuantity = [
+// { value: 'dus', label: 'Dus' },
+// { value: 'lusin', label: 'Lusin' },
+// { value: 'minimum pembelian', label: 'Minimum pembelian' },
+// ];
+
+// const category_produk = [
+// { id: 0, name: 'TKDN' },
+// { id: 1, name: 'SNI' },
+// { id: 2, name: 'K3L' },
+// { id: 3, name: '' },
+// ];
+
+// const [selectedIds, setSelectedIds] = useState(
+// watch('sertifikatProduk')
+// ? watch('sertifikatProduk').split(',').map(Number)
+// : []
+// // form.sertifikatProduk ? form.sertifikatProduk.split(',').map(Number) : [] // Parse string menjadi array angka
+// // [] // Parse string menjadi array angka
+// );
+// const handleCheckboxChange = (id) => {
+// const updatedSelected = selectedIds.includes(id)
+// ? selectedIds.filter((selectedId) => selectedId !== id)
+// : [...selectedIds, id];
+
+// setSelectedIds(updatedSelected);
+
+// // Mengubah array kembali menjadi string yang dipisahkan oleh koma
+// setValue('sertifikatProduk', updatedSelected.join(','));
+// };
+// const custom_sertifikat_produk_handle = () => {
+// const updatedSelected = [...selectedIds, 3];
+
+// setSelectedIds(updatedSelected);
+
+// // Mengubah array kembali menjadi string yang dipisahkan oleh koma
+// setValue('sertifikatProduk', updatedSelected.join(','));
+// };
+
+// const isChecked = (id) => selectedIds.includes(id);
+
+// const handleCheckboxReturChange = (value) => {
+// setValue('isKembaliBarang', `${value}`);
+// };
+// const handleCheckboxOrderQuantityChange = (value) => {
+// setValue('isOrderQuantity', `${value}`);
+// };
+
+// const handleCheckboxTenggatWaktuChange = (value) => {
+// setValue('tenggatWaktu', `${value}`);
+// };
+
+// const onSubmitHandler = async (values) => {
+// const toastId = toast.loading('Mengirimkan formulir merchant...');
+// const data = {
+// is_kembali_barang: values.isKembaliBarang,
+// text_return: values.textReturn,
+// tenggat_waktu: values.tenggatWaktu,
+// custom_tenggat_waktu: values.customTenggatWaktu,
+// sertifikat_produk: values.sertifikatProduk,
+// custom_sertifikat_produk:
+// values.customSertifikatProduk == ''
+// ? false
+// : values.customSertifikatProduk,
+// tempo_garansi: values.tempoGaransi,
+// explain_garansi: values.explainGaransi,
+// is_order_quantity: values.isOrderQuantity,
+// minimum_pembelian: values.minimumPembelian,
+// };
+// const create_leads = await createMerchantApi({ data });
+// if (create_leads) {
+// toast.dismiss(toastId);
+// toast.success('Berhasil menambahkan data');
+// isError(false);
+// reset();
+// // router.push('/');
+// } else {
+// toast.dismiss(toastId);
+// toast.error('Gagal menambahkan data');
+// }
+// };
+
+// // const DownLoadSurat = () => {
+// // download surat dari /public/file/Surat Pernyataan Nomor Rekening.docx
+// // };
+
+// if (!auth) {
+// return;
+// }
+// // Tetap di bagian atas, tidak boleh ada kondisi sebelum hook
+
+// return (
+// <>
+// <BottomPopup
+// className=''
+// title='Contoh SPPKP'
+// active={isExample}
+// close={() => setIsExample(false)}
+// >
+// <div className='flex p-2'>
+// <Image
+// src='/images/NO-SPPKP-FORMAT-TEMPLATE.jpg'
+// alt='Contoh SPPKP'
+// className='w-full h-full '
+// width={800}
+// height={800}
+// quality={85}
+// />
+// </div>
+// </BottomPopup>
+// <DesktopView>
+// <div
+// className={`container flex flex-col h-fit items-start ${
+// !isKonfirmasi && 'py-4'
+// }`}
+// >
+// <h2 className='text-xs md:text-title-sm font-semibold mb-6'>
+// Syarat Perdagangan
+// </h2>
+
+// <div className='w-full mt-4 '>
+// <form
+// onSubmit={handleSubmit(onSubmitHandler)}
+// className='flex flex-col gap-4'
+// >
+// <div className='flex flex-row justify-between items-start'>
+// <div className='w-2/5'>
+// <label className='form-label text-wrap '>
+// Syarat Pengembalian Barang
+// </label>
+// </div>
+// <div className='w-3/5 flex flex-row justify-start'>
+// <div className='flex gap-x-4 flex-col w-full'>
+// <RadioGroup
+// onChange={handleCheckboxReturChange}
+// value={watch('isKembaliBarang')}
+// >
+// <Stack direction='column'>
+// <div className='flex flex-row text-nowrap gap-2'>
+// <Radio colorScheme='red' value='ya'>
+// Ya, dapat diretur
+// </Radio>
+// {watch('isKembaliBarang') == 'ya' && (
+// <textarea
+// {...register('textReturn')}
+// placeholder='jelaskan syarat pengembalian'
+// type='textarea'
+// className='form-input w-full'
+// />
+// )}
+// </div>
+// <Radio colorScheme='red' value='tidak'>
+// Tidak dapat diretur
+// </Radio>
+// </Stack>
+// </RadioGroup>
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.isKembaliBarang?.message}
+// </div>
+// </div>
+// </div>
+// </div>
+
+// <div className='flex flex-row justify-between items-start'>
+// <div className='w-2/5'>
+// <label className='form-label text-wrap '>
+// Tenggat Waktu Perubahan Harga
+// </label>
+// </div>
+// <div className='w-3/5 flex flex-row justify-start'>
+// <div className='flex gap-x-4 flex-col w-full'>
+// <RadioGroup
+// onChange={handleCheckboxTenggatWaktuChange}
+// value={watch('tenggatWaktu')}
+// >
+// <Stack direction='column'>
+// <Radio
+// colorScheme='red'
+// value='14'
+// onChange={() => setValue('customTenggatWaktu', ' ')}
+// >
+// 14 hari sejak data dikirimkan
+// </Radio>
+// <Radio
+// colorScheme='red'
+// value='30'
+// onChange={() => setValue('customTenggatWaktu', ' ')}
+// >
+// 30 hari sejak data dikirimkan
+// </Radio>
+// <div className='flex flex-row gap-2'>
+// <Radio colorScheme='red' value='custom'></Radio>
+// <input
+// {...register('customTenggatWaktu')}
+// placeholder='Masukkan jumlah hari untuk tenggat waktu'
+// type='text'
+// onFocus={() => setValue('tenggatWaktu', 'custom')}
+// className='form-input mt-2'
+// />
+// </div>
+// </Stack>
+// </RadioGroup>
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.tenggatWaktu?.message}
+// </div>
+// </div>
+// </div>
+// </div>
+
+// <div className={`flex flex-row justify-between items-start`}>
+// <div className='w-2/5 text-nowrap'>
+// <label
+// className={`form-label ${isKonfirmasi && 'text-wrap'}`}
+// >
+// Dokumen/Sertifikat yang Dimiliki Oleh Brand
+// </label>
+// {!isKonfirmasi && (
+// <span className='text-xs opacity-60'>
+// Pilih dokumen/sertifikat bisa lebih dari 1
+// </span>
+// )}
+// </div>
+// <div className='w-3/5 flex flex-col'>
+// <div className='flex flex-row justify-between w-full'>
+// <div
+// className='flex flex-col gap-2 w-full'
+// // ref={categoryProdukRef}
+// >
+// <Checkbox
+// colorScheme='red'
+// key={0}
+// onChange={() => handleCheckboxChange(0)}
+// isChecked={isChecked(0)}
+// >
+// TKDN
+// </Checkbox>
+// <Checkbox
+// colorScheme='red'
+// key={1}
+// onChange={() => handleCheckboxChange(1)}
+// isChecked={isChecked(1)}
+// >
+// SNI
+// </Checkbox>
+// <Checkbox
+// colorScheme='red'
+// key={2}
+// onChange={() => handleCheckboxChange(2)}
+// isChecked={isChecked(2)}
+// >
+// K3L
+// </Checkbox>
+// <div className='flex flex-row gap-2 w-full'>
+// <Checkbox
+// colorScheme='red'
+// key={3}
+// onChange={() => handleCheckboxChange(3)}
+// isChecked={isChecked(3)}
+// ></Checkbox>
+// <input
+// {...register('customSertifikatProduk')}
+// placeholder='Masukkan Dokumen/Sertifikat yang dimiliki oleh brand'
+// type='text'
+// onFocus={() => {
+// custom_sertifikat_produk_handle();
+// }}
+// // onFocus={() => handleCheckboxChange(4)}
+// className='form-input mt-2'
+// />
+// </div>
+// </div>
+// </div>
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.sertifikatProduk?.message}
+// </div>
+// </div>
+// </div>
+
+// <div className='flex flex-row justify-between items-center'>
+// <div className='w-2/5'>
+// <label className='form-label text-nowrap'>Garansi</label>
+// {!isKonfirmasi && (
+// <span className='text-xs opacity-60'>
+// Pilih waktu garansi yang diberikan
+// </span>
+// )}
+// </div>
+// <div className='w-3/5 flex flex-col '>
+// <div className='flex flex-row items-center gap-3'>
+// <div className={`${!isKonfirmasi && 'w-[25%]'}`}>
+// <Controller
+// name='tempoGaransi'
+// control={control}
+// render={(props) => (
+// <HookFormSelect
+// {...props}
+// options={dataGaransi}
+// placeholder={'Pilih durasi garansi'}
+// />
+// )}
+// />
+
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.tempoGaransi?.message}
+// </div>
+// </div>
+// </div>
+// </div>
+// </div>
+
+// <div className='w-full flex flex-row'>
+// <div className='w-2/5'>
+// <label className='form-label text-nowrap'>
+// Jelaskan Garansi yang dimaksud!
+// </label>
+// </div>
+// <div className='w-3/5'>
+// <textarea
+// {...register('explainGaransi')}
+// placeholder='Jelaskan bagian apa yang termasuk garansi'
+// type='textarea'
+// className='form-input'
+// rows={4}
+// cols={40}
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.explainGaransi?.message}
+// </div>
+// </div>
+// </div>
+
+// <div className='flex flex-row justify-between items-start'>
+// <div className='w-2/5'>
+// <label className='form-label text-wrap '>
+// Apakah Memiliki Minimum Order Quantity (MOQ)
+// </label>
+// </div>
+// <div className='w-3/5 flex flex-row justify-start'>
+// <div className='flex gap-x-4 flex-col w-full'>
+// <RadioGroup
+// onChange={handleCheckboxOrderQuantityChange}
+// value={
+// watch('minimumPembelian')
+// ? 'ya'
+// : watch('isOrderQuantity')
+// }
+// >
+// <Stack direction='column'>
+// <div className='flex flex-row text-nowrap gap-2'>
+// <Radio
+// colorScheme='red'
+// value='ya'
+// onChange={() => setValue('isOrderQuantity', 'ya')}
+// >
+// Ya
+// </Radio>
+
+// <Controller
+// name='minimumPembelian'
+// control={control}
+// render={(props) => (
+// <HookFormSelect
+// {...props}
+// options={dataMinimumOrderQuantity}
+// placeholder={
+// 'Pilih jenis minimum order quantity'
+// }
+// onFocus={() =>
+// setValue('isOrderQuantity', 'ya')
+// }
+// />
+// )}
+// />
+// </div>
+// <Radio
+// colorScheme='red'
+// value='tidak'
+// onChange={() => setValue('minimumPembelian', '')}
+// >
+// Tidak Ada
+// </Radio>
+// </Stack>
+// </RadioGroup>
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.isOrderQuantity?.message}
+// </div>
+// </div>
+// </div>
+// </div>
+
+// {!isKonfirmasi && (
+// <div className='flex justify-end'>
+// <div>
+// <span className='text-xs opacity-60'>
+// *Pastikan data yang anda masukan sudah benar dan sesuai
+// </span>
+// <button
+// type='submit'
+// className='btn-light bg-red-500 rounded-lg text-white w-fit py-2 px-4 md:w-fit mt-2 ml-0 md:ml-auto flex justify-between hover:bg-red-400'
+// >
+// <span className={` `}>Langkah Selanjutnya</span>
+// {<ChevronRightIcon className='w-5' />}
+// </button>
+// </div>
+// </div>
+// )}
+// </form>
+// </div>
+// </div>
+// </DesktopView>
+// <MobileView>
+// <div className='container flex flex-col items-star py-4'>
+// {!isKonfirmasi && (
+// <h2 className='font-semibold mb-6 text-xl'>Syarat Perdagangan</h2>
+// )}
+
+// <div className='w-full mt-4'>
+// <form
+// onSubmit={handleSubmit(onSubmitHandler)}
+// className='flex flex-col gap-4'
+// >
+// <div className='w-full flex flex-col'>
+// <div className='w-full'>
+// <label className='form-label text-nowrap'>
+// Syarat Pengembalian Barang
+// </label>
+// <div className='flex gap-x-4 flex-col w-full'>
+// <RadioGroup
+// onChange={handleCheckboxReturChange}
+// value={watch('isKembaliBarang')}
+// >
+// <Stack direction='column'>
+// <div className='flex flex-row text-nowrap gap-2'>
+// <Radio colorScheme='red' value='ya'>
+// Ya, dapat diretur
+// </Radio>
+// {watch('isKembaliBarang') == 'ya' && (
+// <textarea
+// {...register('textReturn')}
+// placeholder='jelaskan syarat pengembalian'
+// type='textarea'
+// className='form-input w-full'
+// />
+// )}
+// </div>
+// <Radio colorScheme='red' value='tidak'>
+// Tidak dapat diretur
+// </Radio>
+// </Stack>
+// </RadioGroup>
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.isKembaliBarang?.message}
+// </div>
+// </div>
+// </div>
+// </div>
+// <div className='w-full flex flex-col'>
+// <div className='w-full'>
+// <label className='form-label text-nowrap'>
+// Tenggat Waktu Perubahan Harga
+// </label>
+// <div className='flex gap-x-4 flex-col w-full'>
+// <RadioGroup
+// onChange={handleCheckboxTenggatWaktuChange}
+// value={watch('tenggatWaktu')}
+// >
+// <Stack direction='column'>
+// <Radio
+// colorScheme='red'
+// value='14'
+// onChange={() => setValue('customTenggatWaktu', ' ')}
+// >
+// 14 hari sejak data dikirimkan
+// </Radio>
+// <Radio
+// colorScheme='red'
+// value='30'
+// onChange={() => setValue('customTenggatWaktu', ' ')}
+// >
+// 30 hari sejak data dikirimkan
+// </Radio>
+// <div className='flex flex-row gap-2'>
+// <Radio colorScheme='red' value='custom'></Radio>
+// <input
+// {...register('customTenggatWaktu')}
+// placeholder='Masukkan jumlah hari untuk tenggat waktu'
+// type='text'
+// onFocus={() => setValue('tenggatWaktu', 'custom')}
+// className='form-input mt-2'
+// />
+// </div>
+// </Stack>
+// </RadioGroup>
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.tenggatWaktu?.message}
+// </div>
+// </div>
+// </div>
+// </div>
+// <div className='w-full flex flex-col'>
+// <div className='w-full'>
+// <label className='form-label text-nowrap'>
+// Dokumen/Sertifikat yang Dimiliki Oleh Brand
+// </label>
+// <div
+// className='flex flex-col gap-2 w-full'
+// // ref={categoryProdukRef}
+// >
+// <Checkbox
+// colorScheme='red'
+// key={0}
+// onChange={() => handleCheckboxChange(0)}
+// isChecked={isChecked(0)}
+// >
+// TKDN
+// </Checkbox>
+// <Checkbox
+// colorScheme='red'
+// key={1}
+// onChange={() => handleCheckboxChange(1)}
+// isChecked={isChecked(1)}
+// >
+// SNI
+// </Checkbox>
+// <Checkbox
+// colorScheme='red'
+// key={2}
+// onChange={() => handleCheckboxChange(2)}
+// isChecked={isChecked(2)}
+// >
+// K3L
+// </Checkbox>
+// <div className='flex flex-row gap-2 w-full'>
+// <Checkbox
+// colorScheme='red'
+// key={3}
+// onChange={() => handleCheckboxChange(3)}
+// isChecked={isChecked(3)}
+// ></Checkbox>
+// <input
+// {...register('customSertifikatProduk')}
+// placeholder='Masukkan Dokumen/Sertifikat yang dimiliki oleh brand'
+// type='text'
+// onFocus={() => {
+// custom_sertifikat_produk_handle();
+// }}
+// // onFocus={() => handleCheckboxChange(4)}
+// className='form-input mt-2'
+// />
+// </div>
+// </div>
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.sertifikatProduk?.message}
+// </div>
+// </div>
+// </div>
+
+// <div className='w-full flex flex-col'>
+// <label className='form-label text-nowrap'>Garansi</label>
+// <div className='w-full flex flex-row gap-2'>
+// <Controller
+// name='tempoGaransi'
+// control={control}
+// render={(props) => (
+// <HookFormSelect
+// {...props}
+// options={dataGaransi}
+// placeholder={'Pilih durasi garansi'}
+// />
+// )}
+// />
+
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.tempoGaransi?.message}
+// </div>
+// </div>
+// </div>
+// <div className='w-full flex flex-col'>
+// <label className='form-label text-nowrap'>
+// Jelaskan Garansi yang dimaksud!
+// </label>
+// <textarea
+// {...register('explainGaransi')}
+// placeholder='Jelaskan bagian apa yang termasuk garansi'
+// type='textarea'
+// className='form-input'
+// rows={4}
+// cols={40}
+// />
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.explainGaransi?.message}
+// </div>
+// </div>
+// <div className='w-full flex flex-col'>
+// <label className='form-label text-nowrap'>
+// Apakah Memiliki Minimum Order Quantity (MOQ)
+// </label>
+// <div className='flex gap-x-4 flex-col w-full'>
+// <RadioGroup
+// onChange={handleCheckboxOrderQuantityChange}
+// value={
+// watch('minimumPembelian')
+// ? 'ya'
+// : watch('isOrderQuantity')
+// }
+// >
+// <Stack direction='column'>
+// <div className='flex flex-row text-nowrap gap-2'>
+// <Radio
+// colorScheme='red'
+// value='ya'
+// onChange={() => setValue('isOrderQuantity', 'ya')}
+// >
+// Ya
+// </Radio>
+
+// <Controller
+// name='minimumPembelian'
+// control={control}
+// render={(props) => (
+// <HookFormSelect
+// {...props}
+// options={dataMinimumOrderQuantity}
+// placeholder={'Pilih jenis minimum order quantity'}
+// onFocus={() => setValue('isOrderQuantity', 'ya')}
+// />
+// )}
+// />
+// </div>
+// <Radio
+// colorScheme='red'
+// value='tidak'
+// onChange={() => setValue('minimumPembelian', '')}
+// >
+// Tidak Ada
+// </Radio>
+// </Stack>
+// </RadioGroup>
+// <div className='text-caption-2 text-danger-500 mt-1'>
+// {errors.isOrderQuantity?.message}
+// </div>
+// </div>
+// </div>
+
+// <div className=''>
+// {/* <div>
+// <ReCAPTCHA
+// ref={recaptchaRef}
+// sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE}
+// />
+// </div> */}
+// </div>
+// <div className='flex justify-center w-full '>
+// {/* <Button
+// colorScheme='red'
+// className='w-full md:w-fit'
+// type='submit'
+// >
+// Daftar Merchant{' '}
+// {<ChevronRightIcon className='w-5' color='white' />}
+// </Button> */}
+// {!isKonfirmasi && (
+// <div className='w-full'>
+// <span className='text-xs opacity-60'>
+// *Pastikan data yang anda masukan sudah benar dan sesuai
+// </span>
+// <button
+// type='submit'
+// className='btn-light bg-red-500 rounded-lg text-white w-full py-2 px-4 md:w-fit mt-2 ml-0 md:ml-auto flex justify-center hover:bg-red-400'
+// >
+// <span className={` `}>Langkah Selanjutnya</span>
+// {<ChevronRightIcon className='w-5' />}
+// </button>
+// </div>
+// )}
+// </div>
+// </form>
+// <PageContent path='/daftar-merchant' />
+// </div>
+// </div>
+// </MobileView>
+// </>
+// );
+// });
+// const validationSchema = Yup.object().shape({
+// isKembaliBarang: Yup.string().required('Harus di-pilih'),
+// tenggatWaktu: Yup.string().required('Harus di-pilih'),
+// sertifikatProduk: Yup.string().required('Harus di-pilih'),
+// tempoGaransi: Yup.string().required('Harus di-pilih'),
+// explainGaransi: Yup.string().required('Harus di-isi'),
+// isOrderQuantity: Yup.string().required('Harus di-isi'),
+// });
+// const defaultValues = {
+// isKembaliBarang: '',
+// tenggatWaktu: '',
+// sertifikatProduk: '',
+// explainGaransi: '',
+// isOrderQuantity: '',
+// };
+
+// export default SyaratDagang;
diff --git a/src/lib/pengajuan-tempo/component/Pengiriman.jsx b/src/lib/pengajuan-tempo/component/Pengiriman.jsx
index fcfa7e1e..a8e7fd22 100644
--- a/src/lib/pengajuan-tempo/component/Pengiriman.jsx
+++ b/src/lib/pengajuan-tempo/component/Pengiriman.jsx
@@ -360,6 +360,8 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
[errorsPengiriman]
);
+ const tukarInvoiceInputRef = useRef(null);
+ const tukarInvoiceInputPembayaranRef = useRef(null);
const PICNameRef = useRef(null);
const streetPengirimanRef = useRef(null);
const statePengirimanRef = useRef(null);
@@ -390,6 +392,14 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
behavior: 'smooth',
block: 'center',
};
+ if (errorsPengiriman.tukarInvoiceInput && tukarInvoiceInputRef.current) {
+ tukarInvoiceInputRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsPengiriman.tukarInvoiceInputPembayaran && tukarInvoiceInputPembayaranRef.current) {
+ tukarInvoiceInputPembayaranRef.current.scrollIntoView(options);
+ return;
+ }
if (errorsPengiriman.PICName && PICNameRef.current) {
PICNameRef.current.scrollIntoView(options);
return;
@@ -1052,9 +1062,7 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
<HookFormSelect
{...props}
options={zips}
- disabled={
- sameAddressStreet
- }
+ disabled={sameAddressStreet}
placeholder='Zip'
/>
) : (
@@ -1065,9 +1073,7 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
ref={zipRef}
placeholder='Kode Pos'
type='number'
- disabled={
- sameAddressStreet
- }
+ disabled={sameAddressStreet}
value={formPengiriman.zipPengiriman}
className='form-input'
onChange={handleInputChange}
@@ -1322,9 +1328,7 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
<HookFormSelect
{...props}
options={zipsInvoice}
- disabled={
- sameAddress
- }
+ disabled={sameAddress}
placeholder='Zip'
/>
) : (
@@ -1334,9 +1338,7 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
ref={zipInvoiceRef}
placeholder='Kode Pos'
type='number'
- disabled={
- sameAddress
- }
+ disabled={sameAddress}
value={formPengiriman.zipInvoice}
className='form-input'
onChange={handleInputChange}
@@ -1361,11 +1363,10 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
<div className='w-2/5'>
<label className='form-label text-wrap'>
Jadwal Penukaran Invoice{' '}
- <span className=' opacity-60'>(Opsional)</span>
</label>
{!isKonfirmasi && (
<span className='text-xs opacity-60'>
- isi jika perusahaan anda memiliki jadwal penukaran invoice
+ isi jadwal penukaran invoice
</span>
)}
</div>
@@ -1378,9 +1379,17 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
value={formPengiriman.tukarInvoiceInput}
className='form-input'
rows={4}
+ aria-invalid={errorsPengiriman.tukarInvoiceInput}
+ ref={tukarInvoiceInputRef}
cols={40}
onChange={handleInputChange}
+ required
/>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.tukarInvoiceInput}
+ </div>
+ )}
</div>
</div>
@@ -1388,11 +1397,10 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
<div className='w-2/5'>
<label className='form-label text-nowrap'>
Jadwal Pembayaran{' '}
- <span className=' opacity-60'>(Opsional)</span>
</label>
{!isKonfirmasi && (
<span className='text-xs opacity-60'>
- isi jika perusahaan anda memiliki jadwal pembayaran
+ isi jadwal pembayaran
</span>
)}
</div>
@@ -1403,10 +1411,18 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
placeholder='Masukkan jadwal pembayaran'
value={formPengiriman.tukarInvoiceInputPembayaran}
className='form-input'
+ aria-invalid={errorsPengiriman.tukarInvoiceInputPembayaran}
+ ref={tukarInvoiceInputPembayaranRef}
rows={4}
cols={40}
onChange={handleInputChange}
+ required
/>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.tukarInvoiceInputPembayaran}
+ </div>
+ )}
</div>
</div>
@@ -1601,7 +1617,6 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
)}
</div>
</div>
-
</div>
</form>
</div>
@@ -2330,8 +2345,6 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
</div>
<div className='w-2/5'></div>
</div>
-
-
</div>
</form>
</div>
diff --git a/src/lib/product/components/ProductSearch.jsx b/src/lib/product/components/ProductSearch.jsx
index 486d1a4b..2fb3138a 100644
--- a/src/lib/product/components/ProductSearch.jsx
+++ b/src/lib/product/components/ProductSearch.jsx
@@ -84,10 +84,7 @@ const ProductSearch = ({
if (router.asPath.includes('penawaran')) {
query = {
...query,
- fq: [
- `-flashsale_id_i:${router.query.penawaran}`,
- `flashsale_price_f:[1 TO *]`,
- ],
+ fq:`flashsale_id_i:${router.query.penawaran} AND flashsale_price_f:[1 TO *]`,
orderBy: 'flashsale-discount-desc',
};
setFinalQuery(query);
@@ -152,7 +149,7 @@ const ProductSearch = ({
}, [dataCategoriesProduct, dataLob]);
useEffect(() => {
- if (prefixUrl.includes('category') || prefixUrl.includes('lob')) {
+ if (prefixUrl.includes('category') || prefixUrl.includes('lob') || router.asPath.includes('penawaran')) {
setQueryFinal({ ...finalQuery, q, limit, orderBy });
} else {
setQueryFinal({ ...query, q, limit, orderBy });
diff --git a/src/lib/shipment/components/Shipments.jsx b/src/lib/shipment/components/Shipments.jsx
index 20dbb013..aaf778c3 100644
--- a/src/lib/shipment/components/Shipments.jsx
+++ b/src/lib/shipment/components/Shipments.jsx
@@ -251,7 +251,7 @@ const Shipments = () => {
<tr>
<th>Tanggal</th>
<th>No. Resi</th>
- <th>No. Pengiriman</th>
+ <th>No. Dokumen</th>
<th>Sales Order</th>
<th>Purchase Order</th>
<th>Expedisi</th>
diff --git a/src/lib/transaction/components/Transaction.jsx b/src/lib/transaction/components/Transaction.jsx
index 867de577..8b3a8dd0 100644
--- a/src/lib/transaction/components/Transaction.jsx
+++ b/src/lib/transaction/components/Transaction.jsx
@@ -410,6 +410,11 @@ const Transaction = ({ id }) => {
<TransactionStatusBadge status={transaction.data?.status} />
</div>
</DescriptionRow>
+ <DescriptionRow label='Status Transaksi'>
+ <div className='flex justify-end font-semibold text-red-500'>
+ {transaction.data?.expectedReadyToShip}
+ </div>
+ </DescriptionRow>
<DescriptionRow label='No Transaksi'>
{transaction.data?.name}
</DescriptionRow>
@@ -436,13 +441,15 @@ const Transaction = ({ id }) => {
onClick={() => setIdAWB(airway?.id)}
>
<div>
- <span className='text-sm text-gray_r-11'>
+ <p className='text-sm text-gray_r-11'>{airway?.name}</p>
+ <span className='mt-2 font-medium'>
No Resi : {airway?.trackingNumber || '-'}{' '}
</span>
- <p className='mt-1 font-medium'>{airway?.name}</p>
+ {/*biteship*/}
+ {/*<p className='mt-1 font-medium'>{airway?.name}</p>*/}
</div>
<div className='flex gap-x-2'>
- <div className='text-sm text-gray-600 badge-green leading-[1.5] mt-1'>
+ <div className='text-sm text-gray-600 badge-green leading-[1.5] mt-1 text-center'>
{airway?.delivered ? 'Pesanan Tiba' : 'Sedang Dikirim'}
</div>
<ChevronRightIcon className='w-5 stroke-2' />
@@ -619,6 +626,20 @@ const Transaction = ({ id }) => {
)}
</div>
+ {/*new-release*/}
+ {/*<div className='flex items-center justify-between mb-3'>*/}
+ {/* <div className='flex items-center gap-x-2'>*/}
+ {/* <span className='text-h-sm font-medium'>*/}
+ {/* {transaction?.data?.name}*/}
+ {/* </span>*/}
+ {/* <TransactionStatusBadge status={transaction?.data?.status} />*/}
+ {/* </div>*/}
+ {/* <div className='text-h-sm'>*/}
+ {/* Estimasi Barang Siap:{' '}*/}
+ {/* <span className='text-red-500 font-semibold'>*/}
+ {/* {transaction?.data?.expectedReadyToShip}*/}
+ {/* </span>*/}
+ {/* </div>*/}
<div className='flex items-center gap-x-2 mb-3'>
<span className='text-h-sm font-medium'>
{transaction?.data?.name}
@@ -804,6 +825,34 @@ const Transaction = ({ id }) => {
</div>
<div className='flex '>
+ {/*New release*/}
+ {/* <div className='grid grid-cols-1 gap-1 w-2/3'>*/}
+ {/* {transaction?.data?.pickings?.map((airway) => (*/}
+ {/* <button*/}
+ {/* className='shadow rounded-md p-3 text-gray_r-12 font-normal flex justify-between items-center text-left h-20'*/}
+ {/* key={airway?.id}*/}
+ {/* onClick={() => setIdAWB(airway?.id)}*/}
+ {/* >*/}
+ {/* <div>*/}
+ {/* <p className='text-sm text-gray_r-11'>*/}
+ {/* {airway?.name}*/}
+ {/* </p>*/}
+ {/* <span className='text-md text-bold mt-1'>*/}
+ {/* No Resi : {airway?.trackingNumber || '-'}{' '}*/}
+ {/* </span>*/}
+ {/* </div>*/}
+ {/* <div className='flex gap-x-2'>*/}
+ {/* <div className='text-sm text-gray-600 badge-green leading-[1.5] mt-1 text-center'>*/}
+ {/* {airway?.delivered*/}
+ {/* ? 'Pesanan Tiba'*/}
+ {/* : 'Sedang Dikirim'}*/}
+ {/* </div>*/}
+ {/* <ChevronRightIcon className='w-5 stroke-2' />*/}
+ {/* </div>*/}
+ {/* </button>*/}
+ {/* ))}*/}
+ {/* </div>*/}
+ {/*</div>*/}
<div className='invoice w-1/2 '>
<div className='text-h-sm font-semibold mt-10 mb-4 '>
Invoice
diff --git a/src/pages/api/flashsale-header.js b/src/pages/api/flashsale-header.js
index 578801ae..916a9cd2 100644
--- a/src/pages/api/flashsale-header.js
+++ b/src/pages/api/flashsale-header.js
@@ -35,7 +35,7 @@ export default async function handler(req, res) {
}
return res.status(200).json({ data });
} else {
- const flashSale = await odooApi('GET', `/api/v1/flashsale/header`);
+ const flashSale = await odooApi('GET', `/api/v1/flashsale/header?is_show_program=true`);
if (flashSale.length === 0) {
return res.status(200).json({ data: [] });
} else {
diff --git a/src/pages/api/shop/search.js b/src/pages/api/shop/search.js
index 63ec7ca0..e14b0ca2 100644
--- a/src/pages/api/shop/search.js
+++ b/src/pages/api/shop/search.js
@@ -19,6 +19,7 @@ export default async function handler(req, res) {
source = '',
} = req.query;
+
let { stock = '' } = req.query;
let paramOrderBy = '';
switch (orderBy) {
@@ -88,6 +89,10 @@ export default async function handler(req, res) {
'price_tier1_v2_f:[1 TO *]',
];
+ if (orderBy === 'stock') {
+ filterQueries.push('stock_total_f:{0 TO *}');
+ }
+
if (fq && source != 'similar' && typeof fq != 'string') {
// filterQueries.push(fq);
fq.push(...filterQueries);
diff --git a/src/pages/daftar-merchant.jsx b/src/pages/daftar-merchant.jsx
index e1fa9bcb..945a0060 100644
--- a/src/pages/daftar-merchant.jsx
+++ b/src/pages/daftar-merchant.jsx
@@ -12,4 +12,4 @@ export default function DaftarMerchant() {
</BasicLayout>
</>
)
-}
+} \ No newline at end of file
diff --git a/src/utils/solrMapping.js b/src/utils/solrMapping.js
index ecd62be2..33f0cbaf 100644
--- a/src/utils/solrMapping.js
+++ b/src/utils/solrMapping.js
@@ -43,6 +43,7 @@ export const productMappingSolr = (products, pricelist) => {
let productMapped = {
id: product.product_id_i || '',
image: product.image_s || '',
+ imageCarousel: product.image_carousel_ss || '',
imageMobile: product.image_mobile_s || '',
code: product.default_code_s || '',
description: product.description_t || '',