summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorit-fixcomart <it@fixcomart.co.id>2025-05-14 09:27:26 +0700
committerit-fixcomart <it@fixcomart.co.id>2025-05-14 09:27:26 +0700
commit29ecab270822500ead372d8d3e05c9281a514715 (patch)
treea32735f4b7fab774de6a2bbee9380f363c51bec2
parentdd66804b05166ad6bb71bf54fe3374d9897fee86 (diff)
parent746a11b810ae9e8a974a76d0548297cd0faff9b5 (diff)
Merge branch 'new-release' into CR/form-merchant
-rw-r--r--package.json2
-rw-r--r--public/images/socials/x-indoteknik.pngbin0 -> 17454 bytes
-rw-r--r--src-migrate/modules/cart/components/Item.tsx20
-rw-r--r--src-migrate/modules/popup-information/index.tsx6
-rw-r--r--src-migrate/modules/product-detail/components/AddToCart.tsx37
-rw-r--r--src-migrate/modules/product-detail/components/PriceAction.tsx2
-rw-r--r--src-migrate/modules/product-detail/components/ProductDetail.tsx78
-rw-r--r--src-migrate/modules/promo/components/Voucher.tsx1
-rw-r--r--src-migrate/modules/register/components/RegistrasiBisnis.tsx4
-rw-r--r--src-migrate/modules/register/index.tsx7
-rw-r--r--src-migrate/modules/register/stores/usePengajuanTempoStore.ts14
-rw-r--r--src-migrate/modules/side-banner/index.tsx11
-rw-r--r--src-migrate/pages/api/product-variant/[id].tsx4
-rw-r--r--src-migrate/services/banner.ts9
-rw-r--r--src-migrate/types/auth.ts2
-rw-r--r--src-migrate/types/product.ts3
-rw-r--r--src-migrate/types/tempo.ts5
-rw-r--r--src-migrate/types/voucher.ts1
-rw-r--r--src-migrate/validations/tempo.ts118
-rw-r--r--src/core/components/elements/Navbar/NavbarDesktop.jsx15
-rw-r--r--src/lib/address/components/EditAddress.jsx4
-rw-r--r--src/lib/auth/components/CompanyProfile.jsx16
-rw-r--r--src/lib/auth/components/Menu.jsx1
-rw-r--r--src/lib/category/components/Category.jsx2
-rw-r--r--src/lib/checkout/components/Checkout.jsx76
-rw-r--r--src/lib/checkout/components/CheckoutSection.jsx2
-rw-r--r--src/lib/flashSale/api/flashSaleApi.js5
-rw-r--r--src/lib/flashSale/components/FlashSaleNonDisplay.jsx12
-rw-r--r--src/lib/home/components/ServiceList.jsx2
-rw-r--r--src/lib/pengajuan-tempo/component/Dokumen.jsx4
-rw-r--r--src/lib/pengajuan-tempo/component/KonfirmasiDokumen.jsx4
-rw-r--r--src/lib/pengajuan-tempo/component/PengajuanTempo.jsx34
-rw-r--r--src/lib/pengajuan-tempo/component/Pengiriman.jsx310
-rw-r--r--src/lib/pengajuan-tempo/component/informasiPerusahaan.jsx12
-rw-r--r--src/lib/product/components/Product/ProductDesktopVariant.jsx52
-rw-r--r--src/lib/product/components/Product/ProductMobile.jsx6
-rw-r--r--src/lib/product/components/ProductSearch.jsx9
-rw-r--r--src/lib/tempo/components/Tempo.jsx16
-rw-r--r--src/lib/tracking-order/component/TrackingOrder.jsx35
-rw-r--r--src/lib/transaction/components/Transaction.jsx2
-rw-r--r--src/lib/variant/components/VariantCard.jsx31
-rw-r--r--src/pages/api/flashsale-header.js2
-rw-r--r--src/pages/api/shop/search.js1
-rw-r--r--src/pages/api/webhook/biteship.js13
-rw-r--r--src/pages/google_merchant/products/[page].js3
-rw-r--r--src/pages/index.jsx14
-rw-r--r--src/pages/my/profile.jsx54
-rw-r--r--src/pages/my/tempo/index.jsx54
-rw-r--r--src/pages/pengajuan-tempo/index.jsx2
-rw-r--r--src/pages/sitemap/products/[page].js5
-rw-r--r--src/utils/solrMapping.js1
51 files changed, 905 insertions, 218 deletions
diff --git a/package.json b/package.json
index 1e9dc696..89e3b47a 100644
--- a/package.json
+++ b/package.json
@@ -50,8 +50,10 @@
"react-multi-select-component": "^4.3.4",
"react-query": "^3.39.3",
"react-select": "^5.8.0",
+ "react-slick": "^0.30.3",
"react-web-share": "^2.0.2",
"redis": "^4.7.0",
+ "slick-carousel": "^1.8.1",
"snakecase-keys": "^5.5.0",
"swiper": "^8.4.4",
"tw-merge": "^0.0.1-alpha.3",
diff --git a/public/images/socials/x-indoteknik.png b/public/images/socials/x-indoteknik.png
new file mode 100644
index 00000000..c58056a0
--- /dev/null
+++ b/public/images/socials/x-indoteknik.png
Binary files differ
diff --git a/src-migrate/modules/cart/components/Item.tsx b/src-migrate/modules/cart/components/Item.tsx
index ab2e7ce1..86d1dc43 100644
--- a/src-migrate/modules/cart/components/Item.tsx
+++ b/src-migrate/modules/cart/components/Item.tsx
@@ -1,5 +1,6 @@
import style from '../styles/item.module.css'
-
+import odooApi from '~/libs/odooApi';
+import { useEffect, useState } from 'react';
import { Skeleton, SkeletonProps, Tooltip } from '@chakra-ui/react'
import { InfoIcon } from 'lucide-react'
import Image from 'next/image'
@@ -22,6 +23,17 @@ type Props = {
}
const CartItem = ({ item, editable = true, selfPicking}: Props) => {
+ const [qtyPickUp, setQtyPickUp] = useState(0);
+ useEffect(() => {
+ const fetchData = async () => {
+ const qty_available = await odooApi(
+ 'GET',
+ `/api/v1/product_variant/${item.id}/qty_available`
+ );
+ setQtyPickUp(qty_available?.qty);
+ };
+ fetchData();
+ }, [item]);
return (
<div className={style.wrapper}>
{item.cart_type === 'promotion' && (
@@ -54,11 +66,11 @@ const CartItem = ({ item, editable = true, selfPicking}: Props) => {
<CartItem.Image item={item} />
<div className={style.details}>
- {item?.available_quantity > 0 && (
+ {qtyPickUp > 0 && (
<div className='text-[10px] text-red-500 italic'>
- {item.quantity <= item?.available_quantity
+ {item.quantity <= qtyPickUp
? '*Barang ini bisa di pickup maksimal pukul 16.00'
- : `*${item?.available_quantity} Barang ini bisa di pickup maksimal pukul 16.00`}
+ : `*${qtyPickUp} Barang ini bisa di pickup maksimal pukul 16.00`}
</div>
)}
<CartItem.Name item={item} />
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/AddToCart.tsx b/src-migrate/modules/product-detail/components/AddToCart.tsx
index 280e4a7a..95bc1d88 100644
--- a/src-migrate/modules/product-detail/components/AddToCart.tsx
+++ b/src-migrate/modules/product-detail/components/AddToCart.tsx
@@ -15,7 +15,8 @@ import { useProductCartContext } from '@/contexts/ProductCartContext';
import { createSlug } from '~/libs/slug';
import formatCurrency from '~/libs/formatCurrency';
import { useProductDetail } from '../stores/useProductDetail';
-
+import { gtagAddToCart } from '@/core/utils/googleTag';
+import axios from 'axios';
type Props = {
variantId: number | null;
quantity?: number;
@@ -50,6 +51,38 @@ const AddToCart = ({
isLoading,
setIsloading,
} = useProductCartContext();
+ const [activeVariant, setActiveVariant] = useState({
+ id: 0,
+ code: '',
+ name: '',
+ price: '',
+ stock: '',
+ weight: '',
+ isFlashSale: false,
+ });
+
+ useEffect(() => {
+ const fetchData = async () => {
+ if (variantId) {
+ let response = await axios(
+ `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/variant-detail?id=${variantId}`
+ );
+ let productVariant = response.data;
+ if (productVariant) {
+ setActiveVariant({
+ id: productVariant[0].id,
+ code: productVariant[0].code,
+ name: productVariant[0].name,
+ price: productVariant[0].price.price,
+ stock: productVariant[0].stockTotal,
+ weight: productVariant[0].weight,
+ isFlashSale: productVariant[0].isFlashsale,
+ });
+ }
+ }
+ };
+ fetchData();
+ }, [variantId]);
const productSimilarQuery = [
product?.name,
@@ -101,6 +134,8 @@ const AddToCart = ({
setRefreshCart(true);
setAddCartAlert(true);
+ gtagAddToCart(activeVariant, quantity);
+
toast({
title: 'Tambah ke keranjang',
description: 'Berhasil menambahkan barang ke keranjang belanja',
diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx
index 9348bbfb..850c2d9d 100644
--- a/src-migrate/modules/product-detail/components/PriceAction.tsx
+++ b/src-migrate/modules/product-detail/components/PriceAction.tsx
@@ -163,7 +163,7 @@ const PriceAction = ({ product }: Props) => {
</span> */}
</div>
<div>
- {selectedVariant?.is_in_bu && (
+ {qtyPickUp > 0 && (
<Link href='/panduan-pick-up-service' className='group'>
<Image
src='/images/PICKUP-NOW.png'
diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx
index b036cc2d..0660b9c0 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';
@@ -66,9 +66,23 @@ const ProductDetail = ({ product }: Props) => {
if (typeof auth === 'object') {
setIsApproval(auth?.feature?.soApproval);
}
- setSelectedVariant(product?.variants[0])
+ const selectedVariant =
+ product?.variants?.find((variant) => variant.is_in_bu) ||
+ product?.variants?.[0];
+ setSelectedVariant(selectedVariant);
+ // 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'>
@@ -78,7 +92,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'>
@@ -102,7 +146,12 @@ const ProductDetail = ({ product }: Props) => {
)}
<div className='h-4 md:h-10' />
- {!!activeVariantId && !isApproval && <ProductPromoSection product={product} productId={activeVariantId} />}
+ {!!activeVariantId && !isApproval && (
+ <ProductPromoSection
+ product={product}
+ productId={activeVariantId}
+ />
+ )}
{/* <div className={style['section-card']}>
<h2 className={style['heading']}>
@@ -117,15 +166,18 @@ const ProductDetail = ({ product }: Props) => {
<div className={style['section-card']}>
<h2 className={style['heading']}>Informasi Produk</h2>
<div className='h-4' />
- <div
- className={style['description']}
- dangerouslySetInnerHTML={{
- __html:
- !product.description || product.description == '<p><br></p>'
- ? 'Belum ada deskripsi'
- : product.description,
- }}
- />
+ <div className='overflow-x-auto'>
+ <div
+ className={style['description']}
+ dangerouslySetInnerHTML={{
+ __html:
+ !product.description ||
+ product.description == '<p><br></p>'
+ ? 'Belum ada deskripsi'
+ : product.description,
+ }}
+ />
+ </div>
</div>
</div>
</div>
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/modules/register/components/RegistrasiBisnis.tsx b/src-migrate/modules/register/components/RegistrasiBisnis.tsx
index 332c5358..5933b5f2 100644
--- a/src-migrate/modules/register/components/RegistrasiBisnis.tsx
+++ b/src-migrate/modules/register/components/RegistrasiBisnis.tsx
@@ -13,11 +13,11 @@ const RegistrasiBisnis: React.FC<FormProps> = ({
chekValid,
buttonSubmitClick,
}) => {
- const [isPKP, setIsPKP] = useState(true);
+ const [isPKP, setIsPKP] = useState(false);
const [isTerdaftar, setIsTerdaftar] = useState(false);
const [isDropIndividu, setIsDropIndividu] = useState(true);
const [isBisnisClicked, setisBisnisClicked] = useState(true);
- const [selectedValue, setSelectedValue] = useState('PKP');
+ const [selectedValue, setSelectedValue] = useState('Non-PKP');
const [selectedValueBisnis, setSelectedValueBisnis] = useState('false');
const { validate, updateForm } = useRegisterStore();
diff --git a/src-migrate/modules/register/index.tsx b/src-migrate/modules/register/index.tsx
index 39f4771c..06f7cbea 100644
--- a/src-migrate/modules/register/index.tsx
+++ b/src-migrate/modules/register/index.tsx
@@ -101,6 +101,13 @@ const Register = () => {
status: 'warning',
});
break;
+ case 'BISNIS_NOT_FOUND':
+ toast({
+ ...toastProps,
+ title: 'Bisnis tidak ditemukan',
+ status: 'warning',
+ });
+ break;
}
};
diff --git a/src-migrate/modules/register/stores/usePengajuanTempoStore.ts b/src-migrate/modules/register/stores/usePengajuanTempoStore.ts
index 1e086c06..79ab3612 100644
--- a/src-migrate/modules/register/stores/usePengajuanTempoStore.ts
+++ b/src-migrate/modules/register/stores/usePengajuanTempoStore.ts
@@ -171,7 +171,7 @@ type StatePengiriman = {
};
type ActionPengiriman = {
updateFormPengiriman: (name: string, value: string) => void;
-
+ updateDokumenProsedur: (name: string, format: string, base64: string) => void;
validatePengiriman: () => void;
};
export const usePengajuanTempoStorePengiriman = create<
@@ -186,6 +186,8 @@ export const usePengajuanTempoStorePengiriman = create<
districtPengiriman: '',
subDistrictPengiriman: '',
zipPengiriman: '',
+ PICBarangMobile: '',
+ invoicePicMobile: '',
invoicePicTittle: '',
invoicePic: '',
isSameAddrees: '',
@@ -202,12 +204,21 @@ export const usePengajuanTempoStorePengiriman = create<
dokumenPengirimanInput: '',
dokumenKirimInput: '',
dokumenPengirimanInvoice: '',
+ dokumenProsedur: { name: '', format: '', base64: '' },
},
updateFormPengiriman: (name, value) =>
set((state) => ({
formPengiriman: { ...state.formPengiriman, [name]: value },
})),
+ updateDokumenProsedur: (name, format, base64) =>
+ set((state) => ({
+ formPengiriman: {
+ ...state.formPengiriman,
+ dokumenProsedur: { name, format, base64 },
+ },
+ })),
+
errorsPengiriman: {},
validatePengiriman: () => {
try {
@@ -260,6 +271,7 @@ export const usePengajuanTempoStoreDokumen = create<
dokumenLaporanKeuangan: { name: '', format: '', base64: '' },
dokumenFotoKantor: { name: '', format: '', base64: '' },
dokumenTempatBekerja: { name: '', format: '', base64: '' },
+ dokumenProsedur: { name: '', format: '', base64: '' },
},
// Memperbarui dokumen dengan name, format, dan base64
diff --git a/src-migrate/modules/side-banner/index.tsx b/src-migrate/modules/side-banner/index.tsx
index 878b8e70..7bc5f394 100644
--- a/src-migrate/modules/side-banner/index.tsx
+++ b/src-migrate/modules/side-banner/index.tsx
@@ -5,12 +5,15 @@ import Image from "~/components/ui/image";;
import { getBanner } from "~/services/banner";
import { getRandomInt } from '@/utils/getRandomInt';
-const SideBanner = () => {
+interface SideBannerProps {
+ query?: string; // Menentukan bahwa 'query' adalah string (bisa undefined)
+}
+
+const SideBanner: React.FC<SideBannerProps> = ({ query }) => {
const fetchSideBanner = useQuery({
- queryKey: 'sideBanner',
- queryFn: () => getBanner({ type: 'side-banner-search' })
+ queryKey: ["sideBanner", query],
+ queryFn: () => getBanner({ type: "side-banner-search", keyword: query }),
});
-
const length = useMemo(() => fetchSideBanner.data?.length, [fetchSideBanner.data]);
const randomIndex = useMemo(() => getRandomInt(length), [length]);
const banner = fetchSideBanner?.data?.[randomIndex] || false;
diff --git a/src-migrate/pages/api/product-variant/[id].tsx b/src-migrate/pages/api/product-variant/[id].tsx
index 2c46ac89..0f7524d0 100644
--- a/src-migrate/pages/api/product-variant/[id].tsx
+++ b/src-migrate/pages/api/product-variant/[id].tsx
@@ -6,7 +6,7 @@ const SOLR_HOST = process.env.SOLR_HOST as string
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const variantId = req.query.id as string
- let price_tier = 'tier1'
+ let price_tier = 'tier1_v2'
let auth = req.cookies.auth ? JSON.parse(req.cookies.auth) : null
if (auth?.pricelist) price_tier = auth.pricelist
@@ -29,7 +29,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const map = async (variant: any, price_tier: string) => {
const data: any = {}
- const price = variant[`price_${price_tier}_v2_f`] || 0
+ const price = variant[`price_${price_tier}_f`] || 0
data.id = parseInt(variant.id)
data.parent_id = variant.template_id_i
diff --git a/src-migrate/services/banner.ts b/src-migrate/services/banner.ts
index 1b46ba06..7fb922cf 100644
--- a/src-migrate/services/banner.ts
+++ b/src-migrate/services/banner.ts
@@ -3,9 +3,16 @@ import { IBanner } from '~/types/banner';
export const getBanner = async ({
type,
+ keyword,
}: {
type: string;
+ keyword?: string; // Tambahkan keyword sebagai parameter opsional
}): Promise<IBanner[]> => {
const searchParams = new URLSearchParams({ type });
- return await odooApi('GET', `/api/v1/banner?${searchParams.toString()}`);
+
+ if (keyword) {
+ searchParams.append("keyword", keyword);
+ }
+
+ return await odooApi("GET", `/api/v1/banner?${searchParams.toString()}`);
};
diff --git a/src-migrate/types/auth.ts b/src-migrate/types/auth.ts
index 1b400e95..f451ef75 100644
--- a/src-migrate/types/auth.ts
+++ b/src-migrate/types/auth.ts
@@ -30,7 +30,7 @@ export type RegisterProps = z.infer<typeof registerSchema>;
export type RegisterResApiProps = {
register: boolean;
- reason: 'EMAIL_USED' | 'NOT_ACTIVE' | null;
+ reason: 'EMAIL_USED' | 'NOT_ACTIVE' | 'BISNIS_NOT_FOUND' | null;
};
type ActivationResProps = {
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/tempo.ts b/src-migrate/types/tempo.ts
index d043e2d6..880a30bb 100644
--- a/src-migrate/types/tempo.ts
+++ b/src-migrate/types/tempo.ts
@@ -42,6 +42,8 @@ export type tempoPropsPengiriman = {
cityPengiriman: string;
streetInvoice: string;
zip: string;
+ PICBarangMobile: string;
+ invoicePicMobile: string;
invoicePic: string;
isSameAddrees: string;
stateInvoice: string;
@@ -52,6 +54,7 @@ export type tempoPropsPengiriman = {
dokumenPengirimanInput: string;
dokumenPengirimanInvoice: string;
dokumenPengirimanInvoiceInput: string;
+ dokumenProsedur: string;
};
export type tempoPropsSupplier = {
supplier: string;
@@ -82,7 +85,7 @@ export type TempoPropsDokumen = z.infer<typeof TempoSchemaDokumen>;
export type TempoResApiProps = {
Tempo: boolean;
- reason: 'EMAIL_USED' | 'NOT_ACTIVE' | null;
+ reason: 'EMAIL_USED' | 'NOT_ACTIVE' | 'BISNIS_NOT_FOUND' | null;
};
type ActivationResProps = {
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/validations/tempo.ts b/src-migrate/validations/tempo.ts
index f019275c..46ac1ef1 100644
--- a/src-migrate/validations/tempo.ts
+++ b/src-migrate/validations/tempo.ts
@@ -8,7 +8,7 @@ export const TempoSchema = z.object({
state: z.string().min(1, { message: 'Provinsi harus dipilih' }),
city: z.string().min(1, { message: 'Kota harus dipilih' }),
district: z.string().min(1, { message: 'Kecamatan harus dipilih' }),
- subDistrict: z.string().min(1, { message: 'Kelurahan harus dipilih' }),
+ subDistrict: z.string().optional(),
mobile: z
.string()
.min(1, { message: 'Nomor telepon harus diisi' })
@@ -33,50 +33,81 @@ export const TempoSchema = z.object({
.min(1, { message: 'Category produk harus dipilih' }),
});
-export const TempoSchemaKontakPerson = z.object({
- direkturName: z.string().min(1, { message: 'Nama harus diisi' }),
- direkturTittle: z.string().min(1, { message: 'Tittle harus dipilih' }),
- financeName: z.string().min(1, { message: 'Nama harus diisi' }),
- direkturMobile: z.string().optional(),
- financeMobile: z
+export const TempoSchemaKontakPerson = z
+ .object({
+ direkturName: z.string().min(1, { message: 'Nama harus diisi' }),
+ direkturTittle: z.string().min(1, { message: 'Tittle harus dipilih' }),
+ financeName: z.string().min(1, { message: 'Nama harus diisi' }),
+ direkturMobile: z.string().optional(),
+ financeMobile: z
+ .string()
+ .min(1, { message: 'Nomor telepon harus diisi' })
+ .refine((val) => /^\d{10,12}$/.test(val), {
+ message: 'Format nomor telepon tidak valid, contoh: 081234567890',
+ }),
+ purchasingTittle: z.string().min(1, { message: 'Tittle harus dipilih' }),
+ financeTittle: z.string().min(1, { message: 'Tittle harus dipilih' }),
+ purchasingMobile: z
+ .string()
+ .min(1, { message: 'Nomor telepon harus diisi' })
+ .refine((val) => /^\d{10,12}$/.test(val), {
+ message: 'Format nomor telepon tidak valid, contoh: 081234567890',
+ }),
+ direkturEmail: z
+ .string()
+ .min(1, { message: 'Email harus diisi' })
+ .email({ message: 'Email harus menggunakan format example@mail.com' }),
+ purchasingEmail: z
+ .string()
+ .min(1, { message: 'Email harus diisi' })
+ .email({ message: 'Email harus menggunakan format example@mail.com' }),
+ financeEmail: z
+ .string()
+ .min(1, { message: 'Email harus diisi' })
+ .email({ message: 'Email harus menggunakan format example@mail.com' }),
+ purchasingName: z.string().min(1, { message: 'Nama harus diisi' }),
+ })
+ .refine(
+ (data) =>
+ !data.financeEmail || // Jika financeEmail kosong, tidak perlu validasi
+ data.financeEmail !== data.purchasingEmail, // Validasi jika financeEmail ada
+ {
+ message: 'Email Finance tidak boleh sama dengan Email Purchasing',
+ path: ['financeEmail'],
+ }
+ )
+ .refine(
+ (data) =>
+ !data.direkturEmail ||
+ (data.direkturEmail !== data.financeEmail &&
+ data.direkturEmail !== data.purchasingEmail),
+ {
+ message:
+ 'Email Direktur tidak boleh sama dengan Email Finance atau Purchasing',
+ path: ['direkturEmail'],
+ }
+ );
+export const TempoSchemaPengiriman = z.object({
+ PICTittle: z.string().min(1, { message: 'Tittle harus dipilih' }),
+ PICName: z.string().min(1, { message: 'Nama harus diisi' }),
+ streetPengiriman: z.string().min(1, { message: 'Alamat harus diisi' }),
+ statePengiriman: z.string().min(1, { message: 'Provinsi harus dipilih' }),
+ cityPengiriman: z.string().min(1, { message: 'Kota harus dipilih' }),
+ districtPengiriman: z.string().min(1, { message: 'Kecamatan harus dipilih' }),
+ subDistrictPengiriman: z.string().optional(),
+ zipPengiriman: z.string().min(1, { message: 'Kode pos harus diisi' }),
+ PICBarangMobile: z
.string()
.min(1, { message: 'Nomor telepon harus diisi' })
.refine((val) => /^\d{10,12}$/.test(val), {
message: 'Format nomor telepon tidak valid, contoh: 081234567890',
}),
- purchasingTittle: z.string().min(1, { message: 'Tittle harus dipilih' }),
- financeTittle: z.string().min(1, { message: 'Tittle harus dipilih' }),
- purchasingMobile: z
+ invoicePicMobile: z
.string()
.min(1, { message: 'Nomor telepon harus diisi' })
.refine((val) => /^\d{10,12}$/.test(val), {
message: 'Format nomor telepon tidak valid, contoh: 081234567890',
}),
- direkturEmail: z
- .string()
- .min(1, { message: 'Email harus diisi' })
- .email({ message: 'Email harus menggunakan format example@mail.com' }),
- purchasingEmail: z
- .string()
- .min(1, { message: 'Email harus diisi' })
- .email({ message: 'Email harus menggunakan format example@mail.com' }),
- financeEmail: z
- .string()
- .min(1, { message: 'Email harus diisi' })
- .email({ message: 'Email harus menggunakan format example@mail.com' }),
- purchasingName: z.string().min(1, { message: 'Nama harus diisi' }),
-});
-export const TempoSchemaPengiriman = z.object({
- PICTittle: z.string().min(1, { message: 'Tittle harus dipilih' }),
- PICName: z.string().min(1, { message: 'Nama harus diisi' }),
- streetPengiriman: z.string().min(1, { message: 'Alamat harus diisi' }),
- statePengiriman: z.string().min(1, { message: 'Provinsi harus dipilih' }),
- cityPengiriman: z.string().min(1, { message: 'Kota harus dipilih' }),
- districtPengiriman: z.string().min(1, { message: 'Kecamatan harus dipilih' }),
- subDistrictPengiriman: z
- .string()
- .min(1, { message: 'Kelurahan harus dipilih' }),
- zipPengiriman: z.string().min(1, { message: 'Kode pos harus diisi' }),
invoicePicTittle: z.string().min(1, { message: 'Tittle harus dipilih' }),
invoicePic: z.string().min(1, { message: 'Nama pic invoice harus diisi' }),
streetInvoice: z.string().min(1, { message: 'Alamat invoice harus diisi' }),
@@ -87,18 +118,25 @@ export const TempoSchemaPengiriman = z.object({
districtInvoice: z
.string()
.min(1, { message: 'Kecamatan invoice harus dipilih' }),
- subDistrictInvoice: z
- .string()
- .min(1, { message: 'Kelurahan invoice harus dipilih' }),
+ subDistrictInvoice: z.string().optional(),
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(),
dokumenPengirimanInvoiceInput: z.string().optional(),
+ dokumenProsedur: z.object({
+ name: z.string().optional(),
+ format: z.string().optional(),
+ base64: z.string().optional(),
+ }),
});
export const TempoSchemaSupplier = z.object({
supplier: z.string().min(1, { message: 'Nama supplier harus diisi' }),
diff --git a/src/core/components/elements/Navbar/NavbarDesktop.jsx b/src/core/components/elements/Navbar/NavbarDesktop.jsx
index d6b1bdea..03d7fa0c 100644
--- a/src/core/components/elements/Navbar/NavbarDesktop.jsx
+++ b/src/core/components/elements/Navbar/NavbarDesktop.jsx
@@ -423,6 +423,20 @@ const SocialMedias = () => (
loading='eager'
/>
</a>
+ <a
+ target='_blank'
+ rel='noreferrer'
+ href='https://x.com/indoteknikcom'
+ aria-label='X - Indoteknik.com'
+ >
+ <NextImage
+ src='/images/socials/x-indoteknik.png'
+ alt='X'
+ width={24}
+ height={24}
+ loading='eager'
+ />
+ </a>
{/* <a target='_blank' rel='noreferrer' href={whatsappUrl(null)}>
<NextImage
src='/images/socials/Whatsapp.png'
@@ -487,6 +501,7 @@ const SocialMedias = () => (
loading='eager'
/>
</a>
+
</div>
</div>
);
diff --git a/src/lib/address/components/EditAddress.jsx b/src/lib/address/components/EditAddress.jsx
index 62858465..ba6bd25b 100644
--- a/src/lib/address/components/EditAddress.jsx
+++ b/src/lib/address/components/EditAddress.jsx
@@ -38,7 +38,7 @@ const EditAddress = ({ id, defaultValues }) => {
useEffect(() => {
const loadProfile = async () => {
- const dataProfile = await addressApi({ id: auth.parentId });
+ const dataProfile = await addressApi({ id: auth.partnerId });
setValue('industry', dataProfile.industryId);
setValue('companyType', dataProfile.companyTypeId);
setValue('taxName', dataProfile.taxName);
@@ -161,7 +161,7 @@ const EditAddress = ({ id, defaultValues }) => {
}
// if (isUpdated?.id) {
- if (address?.id && auth.company ? isUpdated?.id : true) {
+ if (address?.id && (auth.company && auth?.partnerId == id ? isUpdated?.id : true)) {
toast.success('Berhasil mengubah alamat');
router.back();
} else {
diff --git a/src/lib/auth/components/CompanyProfile.jsx b/src/lib/auth/components/CompanyProfile.jsx
index 410d6a23..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.parentId ? auth.parentId : auth.parent_id,
+ 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/auth/components/Menu.jsx b/src/lib/auth/components/Menu.jsx
index 4682dbab..df33314c 100644
--- a/src/lib/auth/components/Menu.jsx
+++ b/src/lib/auth/components/Menu.jsx
@@ -197,6 +197,7 @@ const Menu = () => {
<button
type='button'
className='text-gray_r-12/80 p-2 text-left hover:bg-gray_r-5 '
+ onClick={() => logout()}
>
<div className='flex gap-x-3 items-center'>
<ImageNext
diff --git a/src/lib/category/components/Category.jsx b/src/lib/category/components/Category.jsx
index 91553295..15bfabc9 100644
--- a/src/lib/category/components/Category.jsx
+++ b/src/lib/category/components/Category.jsx
@@ -14,7 +14,7 @@ const Category = () => {
const [openCategories, setOpenCategory] = useState([]);
const [banner, setBanner] = useState([]);
- const promotionProgram = useQuery('banner-promo-category-card', bannerApi({ type: 'banner-promo-category-card' }));
+ // const promotionProgram = useQuery('banner-promo-category-card', bannerApi({ type: 'banner-promo-category-card' }));
useEffect(() => {
const loadCategories = async () => {
diff --git a/src/lib/checkout/components/Checkout.jsx b/src/lib/checkout/components/Checkout.jsx
index e3b586f1..5256a328 100644
--- a/src/lib/checkout/components/Checkout.jsx
+++ b/src/lib/checkout/components/Checkout.jsx
@@ -39,18 +39,20 @@ const { getProductsCheckout } = require('../api/checkoutApi');
function convertToInternational(number) {
if (typeof number !== 'string') {
- throw new Error("Input harus berupa string");
+ throw new Error('Input harus berupa string');
}
if (number.startsWith('08')) {
- return '+62' + number.slice(2);
+ return '+62' + number.slice(2);
}
return 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;
@@ -145,15 +147,31 @@ const Checkout = () => {
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,
+ 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 {
@@ -163,10 +181,23 @@ 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);
@@ -482,13 +513,25 @@ const Checkout = () => {
if (query) {
data.source = 'buy';
}
- if (poNumber.current.value) data.po_number = poNumber.current.value;
+ if (poNumber.current.value) {
+ if (typeof file == 'undefined') {
+ toast.error(
+ 'Nomor PO ' +
+ poNumber.current.value +
+ ' telah dimasukkan, Harap upload file PO yang dimaksud'
+ );
+ setIsLoading(false);
+ return;
+ }
+ data.po_number = poNumber.current.value;
+ }
if (typeof file !== 'undefined') data.po_file = await getFileBase64(file);
const isCheckouted = await checkoutApi({ data });
if (!isCheckouted?.id) {
toast.error('Gagal melakukan transaksi, terjadi kesalahan internal');
+ setIsLoading(false);
return;
} else {
gtagPurchase(products, biayaKirim, isCheckouted.name);
@@ -504,10 +547,12 @@ const Checkout = () => {
gtag('set', 'user_data', {
email: auth.email,
- phone_number: convertToInternational(auth.mobile) ?? convertToInternational(auth.phone),
+ phone_number:
+ convertToInternational(auth.mobile) ??
+ convertToInternational(auth.phone),
});
- gtag('config', 'AW-954540379', { ' allow_enhanced_conversions':true } ) ;
+ gtag('config', 'AW-954540379', { ' allow_enhanced_conversions': true });
for (const product of products) deleteItemCart({ productId: product.id });
if (grandTotal > 0) {
@@ -1114,7 +1159,7 @@ const Checkout = () => {
</Skeleton>
)}
<Divider />
- <SectionValidation address={selectedAddress.invoicing} />
+ <SectionValidation address={selectedAddress.shipping} />
<SectionExpedisi
address={selectedAddress.shipping}
listExpedisi={listExpedisi}
@@ -1210,7 +1255,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'>
@@ -1412,7 +1459,7 @@ const Checkout = () => {
</Skeleton>
)}
<Divider />
- <SectionValidation address={selectedAddress.invoicing} />
+ <SectionValidation address={selectedAddress.shipping} />
<SectionExpedisi
address={selectedAddress.shipping}
listExpedisi={listExpedisi}
@@ -1514,7 +1561,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'>
@@ -1697,7 +1746,8 @@ const SectionAddress = ({ address, label, url }) => (
);
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.{' '}
@@ -1711,7 +1761,7 @@ 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 623152c6..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?.rajaongkirCityId == 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/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/home/components/ServiceList.jsx b/src/lib/home/components/ServiceList.jsx
index e32e8747..8fa1cf22 100644
--- a/src/lib/home/components/ServiceList.jsx
+++ b/src/lib/home/components/ServiceList.jsx
@@ -59,7 +59,7 @@ const ServiceList = () => {
</div>
<div className='w-full '>
<Link
- href='/pembayaran-tempo-detail'
+ href='/pengajuan-tempo'
className='border border-gray-200 p-2 flex items-center gap-x-2 rounded-lg'
>
<div className=''>
diff --git a/src/lib/pengajuan-tempo/component/Dokumen.jsx b/src/lib/pengajuan-tempo/component/Dokumen.jsx
index f986d47a..aedb9016 100644
--- a/src/lib/pengajuan-tempo/component/Dokumen.jsx
+++ b/src/lib/pengajuan-tempo/component/Dokumen.jsx
@@ -566,7 +566,7 @@ const Dokumen = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
>
<div>
<label className='form-label text-nowrap'>
- Foto tempat kerja
+ Foto Ruangan Tempat Kerja
</label>
<span className='text-xs opacity-60 text-red-500'>
Format: pdf, jpeg, jpg, png. max file size 2MB
@@ -1144,7 +1144,7 @@ const Dokumen = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
ref={dokumenTempatBekerjaRef}
>
<label className='form-label text-nowrap'>
- Foto tempat kerja
+ Foto Ruangan Tempat Kerja
</label>
<div className='flex flex-row gap-2'>
<label
diff --git a/src/lib/pengajuan-tempo/component/KonfirmasiDokumen.jsx b/src/lib/pengajuan-tempo/component/KonfirmasiDokumen.jsx
index 66d33b43..7c10f80c 100644
--- a/src/lib/pengajuan-tempo/component/KonfirmasiDokumen.jsx
+++ b/src/lib/pengajuan-tempo/component/KonfirmasiDokumen.jsx
@@ -646,7 +646,7 @@ const KonfirmasiDokumen = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
<div className='w-full flex flex-row items-center '>
<div className='w-2/5'>
<label className='form-label text-nowrap'>
- Foto tempat kerja
+ Foto Ruangan Tempat Kerja
</label>
</div>
<div className='w-3/5'>
@@ -1492,7 +1492,7 @@ const KonfirmasiDokumen = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
<div className='w-full flex flex-row items-center '>
<div className='w-2/5'>
<label className='form-label text-wrap'>
- Foto tempat kerja
+ Foto Ruangan Tempat Kerja
</label>
</div>
<div className='w-3/5'>
diff --git a/src/lib/pengajuan-tempo/component/PengajuanTempo.jsx b/src/lib/pengajuan-tempo/component/PengajuanTempo.jsx
index 5bef5134..7cf201b7 100644
--- a/src/lib/pengajuan-tempo/component/PengajuanTempo.jsx
+++ b/src/lib/pengajuan-tempo/component/PengajuanTempo.jsx
@@ -173,11 +173,11 @@ const PengajuanTempo = () => {
}
return data; // Jika bukan object atau array, kembalikan nilai aslinya
};
-
+ const payment = auth?.parentId ? auth?.parentId : auth?.partnerId;
try {
const dataPaymentTerm = await odooApi(
'GET',
- `/api/v1/partner/detail-tempo/${auth.parentId}`
+ `/api/v1/partner/detail-tempo/${payment}`
);
const transformedData = transformKeysToCamelCase(dataPaymentTerm);
setBigData(transformedData);
@@ -191,7 +191,6 @@ const PengajuanTempo = () => {
useEffect(() => {
const cachedData = bigData;
-
const loadBigData = async () => {
if (cachedData) {
// Ambil kunci-kunci yang relevan berdasarkan currentStep dari stepDivsForm
@@ -218,6 +217,10 @@ const PengajuanTempo = () => {
: String(bigData[key]); // Untuk tipe primitif
// Kirim data yang sudah diubah ke string ke stepDivsUpdateForm
stepDivsUpdateForm[currentStep](key, stringData);
+ if (key == 'dokumenProsedur') {
+ const stringData = bigData[key];
+ stepDivsUpdateForm[currentStep](key, stringData);
+ }
}
});
}
@@ -293,11 +296,14 @@ const PengajuanTempo = () => {
try {
const address = await createPengajuanTempoApi({
id: idTempo,
- user_id: auth.parentId,
+ user_id: auth.parentId ? auth.parentId : auth.partnerId,
partner_id: auth.partnerId,
section: label,
tempo_request: tempoRequest,
...formData,
+ formDokumenProsedur: formData.dokumenProsedur
+ ? JSON.stringify(formData.dokumenProsedur)
+ : false,
});
if (address.id) {
setIdTempo(address.id);
@@ -341,7 +347,7 @@ const PengajuanTempo = () => {
try {
const address = await createPengajuanTempoApi({
id: idTempo,
- user_id: auth.parentId,
+ user_id: auth.parentId ? auth.parentId : auth.partnerId,
partner_id: auth.partnerId,
formDocs: JSON.stringify(formattedDokumen),
});
@@ -402,7 +408,7 @@ const PengajuanTempo = () => {
try {
const address = await createPengajuanTempoApi({
id: idTempo,
- user_id: auth.parentId,
+ user_id: auth.parentId ? auth.parentId : auth.partnerId,
partner_id: auth.partnerId,
formSupplier: JSON.stringify(productOrder),
});
@@ -447,7 +453,7 @@ const PengajuanTempo = () => {
const address = await createPengajuanTempoApi({
id: 0,
partner_id: auth.partnerId,
- user_id: auth.parentId,
+ user_id: auth.parentId ? auth.parentId : auth.partnerId,
tempo_request: false,
...form,
});
@@ -466,6 +472,9 @@ const PengajuanTempo = () => {
user_id: address2.userId,
tempo_request: false,
...formPengiriman,
+ formDokumenProsedur: formPengiriman.dokumenProsedur
+ ? JSON.stringify(formPengiriman.dokumenProsedur)
+ : false,
});
if (address3.id && formattedDokumen.length > 0) {
// Kirim dokumen yang sudah difilter
@@ -518,7 +527,7 @@ const PengajuanTempo = () => {
toast.dismiss(toastId);
setIsLoading(false);
- toast.error('Terjadi kesalahan dalam pengiriman formulir');
+ toast.error('Terjadi kesalahan dalam pengiriman formulir hehehehe');
console.error(error);
}
};
@@ -647,11 +656,14 @@ const PengajuanTempo = () => {
<TempoTermCondition onCheckChange={handleCheckChange} />
<Button
colorScheme='red'
- w={`${isMobile ? 'full' : '36'}`}
- isDisabled={!isCheckedTNC}
+ w={`${isMobile ? 'full' : '48'}`}
+ isDisabled={!isCheckedTNC || isLoading}
onClick={handleDaftarTempo}
>
- Daftar Tempo {<ChevronRightIcon className='w-5' />}
+ {isLoading
+ ? 'Loading...'
+ : 'Daftar Tempo'}
+ {!isLoading && <ChevronRightIcon className='w-5' />}
</Button>
</div>
)}
diff --git a/src/lib/pengajuan-tempo/component/Pengiriman.jsx b/src/lib/pengajuan-tempo/component/Pengiriman.jsx
index 755cf45d..a8e7fd22 100644
--- a/src/lib/pengajuan-tempo/component/Pengiriman.jsx
+++ b/src/lib/pengajuan-tempo/component/Pengiriman.jsx
@@ -13,6 +13,7 @@ import {
} from '../../../../src-migrate/modules/register/stores/usePengajuanTempoStore';
import { toast } from 'react-hot-toast';
import useDevice from '@/core/hooks/useDevice';
+import getFileBase64 from '@/core/utils/getFileBase64';
const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
const { control, watch, setValue } = useForm();
const { isDesktop, isMobile } = useDevice();
@@ -21,6 +22,7 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
errorsPengiriman,
validatePengiriman,
updateFormPengiriman,
+ updateDokumenProsedur,
} = usePengajuanTempoStorePengiriman();
const { form } = usePengajuanTempoStore();
const [states, setState] = useState([]);
@@ -34,6 +36,7 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
const [zipsInvoice, setZipsInvoice] = useState([]);
const [sameAddress, setSameAddress] = useState(false);
const [sameAddressStreet, setSameAddressStreet] = useState(false);
+ const [nameFile, setNameFile] = useState();
const [selectedIds, setSelectedIds] = useState(
formPengiriman.dokumenPengiriman
@@ -299,11 +302,66 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
validatePengiriman();
};
+ const handleInputChangeFile = async (event) => {
+ let fileBase64 = '';
+ const { name } = event.target;
+ const file = event.target.files?.[0];
+ // Allowed file extensions
+ const allowedExtensions = ['pdf', 'png', 'jpg', 'jpeg'];
+ let fileExtension = '';
+ if (file) {
+ fileExtension = file.name.split('.').pop()?.toLowerCase(); // Extract file extension
+
+ // Check if the file extension is allowed
+ if (!fileExtension || !allowedExtensions.includes(fileExtension)) {
+ toast.error(
+ 'Format file yang diijinkan adalah .pdf, .png, .jpg, atau .jpeg',
+ { duration: 4000 }
+ );
+
+ event.target.value = '';
+ return;
+ }
+
+ // Check for file size
+ 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);
+ // updateFormDokumen(
+ // name,
+ // compressedFile.name,
+ // fileExtension,
+ // fileBase64
+ // );
+ // } catch (error) {
+ // toast.error('Gagal mengompresi file', { duration: 4000 });
+ // }
+ toast.error('Max File Upload 2MB', { duration: 4000 });
+ } else {
+ // Convert file to Base64
+ fileBase64 = await getFileBase64(file);
+ updateDokumenProsedur(file.name, fileExtension, fileBase64);
+ }
+ validatePengiriman();
+ }
+ };
const isFormValid = useMemo(
() => Object.keys(errorsPengiriman).length === 0,
[errorsPengiriman]
);
+ const tukarInvoiceInputRef = useRef(null);
+ const tukarInvoiceInputPembayaranRef = useRef(null);
const PICNameRef = useRef(null);
const streetPengirimanRef = useRef(null);
const statePengirimanRef = useRef(null);
@@ -311,7 +369,9 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
const districtPengirimanRef = useRef(null);
const subDistrictPengirimanRef = useRef(null);
const zipRef = useRef(null);
+ const PICBarangMobileRef = useRef(null);
const invoicePicRef = useRef(null);
+ const invoicePicMobileRef = useRef(null);
const streetInvoiceRef = useRef(null);
const stateInvoiceRef = useRef(null);
const cityInvoiceRef = useRef(null);
@@ -332,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;
@@ -366,10 +434,18 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
zipRef.current.scrollIntoView(options);
return;
}
+ if (errorsPengiriman.PICBarangMobile && PICBarangMobileRef.current) {
+ PICBarangMobileRef.current.scrollIntoView(options);
+ return;
+ }
if (errorsPengiriman.invoicePic && invoicePicRef.current) {
invoicePicRef.current.scrollIntoView(options);
return;
}
+ if (errorsPengiriman.invoicePicMobile && invoicePicMobileRef.current) {
+ invoicePicMobileRef.current.scrollIntoView(options);
+ return;
+ }
if (errorsPengiriman.streetInvoice && streetInvoiceRef.current) {
streetInvoiceRef.current.scrollIntoView(options);
return;
@@ -803,6 +879,38 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
<div className='flex flex-row justify-between items-start'>
<div className='w-2/5'>
<label className='form-label text-nowrap'>
+ No. HP PIC Penerimaan Barang
+ </label>
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ isi nomor PIC penerimaan barang yang bertanggung jawab di
+ perusahaan anda
+ </span>
+ )}
+ </div>
+ <div className='w-3/5'>
+ <input
+ id='PICBarangMobile'
+ name='PICBarangMobile'
+ ref={PICBarangMobileRef}
+ placeholder='Masukkan nomor PIC penerimaan barang'
+ value={formPengiriman.PICBarangMobile}
+ type='tel'
+ className='form-input'
+ aria-invalid={errorsPengiriman.PICBarangMobile}
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.PICBarangMobile}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
Alamat Pengiriman Barang
</label>
{!isKonfirmasi && (
@@ -954,9 +1062,7 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
<HookFormSelect
{...props}
options={zips}
- disabled={
- !watchsubDistrict || sameAddressStreet
- }
+ disabled={sameAddressStreet}
placeholder='Zip'
/>
) : (
@@ -967,9 +1073,7 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
ref={zipRef}
placeholder='Kode Pos'
type='number'
- disabled={
- !watchsubDistrict || sameAddressStreet
- }
+ disabled={sameAddressStreet}
value={formPengiriman.zipPengiriman}
className='form-input'
onChange={handleInputChange}
@@ -1035,6 +1139,38 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
<div className='flex flex-row justify-between items-start'>
<div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ No. HP PIC Penerimaan Invoice
+ </label>
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ isi nomor PIC penerimaan invoice yang bertanggung jawab di
+ perusahaan anda
+ </span>
+ )}
+ </div>
+ <div className='w-3/5'>
+ <input
+ id='invoicePicMobile'
+ name='invoicePicMobile'
+ ref={invoicePicMobileRef}
+ placeholder='Masukkan nomor PIC Penerimaan Invoice'
+ value={formPengiriman.invoicePicMobile}
+ type='tel'
+ className='form-input'
+ aria-invalid={errorsPengiriman.invoicePicMobile}
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.invoicePicMobile}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5'>
<label className='form-label text-nowrap'>
Alamat Pengiriman Invoice
</label>
@@ -1192,9 +1328,7 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
<HookFormSelect
{...props}
options={zipsInvoice}
- disabled={
- !watchsubDistrictInvoice || sameAddress
- }
+ disabled={sameAddress}
placeholder='Zip'
/>
) : (
@@ -1204,9 +1338,7 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
ref={zipInvoiceRef}
placeholder='Kode Pos'
type='number'
- disabled={
- !watchsubDistrictInvoice || sameAddress
- }
+ disabled={sameAddress}
value={formPengiriman.zipInvoice}
className='form-input'
onChange={handleInputChange}
@@ -1231,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>
@@ -1248,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>
@@ -1258,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>
@@ -1273,10 +1411,60 @@ 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>
+
+ <div className='w-full flex flex-row justify-between items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ Prosedur Pengiriman{' '}
+ <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ <span className='text-xs opacity-60 text-red-500'>
+ Format: pdf, jpeg, jpg, png. max file size 2MB
+ </span>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start gap-2'>
+ <label
+ htmlFor='dokumenProsedur'
+ className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+ >
+ {formPengiriman?.dokumenProsedur?.name
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ id='dokumenProsedur'
+ name='dokumenProsedur'
+ type='file'
+ title=' '
+ className='hidden'
+ aria-invalid={formPengiriman.dokumenProsedur}
+ onChange={handleInputChangeFile}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='text-gray-600 line-clamp-2'>
+ {formPengiriman?.dokumenProsedur?.name}
+ </span>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.dokumenProsedur}
+ </div>
+ )}
</div>
</div>
@@ -1483,6 +1671,30 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
</div>
<div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label text-nowrap'>
+ No. HP PIC Penerimaan Barang
+ </label>
+ <div className='flex items-center border border-gray-300 rounded-md w-full'>
+ <input
+ id='PICBarangMobile'
+ name='PICBarangMobile'
+ ref={PICBarangMobileRef}
+ value={formPengiriman.PICBarangMobile}
+ placeholder='Masukkan nomor PIC penerimaan barang'
+ type='tel'
+ className='form-input'
+ aria-invalid={errorsPengiriman.PICBarangMobile}
+ onChange={handleInputChange}
+ />
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.PICBarangMobile}
+ </div>
+ )}
+ </div>
+
+ <div className='flex flex-col gap-2 justify-between items-start'>
<label className='form-label text-nowrap'>
Alamat Pengiriman Barang
</label>
@@ -1701,6 +1913,30 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
</div>
<div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label text-nowrap'>
+ No. HP PIC Penerimaan Invoice
+ </label>
+ <div className='flex items-center border border-gray-300 rounded-md w-full'>
+ <input
+ id='invoicePicMobile'
+ name='invoicePicMobile'
+ ref={invoicePicMobileRef}
+ value={formPengiriman.invoicePicMobile}
+ placeholder='Masukkan nomor PIC Penerimaan Invoice'
+ type='tel'
+ className='form-input'
+ aria-invalid={errorsPengiriman.invoicePicMobile}
+ onChange={handleInputChange}
+ />
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.PICBarangMobile}
+ </div>
+ )}
+ </div>
+
+ <div className='flex flex-col gap-2 justify-between items-start'>
<label className='form-label text-nowrap'>
Alamat Pengiriman Invoice
</label>
@@ -1920,6 +2156,48 @@ const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
</div>
</div>
+ <div className='w-full flex flex-col justify-between items-start gap-2'>
+ <div className=''>
+ <label className='form-label text-nowrap'>
+ Prosedur Pengiriman{' '}
+ <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ </div>
+ <div className='flex flex-col gap-2'>
+ <div className='flex flex-row items-start gap-2'>
+ <label
+ htmlFor='dokumenProsedur'
+ className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+ >
+ {formPengiriman?.dokumenProsedur?.name
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ id='dokumenProsedur'
+ name='dokumenProsedur'
+ type='file'
+ title=' '
+ className='hidden'
+ aria-invalid={errorsPengiriman.dokumenProsedur}
+ onChange={handleInputChangeFile}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2'>
+ {formPengiriman?.dokumenProsedur?.name}
+ </span>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.dokumenProsedur}
+ </div>
+ )}
+ <span className='text-xs opacity-60 text-red-500'>
+ Format: pdf, jpeg, jpg, png. max file size 2MB
+ </span>
+ </div>
+ </div>
+
<div className='flex flex-col gap-2 justify-between items-start'>
<label className='form-label text-wrap'>
Apakah ada dokumen tanda terima yang diberikan pada saat
diff --git a/src/lib/pengajuan-tempo/component/informasiPerusahaan.jsx b/src/lib/pengajuan-tempo/component/informasiPerusahaan.jsx
index 25a3a7ee..4898a3f8 100644
--- a/src/lib/pengajuan-tempo/component/informasiPerusahaan.jsx
+++ b/src/lib/pengajuan-tempo/component/informasiPerusahaan.jsx
@@ -470,7 +470,7 @@ const InformasiPerusahaan = ({
useEffect(() => {
const loadProfile = async () => {
try {
- const dataProfile = await addressApi({ id: auth.parentId });
+ const dataProfile = await addressApi({ id: auth.parentId ? auth.parentId : auth.partnerId });
if (dataProfile.name) {
updateForm('name', dataProfile.name);
}
@@ -507,7 +507,7 @@ const InformasiPerusahaan = ({
}
};
- if (auth?.parentId) {
+ if (auth?.parentId || auth?.partnerId) {
loadProfile();
}
}, [auth?.parentId]);
@@ -782,7 +782,7 @@ const InformasiPerusahaan = ({
<HookFormSelect
{...props}
options={zips}
- disabled={!watchsubDistrict}
+ // disabled={!watchsubDistrict}
placeholder='Zip'
/>
) : (
@@ -793,7 +793,7 @@ const InformasiPerusahaan = ({
ref={zipRef}
placeholder='Kode Pos'
type='number'
- disabled={!watchsubDistrict}
+ // disabled={!watchsubDistrict}
value={form.zip}
className='form-input'
onChange={handleInputChange}
@@ -1333,7 +1333,7 @@ const InformasiPerusahaan = ({
<HookFormSelect
{...props}
options={zips}
- disabled={!watchsubDistrict}
+ // disabled={!watchsubDistrict}
placeholder='Zip'
/>
) : (
@@ -1344,7 +1344,7 @@ const InformasiPerusahaan = ({
ref={zipRef}
placeholder='Kode Pos'
type='number'
- disabled={!watchsubDistrict}
+ // disabled={!watchsubDistrict}
value={form.zip}
className='form-input'
onChange={handleInputChange}
diff --git a/src/lib/product/components/Product/ProductDesktopVariant.jsx b/src/lib/product/components/Product/ProductDesktopVariant.jsx
index 5dfd452b..de88e5bb 100644
--- a/src/lib/product/components/Product/ProductDesktopVariant.jsx
+++ b/src/lib/product/components/Product/ProductDesktopVariant.jsx
@@ -26,7 +26,7 @@ import ProductCard from '../ProductCard';
import ProductSimilar from '../ProductSimilar';
import ProductPromoSection from '~/modules/product-promo/components/Section';
import SimilarBottom from '~/modules/product-detail/components/SimilarBottom';
-
+import { gtagAddToCart } from '@/core/utils/googleTag';
const SELF_HOST = process.env.NEXT_PUBLIC_SELF_HOST;
const ProductDesktopVariant = ({
@@ -41,14 +41,38 @@ const ProductDesktopVariant = ({
const { srsltid } = router.query;
const [askAdminUrl, setAskAdminUrl, isApproval] = useState();
const [lowestPrice, setLowestPrice] = useState(null);
-
+ const [qtyPickUp, setQtyPickUp] = useState(0);
const [addCartAlert, setAddCartAlert] = useState(false);
const [isLoadingSLA, setIsLoadingSLA] = useState(true);
-
+ const [selectedVariant, setSelectedVariant] = useState(product.id);
const { setRefreshCart } = useProductCartContext();
const [quantityInput, setQuantityInput] = useState(1);
+ const [activeVariant, setActiveVariant] = useState({
+ id: null,
+ code: product.code,
+ name: product.name,
+ price: lowestPrice,
+ stock: product.stockTotal,
+ weight: product.weight,
+ isFlashSale: product.isFlashSale,
+ });
+
+ useEffect(() => {
+ if (selectedVariant) {
+ setActiveVariant({
+ id: product.id,
+ code: product.code,
+ name: product.name,
+ price: product.price,
+ stock: product.stockTotal,
+ weight: product.weight,
+ isFlashSale: product.isFlashSale,
+ });
+ }
+ }, [selectedVariant, product]);
+
const createdAskUrl = whatsappUrl({
template: 'product',
payload: {
@@ -95,6 +119,7 @@ const ProductDesktopVariant = ({
}
const quantity = quantityInput;
if (!validQuantity(quantity)) return;
+ gtagAddToCart(activeVariant, quantity);
updateItemCart({
productId: product.id,
quantity,
@@ -232,6 +257,17 @@ const ProductDesktopVariant = ({
fetchData();
}, [product]);
+ useEffect(() => {
+ const fetchData = async () => {
+ const qty_available = await odooApi(
+ 'GET',
+ `/api/v1/product_variant/${product.id}/qty_available`
+ );
+ setQtyPickUp(qty_available?.qty);
+ };
+ fetchData();
+ }, [product]);
+
return (
<DesktopView>
<div className='container mx-auto pt-10'>
@@ -451,7 +487,7 @@ const ProductDesktopVariant = ({
</Skeleton>
</div>
<div>
- {product?.sla?.qty > 0 && (
+ {qtyPickUp > 0 && (
<Link href='/panduan-pick-up-service' className='group'>
<Image
src='/images/PICKUP-NOW.png'
@@ -462,6 +498,14 @@ const ProductDesktopVariant = ({
)}
</div>
</div>
+ {qtyPickUp > 0 && (
+ <>
+ <div className='text-[12px] mt-1 text-red-500 italic'>
+ * {qtyPickUp} barang bisa di pickup
+ </div>
+ <div className='h-4' />
+ </>
+ )}
<div className='flex gap-x-3'>
<Button
onClick={() => handleAddToCart(product.id)}
diff --git a/src/lib/product/components/Product/ProductMobile.jsx b/src/lib/product/components/Product/ProductMobile.jsx
index 4cfd3755..16182271 100644
--- a/src/lib/product/components/Product/ProductMobile.jsx
+++ b/src/lib/product/components/Product/ProductMobile.jsx
@@ -95,7 +95,11 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => {
useEffect(() => {
if (!selectedVariant && variantOptions.length == 1) {
- setSelectedVariant(variantOptions[0]);
+ const selectedVariant =
+ variantOptions?.find((variant) => variant.is_in_bu) ||
+ variantOptions?.[0];
+ setSelectedVariant(selectedVariant);
+ // setSelectedVariant(variantOptions[0]);
}
}, [selectedVariant, variantOptions]);
diff --git a/src/lib/product/components/ProductSearch.jsx b/src/lib/product/components/ProductSearch.jsx
index f7b044aa..eb86485d 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 });
@@ -549,7 +546,7 @@ const ProductSearch = ({
<div className='h-6' />
- <SideBanner />
+ <SideBanner query={search} />
</div>
<div className='w-9/12 pl-6'>
diff --git a/src/lib/tempo/components/Tempo.jsx b/src/lib/tempo/components/Tempo.jsx
index c246f3d8..19cd673b 100644
--- a/src/lib/tempo/components/Tempo.jsx
+++ b/src/lib/tempo/components/Tempo.jsx
@@ -153,14 +153,18 @@ const Tempo = () => {
</Skeleton>
</p>
</div>
- <div className='grid grid-flow-col gap-4'>
+ <div className='grid grid-flow-col gap-2'>
<div className='border w-full p-4'>
<p>Sisa Kredit Limit</p>
<Skeleton
isLoaded={!isLoading}
- h='36px'
+ h='fit'
w='full'
- className='text-3xl font-semibold text-green-600'
+ className={`text-3xl font-semibold ${
+ limitTempo - amountDue < 0
+ ? 'text-red-500'
+ : 'text-green-600'
+ } `}
>
{limitTempo && amountDue
? currencyFormat(
@@ -468,7 +472,11 @@ const Tempo = () => {
isLoaded={!isLoading}
// h='36px'
// w={16}
- className='font-semibold text-sm text-nowrap text-green-700 '
+ className={`font-semibold text-sm text-nowrap ${
+ limitTempo - amountDue < 0
+ ? 'text-red-500'
+ : 'text-green-700'
+ }`}
>
{limitTempo && amountDue
? currencyFormat(
diff --git a/src/lib/tracking-order/component/TrackingOrder.jsx b/src/lib/tracking-order/component/TrackingOrder.jsx
index 8a7b2579..31ebd012 100644
--- a/src/lib/tracking-order/component/TrackingOrder.jsx
+++ b/src/lib/tracking-order/component/TrackingOrder.jsx
@@ -8,12 +8,17 @@ import { useQuery } from 'react-query';
import { Spinner } from '@chakra-ui/react';
import { Search } from 'lucide-react';
import Link from 'next/link';
+import odooApi from '~/libs/odooApi';
+import Image from '~/components/ui/image';
+import useDevice from '@/core/hooks/useDevice';
const TrackingOrder = () => {
const [idAWB, setIdAWB] = useState(null);
+ const { isDesktop, isMobile } = useDevice();
const [inputQuery, setInputQuery] = useState(null);
const [buttonClick, setButtonClick] = useState(false);
const [apiError, setApiError] = useState(null); // State to store API error message
+ const [BannerTracking, setBannerTracking] = useState();
const closePopup = () => {
setIdAWB(null);
@@ -65,13 +70,35 @@ const TrackingOrder = () => {
setButtonClick(true);
};
+ useEffect(() => {
+ const getBanner = async () => {
+ const get = await odooApi(
+ 'GET',
+ '/api/v1/banner?type=tracking-order-banner'
+ );
+ setBannerTracking(get[0].image);
+ };
+ getBanner();
+ }, []);
+
return (
<div className='container mx-auto flex py-10 flex-col'>
+ <div className={`${isDesktop && 'min-h-96'} min-w-full`}>
+ {BannerTracking && (
+ <Image
+ src={BannerTracking}
+ alt='Tracking Order'
+ width={500}
+ height={160}
+ className='w-full mt-6'
+ />
+ )}
+ </div>
<h1 className='text-h-sm md:text-title-sm font-semibold mb-6'>
Tracking Order
</h1>
<div className='flex justify-start items-start'>
- <p className='text-base w-full'>
+ <p className={`${isMobile ? 'text-sm' : 'text-base'} w-full text-pretty `}>
{`Untuk melacak pesanan Anda, masukkan Nomor Transaksi di kotak bawah ini dan masukkan Email login anda lalu tekan tombol "Lacak". Nomor Transaksi ini dapat Anda lihat dalam menu `}
<Link href='/my/transactions' className='text-red-500'>
Daftar Transaksi
@@ -93,7 +120,11 @@ const TrackingOrder = () => {
onSubmit={handleSubmit(onSubmitHandler)}
className='flex mt-4 flex-row w-full '
>
- <div className='w-[90%] grid grid-cols-2 gap-4'>
+ <div
+ className={`w-[90%] grid ${
+ isMobile ? 'grid-rows-3' : 'grid-cols-2'
+ } gap-4`}
+ >
<div className='flex flex-col '>
<label className='form-label mb-2'>ID Pesanan*</label>
<input
diff --git a/src/lib/transaction/components/Transaction.jsx b/src/lib/transaction/components/Transaction.jsx
index b2fb2c17..2ca7d386 100644
--- a/src/lib/transaction/components/Transaction.jsx
+++ b/src/lib/transaction/components/Transaction.jsx
@@ -885,7 +885,7 @@ const Transaction = ({ id }) => {
? `| ${product?.attributes.join(', ')}`
: ''}
</div>
- {product.soQty && product.reservedStockQty && (
+ {product.soQty && (
<div className='text-[10px] text-red-500 italic mt-2'>
{product.soQty !== product.reservedStockQty
? 'Barang sedang disiapkan'
diff --git a/src/lib/variant/components/VariantCard.jsx b/src/lib/variant/components/VariantCard.jsx
index 08b7a97e..79746995 100644
--- a/src/lib/variant/components/VariantCard.jsx
+++ b/src/lib/variant/components/VariantCard.jsx
@@ -7,12 +7,12 @@ import { createSlug } from '@/core/utils/slug'
import currencyFormat from '@/core/utils/currencyFormat'
import { updateItemCart } from '@/core/utils/cart'
import whatsappUrl from '@/core/utils/whatsappUrl'
-import {useState } from 'react';
+import { useEffect, useState } from 'react';
import rejectProductApi from '../../../lib/transaction/api/rejectProductApi'
// import {useTransaction} from 'C:\Users\Indoteknik\next-indoteknik\src\lib\transaction\hooks\useTransaction.js'
import useTransaction from '../../../lib/transaction/hooks/useTransaction';
import ImageNext from 'next/image';
-
+import odooApi from '~/libs/odooApi';
const VariantCard = ({ product, openOnClick = true, buyMore = false }) => {
const router = useRouter()
const id = router.query.id
@@ -21,7 +21,7 @@ const VariantCard = ({ product, openOnClick = true, buyMore = false }) => {
const [isModalOpen, setIsModalOpen] = useState(false);
const [selectedProduct, setSelectedProduct] = useState(null);
const [reason, setReason] = useState('');
-
+ const [qtyPickUp, setQtyPickUp] = useState(0);
const addItemToCart = () => {
@@ -67,6 +67,17 @@ const VariantCard = ({ product, openOnClick = true, buyMore = false }) => {
toast.error('Gagal reject produk. Silakan coba lagi.');
}
};
+
+ useEffect(() => {
+ const fetchData = async () => {
+ const qty_available = await odooApi(
+ 'GET',
+ `/api/v1/product_variant/${product.id}/qty_available`
+ );
+ setQtyPickUp(qty_available?.qty);
+ };
+ fetchData();
+ }, [product]);
const Card = () => (
<div className='flex gap-x-3'>
@@ -117,10 +128,18 @@ const VariantCard = ({ product, openOnClick = true, buyMore = false }) => {
<p className='text-caption-2 text-gray_r-11 mt-1'>
Berat Item : {product?.weight} Kg x {product?.quantity} Barang
</p>
- <p className='text-[10px] text-red-500 italic mt-2'>
- {product.availableQuantity} barang ini bisa di pickup maksimal pukul
+ {product.soQty && (
+ <div className='text-[10px] text-red-500 italic mt-2'>
+ {product.soQty !== product.reservedStockQty
+ ? 'Barang sedang disiapkan'
+ : `${product.reservedStockQty} barang bisa di
+ kirim/pickup`}
+ </div>
+ )}
+ {/* <p className='text-[10px] text-red-500 italic mt-2'>
+ {qtyPickUp} barang ini bisa di pickup maksimal pukul
16.00
- </p>
+ </p> */}
<div className='flex flex-wrap gap-x-1 items-center mt-auto'>
{product.hasFlashsale && (
<>
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..a1eecc52 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) {
diff --git a/src/pages/api/webhook/biteship.js b/src/pages/api/webhook/biteship.js
new file mode 100644
index 00000000..f1100d2d
--- /dev/null
+++ b/src/pages/api/webhook/biteship.js
@@ -0,0 +1,13 @@
+import odooApi from '@/core/api/odooApi';
+
+
+export default async function handler(req, res) {
+ const body = req.body
+
+ if(req.body){
+ let result = odooApi('POST', '/api/v1/webhook/biteship', body)
+ }
+
+ res.status(200).send("ok");
+
+} \ No newline at end of file
diff --git a/src/pages/google_merchant/products/[page].js b/src/pages/google_merchant/products/[page].js
index 8dd28a39..63d52d91 100644
--- a/src/pages/google_merchant/products/[page].js
+++ b/src/pages/google_merchant/products/[page].js
@@ -98,7 +98,8 @@ export async function getServerSideProps({ res, query }) {
'g:price': {
'#text': `${Math.round(product.lowestPrice.price * PPN)} IDR`,
},
- 'g:custom_label_3': {
+ // 'g:custom_label_3': { '#text': product.categories[0]?.name || 'Tidak Ada Kategori' },
+ 'g:custom_label_4': {
'#text': category_level_1?.name_s || 'Tidak Ada Kategori',
},
};
diff --git a/src/pages/index.jsx b/src/pages/index.jsx
index fce2202a..df5047a3 100644
--- a/src/pages/index.jsx
+++ b/src/pages/index.jsx
@@ -97,6 +97,20 @@ export default function Home({ categoryId }) {
'indoteknik, indoteknik.com, toko teknik, toko perkakas, jual genset, jual fogging, jual krisbow, harga krisbow, harga alat safety, harga pompa air',
},
]}
+ openGraph={
+ {
+ title : 'Indoteknik.com: B2B Industrial Supply & Solution',
+ description : 'Temukan pilihan produk B2B Industri &amp; Alat Teknik untuk Perusahaan, UMKM &amp; Pemerintah dengan lengkap, mudah dan transparan.',
+ images: [
+ {
+ url: 'https://indoteknik.com/icon.jpg',
+ width: 800,
+ height: 600,
+ alt: 'indoteknik.com',
+ },
+ ],
+ }
+ }
/>
<PagePopupIformation />
diff --git a/src/pages/my/profile.jsx b/src/pages/my/profile.jsx
index eaf3341c..859b6960 100644
--- a/src/pages/my/profile.jsx
+++ b/src/pages/my/profile.jsx
@@ -71,18 +71,19 @@ export default function Profile() {
<IsAuth>
<MobileView>
<AppLayout title='Akun Saya'>
- {!auth?.parentId && !ubahAkun && (
- <div className='text-sm p-4 flex items-center'>
- <Checkbox
- borderColor='gray.600'
- colorScheme='red'
- size='lg'
- isChecked={isChecked}
- onChange={handleChange}
- />
- <p className='ml-2'>Ubah ke akun bisnis</p>
- </div>
- )}
+ {auth?.company ||
+ (!ubahAkun && (
+ <div className='text-sm p-4 flex items-center'>
+ <Checkbox
+ borderColor='gray.600'
+ colorScheme='red'
+ size='lg'
+ isChecked={isChecked}
+ onChange={handleChange}
+ />
+ <p className='ml-2'>Ubah ke akun bisnis</p>
+ </div>
+ ))}
{isChecked && (
<div>
<SwitchAccount company_type='nonpkp' />
@@ -97,7 +98,7 @@ export default function Profile() {
)} */}
<PersonalProfile />
<Divider />
- {auth?.parentId && <CompanyProfile />}
+ {(auth?.parentId || auth?.company) && <CompanyProfile />}
</AppLayout>
</MobileView>
@@ -108,18 +109,19 @@ export default function Profile() {
<Menu />
</div>
<div className='w-9/12 bg-white border border-gray_r-6 rounded'>
- {!auth?.parentId && !ubahAkun && (
- <div className='text-sm p-4 flex items-center'>
- <Checkbox
- borderColor='gray.600'
- colorScheme='red'
- size='lg'
- isChecked={isChecked}
- onChange={handleChange}
- />
- <p className='ml-2'>Ubah ke akun bisnis</p>
- </div>
- )}
+ {auth?.company ||
+ (!ubahAkun && (
+ <div className='text-sm p-4 flex items-center'>
+ <Checkbox
+ borderColor='gray.600'
+ colorScheme='red'
+ size='lg'
+ isChecked={isChecked}
+ onChange={handleChange}
+ />
+ <p className='ml-2'>Ubah ke akun bisnis</p>
+ </div>
+ ))}
{isChecked && (
<div>
<SwitchAccount company_type='nonpkp' />
@@ -134,7 +136,7 @@ export default function Profile() {
)} */}
<PersonalProfile />
<Divider />
- {auth?.parentId && <CompanyProfile />}
+ {(auth?.parentId || auth?.company) && <CompanyProfile />}
</div>
</div>
</BasicLayout>
diff --git a/src/pages/my/tempo/index.jsx b/src/pages/my/tempo/index.jsx
index 5fb9deba..6ca5c718 100644
--- a/src/pages/my/tempo/index.jsx
+++ b/src/pages/my/tempo/index.jsx
@@ -13,39 +13,41 @@ export default function MyTempo() {
const router = useRouter();
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
+ setIsLoading(true);
if (!auth) {
const nextUrl = encodeURIComponent(router.asPath);
router.push(`/login?next=${nextUrl}`);
- }
- // else if (
- // (auth.tempoProgres === '' || auth.tempoProgres === 'rejected') &&
- // !auth.company
- // ) {
- // router.push('/pengajuan-tempo');
- // }
- else {
+ } else {
+ if (
+ !auth.partnerTempo &&
+ (!auth.partnerTempo || auth.tempoProgres === 'review')
+ ) {
+ setIsLoading(true);
+ router.push('/pengajuan-tempo');
+ }
setIsLoading(false);
}
}, [auth]);
- if (isLoading || !auth) {
- return null; // Tidak render apa pun selama loading atau auth/tempo belum tersedia
- }
- return (
- <IsAuth>
- <Seo title='Tempo - Indoteknik.com' />
+ if (isLoading && !auth) {
+ return null;
+ } else {
+ return (
+ <IsAuth>
+ <Seo title='Tempo - Indoteknik.com' />
- <MobileView>
- <AppLayout title='Pembayaran Tempo'>
- <InvoicesComponent />
- </AppLayout>
- </MobileView>
+ <MobileView>
+ <AppLayout title='Pembayaran Tempo'>
+ <InvoicesComponent />
+ </AppLayout>
+ </MobileView>
- <DesktopView>
- <BasicLayout>
- <InvoicesComponent />
- </BasicLayout>
- </DesktopView>
- </IsAuth>
- );
+ <DesktopView>
+ <BasicLayout>
+ <InvoicesComponent />
+ </BasicLayout>
+ </DesktopView>
+ </IsAuth>
+ );
+ }
}
diff --git a/src/pages/pengajuan-tempo/index.jsx b/src/pages/pengajuan-tempo/index.jsx
index 6987bd29..5ddd3e18 100644
--- a/src/pages/pengajuan-tempo/index.jsx
+++ b/src/pages/pengajuan-tempo/index.jsx
@@ -48,7 +48,7 @@ export default function TrackingOrder() {
router.push(`/login?next=${nextUrl}`);
} else if (auth.tempoProgres === 'approve' || auth?.partnerTempo) {
router.push('/pengajuan-tempo/approve');
- } else if (!auth.parentId) {
+ } else if (!auth.parentId && !auth.company) {
router.push('/pengajuan-tempo/switch-account');
} else if (auth.tempoProgres === 'review' && !tempo?.paymentTerm) {
router.push('/pengajuan-tempo/review');
diff --git a/src/pages/sitemap/products/[page].js b/src/pages/sitemap/products/[page].js
index e39755d6..3419489b 100644
--- a/src/pages/sitemap/products/[page].js
+++ b/src/pages/sitemap/products/[page].js
@@ -7,7 +7,10 @@ export async function getServerSideProps({ query, res }) {
const baseUrl = process.env.SELF_HOST + '/shop/product/'
const { page } = query
const limit = 2500
- const queries = { limit, page: page.replace('.xml', '') }
+ const queries = { limit, page: page.replace('.xml', ''),
+ '-publish_b': false,
+ product_rating_f: '[8 TO *]',
+ price_tier1_v2_f: '[1 TO *]', }
const products = await productSearchApi({ query: _.toQuery(queries) })
const sitemap = create('urlset', { encoding: 'utf-8' }).att(
'xmlns',
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 || '',