summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authortrisusilo48 <tri.susilo@altama.co.id>2025-01-20 10:39:35 +0700
committertrisusilo48 <tri.susilo@altama.co.id>2025-01-20 10:39:35 +0700
commit00afe24409bf3cd517597e7c59cf1c12535c54c0 (patch)
tree2e3e573ead141c8381e9b962b442de3a80f17529 /src/lib
parentbd4cdf2125f717875ba90e03893b319dd962f753 (diff)
parent389382046d804053d8e5c6de13d7d0b197175022 (diff)
Merge branch 'new-release' into feature/integrasi_biteship
# Conflicts: # src/lib/address/components/CreateAddress.jsx # src/lib/address/components/EditAddress.jsx
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/address/api/stateApi.js12
-rw-r--r--src/lib/address/components/CreateAddress.jsx2
-rw-r--r--src/lib/address/components/EditAddress.jsx11
-rw-r--r--src/lib/auth/api/switchAccountApi.js14
-rw-r--r--src/lib/auth/api/switchAccountProgresApi.js13
-rw-r--r--src/lib/auth/components/CompanyProfile.jsx155
-rw-r--r--src/lib/auth/components/Menu.jsx267
-rw-r--r--src/lib/auth/components/PersonalProfile.jsx154
-rw-r--r--src/lib/auth/components/StatusSwitchAccount.jsx5
-rw-r--r--src/lib/auth/components/SwitchAccount.jsx301
-rw-r--r--src/lib/cart/components/Cart.jsx3
-rw-r--r--src/lib/cart/components/CartOld.jsx3
-rw-r--r--src/lib/cart/components/Cartheader.jsx3
-rw-r--r--src/lib/checkout/components/Checkout.jsx6
-rw-r--r--src/lib/checkout/components/CheckoutOld.jsx2
-rw-r--r--src/lib/checkout/email/FinishCheckoutEmail.jsx5
-rw-r--r--src/lib/flashSale/components/FlashSale.jsx14
-rw-r--r--src/lib/form/components/KunjunganSales.jsx215
-rw-r--r--src/lib/form/components/KunjunganService.jsx361
-rw-r--r--src/lib/form/components/Merchant.jsx56
-rw-r--r--src/lib/form/components/RequestForQuotation.jsx215
-rw-r--r--src/lib/form/components/SuratDukungan.jsx33
-rw-r--r--src/lib/home/components/ServiceList.jsx2
-rw-r--r--src/lib/invoice/components/Invoice.jsx5
-rw-r--r--src/lib/pengajuan-tempo/api/createPengajuanTempoApi.js14
-rw-r--r--src/lib/pengajuan-tempo/api/editAuthTempo.js13
-rw-r--r--src/lib/pengajuan-tempo/component/Dokumen.jsx1351
-rw-r--r--src/lib/pengajuan-tempo/component/FinishTempo.jsx112
-rw-r--r--src/lib/pengajuan-tempo/component/Konfirmasi.jsx297
-rw-r--r--src/lib/pengajuan-tempo/component/KonfirmasiDokumen.jsx1677
-rw-r--r--src/lib/pengajuan-tempo/component/KontakPerusahaan.jsx684
-rw-r--r--src/lib/pengajuan-tempo/component/PengajuanTempo.jsx716
-rw-r--r--src/lib/pengajuan-tempo/component/Pengiriman.jsx2078
-rw-r--r--src/lib/pengajuan-tempo/component/Referensi.jsx636
-rw-r--r--src/lib/pengajuan-tempo/component/Stepper.jsx65
-rw-r--r--src/lib/pengajuan-tempo/component/informasiPerusahaan.jsx1630
-rw-r--r--src/lib/quotation/components/Quotation.jsx7
-rw-r--r--src/lib/tempo/components/Tempo.jsx754
-rw-r--r--src/lib/transaction/components/Transaction.jsx5
39 files changed, 11372 insertions, 524 deletions
diff --git a/src/lib/address/api/stateApi.js b/src/lib/address/api/stateApi.js
index cea49e7e..6bfd36d2 100644
--- a/src/lib/address/api/stateApi.js
+++ b/src/lib/address/api/stateApi.js
@@ -1,8 +1,8 @@
-import odooApi from '@/core/api/odooApi'
+import odooApi from '@/core/api/odooApi';
-const stateApi = async () => {
- const dataState = await odooApi('GET', '/api/v1/state')
- return dataState
-}
+const stateApi = async ({ tempo = false }) => {
+ const dataState = await odooApi('GET', `/api/v1/state?tempo=${tempo}`);
+ return dataState;
+};
-export default stateApi \ No newline at end of file
+export default stateApi;
diff --git a/src/lib/address/components/CreateAddress.jsx b/src/lib/address/components/CreateAddress.jsx
index 5f041eb2..8c51dd89 100644
--- a/src/lib/address/components/CreateAddress.jsx
+++ b/src/lib/address/components/CreateAddress.jsx
@@ -64,7 +64,7 @@ const CreateAddress = () => {
useEffect(() => {
const loadState = async () => {
- let dataState = await stateApi();
+ let dataState = await stateApi({ tempo: false });
dataState = dataState.map((state) => ({
value: state.id,
label: state.name,
diff --git a/src/lib/address/components/EditAddress.jsx b/src/lib/address/components/EditAddress.jsx
index 7675db9d..60ca20b7 100644
--- a/src/lib/address/components/EditAddress.jsx
+++ b/src/lib/address/components/EditAddress.jsx
@@ -79,13 +79,16 @@ const EditAddress = ({ id, defaultValues }) => {
setValue('alamat_wajib_pajak', dataProfile.alamatWajibPajak);
setValue('alamat_bisnis', dataProfile.alamatBisnis);
setValue('business_name', dataProfile.name);
+ setValue('city', dataProfile.city?.id);
+ setValue('district', dataProfile.district?.id);
+ setValue('subDistrict', dataProfile.subDistrict?.id);
};
if (auth) loadProfile();
}, [auth?.parentId]);
useEffect(() => {
const loadStates = async () => {
- let dataStates = await stateApi();
+ let dataStates = await stateApi({ tempo: false });
dataStates = dataStates.map((state) => ({
value: state.id,
label: state.name,
@@ -133,8 +136,8 @@ const EditAddress = ({ id, defaultValues }) => {
const watchCity = watch('city');
useEffect(() => {
- setValue('district', '');
if (watchCity) {
+ // setValue('district', '');
const loadDistricts = async () => {
let dataDistricts = await districtApi({ cityId: watchCity });
dataDistricts = dataDistricts.map((district) => ({
@@ -144,7 +147,7 @@ const EditAddress = ({ id, defaultValues }) => {
setDistricts(dataDistricts);
let oldDistrict = getValues('oldDistrict');
if (oldDistrict) {
- setValue('district', oldDistrict);
+ // setValue('district', oldDistrict);
setValue('oldDistrict', '');
}
};
@@ -171,8 +174,8 @@ const EditAddress = ({ id, defaultValues }) => {
const watchDistrict = watch('district');
useEffect(() => {
- setValue('subDistrict', '');
if (watchDistrict) {
+ // setValue('subDistrict', '');
const loadSubDistricts = async () => {
let dataSubDistricts = await subDistrictApi({
districtId: watchDistrict,
diff --git a/src/lib/auth/api/switchAccountApi.js b/src/lib/auth/api/switchAccountApi.js
new file mode 100644
index 00000000..79ca2553
--- /dev/null
+++ b/src/lib/auth/api/switchAccountApi.js
@@ -0,0 +1,14 @@
+import odooApi from '@/core/api/odooApi';
+import { getAuth } from '@/core/utils/auth';
+
+const switchAccountApi = async ({ data }) => {
+ const auth = getAuth();
+ const switchAccount = await odooApi(
+ 'PUT',
+ `/api/v1/user/${auth.partnerId}/switch`,
+ data
+ );
+ return switchAccount;
+};
+
+export default switchAccountApi;
diff --git a/src/lib/auth/api/switchAccountProgresApi.js b/src/lib/auth/api/switchAccountProgresApi.js
new file mode 100644
index 00000000..6289a5dd
--- /dev/null
+++ b/src/lib/auth/api/switchAccountProgresApi.js
@@ -0,0 +1,13 @@
+import odooApi from '@/core/api/odooApi';
+import { getAuth } from '@/core/utils/auth';
+
+const switchAccountProgresApi = async () => {
+ const auth = getAuth();
+ const switchAccount = await odooApi(
+ 'GET',
+ `/api/v1/user/${auth.partnerId}/switch_progres`
+ );
+ return switchAccount;
+};
+
+export default switchAccountProgresApi;
diff --git a/src/lib/auth/components/CompanyProfile.jsx b/src/lib/auth/components/CompanyProfile.jsx
index 7bda992f..410d6a23 100644
--- a/src/lib/auth/components/CompanyProfile.jsx
+++ b/src/lib/auth/components/CompanyProfile.jsx
@@ -9,9 +9,14 @@ import { toast } from 'react-hot-toast';
import BottomPopup from '@/core/components/elements/Popup/BottomPopup';
import { yupResolver } from '@hookform/resolvers/yup';
import * as Yup from 'yup';
+import SwitchAccount from '@/lib/auth/components/SwitchAccount';
+import { Checkbox } from '@chakra-ui/react';
const CompanyProfile = () => {
const [changeConfirmation, setChangeConfirmation] = useState(false);
+ const [changeType, setChangeType] = useState(false);
+ const [isChecked, setIsChecked] = useState(false);
+ const [company_type, setCompany_type] = useState('nonpkp');
const auth = useAuth();
const [isOpen, setIsOpen] = useState(false);
const toggle = () => setIsOpen(!isOpen);
@@ -53,14 +58,20 @@ const CompanyProfile = () => {
useEffect(() => {
const loadProfile = async () => {
- const dataProfile = await addressApi({ id: auth.parentId });
- setValue('name', dataProfile.name);
- setValue('industry', dataProfile.industryId);
- setValue('companyType', dataProfile.companyTypeId);
- setValue('taxName', dataProfile.taxName);
- setValue('npwp', dataProfile.npwp);
- setValue('alamat_wajib_pajak', dataProfile.alamatWajibPajak);
- setValue('alamat_bisnis', dataProfile.alamatBisnis);
+ const dataProfile = await addressApi({
+ id: auth.parentId ? auth.parentId : auth.parent_id,
+ });
+ setCompany_type(dataProfile?.companyType);
+ setValue('name', dataProfile?.name);
+ setValue('industry', dataProfile?.industryId);
+ setValue('companyType', dataProfile?.companyTypeId);
+ setValue('taxName', dataProfile?.taxName);
+ setValue('npwp', dataProfile?.npwp);
+ setValue('alamat_wajib_pajak', dataProfile?.alamatWajibPajak);
+ setValue('alamat_bisnis', dataProfile?.alamatBisnis);
+ setValue('company_type', dataProfile?.companyType);
+ setValue('email_bisnis', dataProfile.email);
+ setValue('mobile_bisnis', dataProfile.mobile);
};
if (auth) loadProfile();
}, [auth, setValue]);
@@ -75,6 +86,8 @@ const CompanyProfile = () => {
tax_name: values.taxName,
alamat_lengkap_text: values.alamat_wajib_pajak,
street: values.alamat_bisnis,
+ email: values.email_bisnis,
+ mobile: values.mobile_bisnis,
};
const isUpdated = await odooApi(
'PUT',
@@ -93,10 +106,49 @@ const CompanyProfile = () => {
setChangeConfirmation(false);
handleSubmit(onSubmitHandler)();
};
+ const handleConfirmSubmitType = () => {
+ setChangeType(false);
+ setIsChecked(true);
+ setIsOpen(!isOpen);
+ };
+ const handleChange = async () => {
+ if (isChecked) {
+ setIsChecked(!isChecked);
+ setIsOpen(!isOpen);
+ } else {
+ setIsChecked(!isChecked);
+ setChangeType(true);
+ }
+ };
return (
<>
<BottomPopup
+ active={changeType}
+ close={() => setChangeType(false)} // Menutup popup
+ title='Ubah type akun'
+ >
+ <div className='leading-7 text-gray_r-12/80'>
+ Anda akan mengubah type akun anda?
+ </div>
+ <div className='flex mt-6 gap-x-4 md:justify-end'>
+ <button
+ className='btn-solid-red flex-1 md:flex-none'
+ type='button'
+ onClick={handleConfirmSubmitType}
+ >
+ Yakin
+ </button>
+ <button
+ className='btn-light flex-1 md:flex-none'
+ type='button'
+ onClick={() => setChangeType(false)}
+ >
+ Batal
+ </button>
+ </div>
+ </BottomPopup>
+ <BottomPopup
active={changeConfirmation}
close={() => setChangeConfirmation(true)}
title='Ubah profil Bisnis'
@@ -121,25 +173,37 @@ const CompanyProfile = () => {
</button>
</div>
</BottomPopup>
- <button
- type='button'
- onClick={toggle}
- className='p-4 flex items-center text-left w-full'
- >
+ <div className='p-4 flex-row items-center text-left w-full'>
+ {company_type === 'nonpkp' && (
+ <div className='text-sm mb-2 flex items-center'>
+ <Checkbox
+ borderColor='gray.600'
+ colorScheme='red'
+ size='lg'
+ isChecked={isChecked}
+ onChange={handleChange}
+ />
+ <p className='ml-2'>Ubah ke akun PKP</p>
+ </div>
+ )}
<div>
- <div className='font-semibold mb-2'>Informasi Usaha</div>
+ <div className='font-semibold mb-2 flex flex-row gap-x-2'>
+ <h2>Informasi Usaha</h2>
+ <div className='badge-red'>{company_type.toUpperCase()}</div>
+ </div>
<div className='text-gray_r-11'>
Dibawah ini adalah data usaha yang anda masukkan, periksa kembali
data usaha anda.
</div>
</div>
- <div className='ml-auto p-2 bg-gray_r-3 rounded'>
- {!isOpen && <ChevronDownIcon className='w-6' />}
- {isOpen && <ChevronUpIcon className='w-6' />}
- </div>
- </button>
-
- {isOpen && (
+ {/* <button
+ onClick={toggle}
+ className='btn-yellow w-full sm:w-fit sm:ml-auto min-w-[92px]'
+ >
+ Ubah
+ </button> */}
+ </div>
+ {!isOpen && (
<form
className='p-4 border-t border-gray_r-6'
onSubmit={(e) => {
@@ -199,6 +263,28 @@ const CompanyProfile = () => {
</div>
</div>
<div>
+ <label>Email Bisnis</label>
+ <input
+ {...register('email_bisnis')}
+ type='email'
+ className='form-input mt-3'
+ />
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.email_bisnis?.message}
+ </div>
+ </div>
+ <div>
+ <label>No. Handphone Bisnis</label>
+ <input
+ {...register('mobile_bisnis')}
+ type='tel'
+ className='form-input mt-3'
+ />
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.mobile_bisnis?.message}
+ </div>
+ </div>
+ <div>
<label>Alamat Wajib Pajak</label>
<input
{...register('alamat_wajib_pajak')}
@@ -238,6 +324,7 @@ const CompanyProfile = () => {
</button>
</form>
)}
+ {isOpen && <SwitchAccount company_type={company_type} />}
</>
);
};
@@ -246,18 +333,38 @@ export default CompanyProfile;
const validationSchema = Yup.object().shape({
alamat_bisnis: Yup.string().required('Harus di-isi'),
- alamat_wajib_pajak: Yup.string().required('Harus di-isi'),
- taxName: Yup.string().required('Harus di-isi'),
- npwp: Yup.string().required('Harus di-isi'),
name: Yup.string().required('Harus di-isi'),
+ email_bisnis: Yup.string().required('Harus di-isi'),
+ mobile_bisnis: Yup.string().required('Harus di-isi'),
industry: Yup.string().required('Harus di-pilih'),
companyType: Yup.string().required('Harus di-pilih'),
+ taxName: Yup.string(),
+ npwp: Yup.string(),
+ alamat_wajib_pajak: Yup.string(),
+ company_type: Yup.string(),
+ taxName: Yup.string().when('company_type', {
+ is: (company_type) => company_type !== 'Non PKP',
+ then: Yup.string().required('Harus di-isi'),
+ otherwise: Yup.string().notRequired(),
+ }),
+ npwp: Yup.string().when('company_type', {
+ is: (company_type) => company_type !== 'Non PKP',
+ then: Yup.string().required('Harus di-isi'),
+ otherwise: Yup.string().notRequired(),
+ }),
+ alamat_wajib_pajak: Yup.string().when('company_type', {
+ is: (company_type) => company_type !== 'Non PKP',
+ then: Yup.string().required('Harus di-isi'),
+ otherwise: Yup.string().notRequired(),
+ }),
});
const defaultValues = {
industry: '',
companyType: '',
name: '',
+ email_bisnis: '',
+ mobile_bisnis: '',
taxName: '',
npwp: '',
alamat_wajib_pajak: '',
diff --git a/src/lib/auth/components/Menu.jsx b/src/lib/auth/components/Menu.jsx
index 9cd10ab4..4682dbab 100644
--- a/src/lib/auth/components/Menu.jsx
+++ b/src/lib/auth/components/Menu.jsx
@@ -2,9 +2,24 @@ import Link from '@/core/components/elements/Link/Link';
import { useRouter } from 'next/router';
import ImageNext from 'next/image';
import whatsappUrl from '@/core/utils/whatsappUrl';
+import useAuth from '@/core/hooks/useAuth';
+import switchAccountProgresApi from '@/lib/auth/api/switchAccountProgresApi.js';
+import { useState, useEffect } from 'react';
+import { InfoIcon } from 'lucide-react';
import { deleteAuth } from '@/core/utils/auth';
const Menu = () => {
const router = useRouter();
+ const auth = useAuth();
+ const [ubahAkun, setUbahAkun] = useState();
+ useEffect(() => {
+ const loadProgres = async () => {
+ const progresSwitchAccount = await switchAccountProgresApi();
+ if (progresSwitchAccount) {
+ setUbahAkun(progresSwitchAccount.status);
+ }
+ };
+ loadProgres();
+ }, []);
const routeStartWith = (route) => router.pathname.startsWith(route);
@@ -13,63 +28,134 @@ const Menu = () => {
router.push('/login');
});
};
-
return (
<div className='grid grid-cols-1 bg-white border border-gray_r-6 rounded py-2 px-4 sticky top-48'>
- <div className='mt-4 mb-1 font-medium'>Menu</div>
- <LinkItem href='/my/quotations' active={routeStartWith('/my/quotations')}>
- <div className='flex gap-x-3 items-center'>
- <ImageNext
- src='/images/icon/icon_daftar_quotation.svg'
- width={18}
- height={20}
- />
- <p>Daftar Quotation</p>
- </div>
- </LinkItem>
- <LinkItem
- href='/my/transactions'
- active={routeStartWith('/my/transactions')}
- >
- <div className='flex gap-x-3 items-center'>
- <ImageNext
- src='/images/icon/icon_daftar_transaksi.svg'
- width={18}
- height={20}
- />
- <p>Daftar Transaksi</p>
- </div>
- </LinkItem>
- <LinkItem href='/my/shipments' active={routeStartWith('/my/shipments')}>
- <div className='flex gap-x-3 items-center'>
- <ImageNext
- src='/images/icon/icon_pengiriman.svg'
- width={18}
- height={20}
- />
- <p>Daftar Pengiriman</p>
- </div>
- </LinkItem>
- <LinkItem href='/my/invoices' active={routeStartWith('/my/invoices')}>
- <div className='flex gap-x-3 items-center'>
- <ImageNext
- src='/images/icon/icon_invoice.svg'
- width={18}
- height={20}
- />
- <p>Invoice & Faktur Pajak</p>
+ <div className='flex justify-between py-4'>
+ <div className='font-semibold text-gray_r-12'>Akun Saya</div>
+ <div className='relative group'>
+ {auth?.company && !(ubahAkun === 'pending') && (
+ <>
+ <Link
+ href='/my/profile'
+ className='badge-solid-red mt-1 p-2 flex flex-row items-center gap-x-2'
+ >
+ <p className='text-white'>Akun Bisnis</p>{' '}
+ <InfoIcon size={14} color='white' />
+ </Link>
+ <div className='absolute bottom-full transform -translate-x-1/2 mb-2 hidden group-hover:block bg-gray-700 text-white text-xs rounded py-1 px-2 w-72 text-justify left-36'>
+ Anda terdaftar sebagai akun bisnis, segala bentuk transaksi anda
+ untuk perusahaan yang sudah anda daftarkan di Indoteknik.com
+ </div>
+ </>
+ )}
+ {ubahAkun === 'pending' && (
+ <>
+ <Link
+ href='/my/profile'
+ className='badge-yellow mt-1 p-2 flex flex-row items-center gap-x-2'
+ >
+ <p className='text-warning-900'>Review</p>
+ <InfoIcon size={14} className='text-warning-900' />
+ </Link>
+ <div className='absolute bottom-full transform -translate-x-1/2 mb-2 hidden group-hover:block bg-gray-700 text-white text-xs rounded py-1 px-2 w-72 text-justify left-36'>
+ Proses perubahan akun anda sedang kami review, mohon menunggu
+ hingga 2x24 jam.
+ </div>
+ </>
+ )}
+ {!auth?.company && !(ubahAkun === 'pending') && (
+ <>
+ <Link
+ href='/my/profile'
+ className='badge-gray mt-1 p-2 flex flex-row items-center gap-x-2'
+ >
+ <p className='text-gray_r-10'>Akun Individu</p>
+ <InfoIcon size={14} className='text-gray_r-10' />
+ </Link>
+ <div className='absolute bottom-full left-36 transform -translate-x-1/2 mb-3 w-72 text-justify hidden group-hover:block bg-gray-700 text-white text-xs rounded py-1 px-2'>
+ <p className='whitespace-pre-wrap break-words'>
+ Anda terdaftar sebagai akun individu, Segala bentuk transaksi
+ anda untuk Pribadi/Individu.
+ </p>
+ </div>
+ </>
+ )}
</div>
- </LinkItem>
- <LinkItem href='/my/wishlist' active={routeStartWith('/my/wishlist')}>
- <div className='flex gap-x-3 items-center'>
- <ImageNext
- src='/images/icon/icon_wishlist.svg'
- width={18}
- height={20}
- />
- <p>Wishlist</p>
- </div>
- </LinkItem>
+ </div>
+ <div className='mt-2 mb-1 font-medium'>Menu</div>
+ <div className='flex flex-col gap-y-2'>
+ <LinkItem
+ href='/my/quotations'
+ active={routeStartWith('/my/quotations')}
+ className=''
+ >
+ <div className='flex gap-x-3 items-center'>
+ <ImageNext
+ src='/images/icon/icon_daftar_quotation.svg'
+ width={18}
+ height={20}
+ />
+ <p>Daftar Quotation</p>
+ </div>
+ </LinkItem>
+ <LinkItem
+ href='/my/transactions'
+ active={routeStartWith('/my/transactions')}
+ >
+ <div className='flex gap-x-3 items-center'>
+ <ImageNext
+ src='/images/icon/icon_daftar_transaksi.svg'
+ width={18}
+ height={20}
+ />
+ <p>Daftar Transaksi</p>
+ </div>
+ </LinkItem>
+ <LinkItem href='/my/shipments' active={routeStartWith('/my/shipments')}>
+ <div className='flex gap-x-3 items-center'>
+ <ImageNext
+ src='/images/icon/icon_pengiriman.svg'
+ width={18}
+ height={20}
+ />
+ <p>Daftar Pengiriman</p>
+ </div>
+ </LinkItem>
+ <LinkItem href='/my/invoices' active={routeStartWith('/my/invoices')}>
+ <div className='flex gap-x-3 items-center'>
+ <ImageNext
+ src='/images/icon/icon_invoice.svg'
+ width={18}
+ height={20}
+ />
+ <p>Invoice & Faktur Pajak</p>
+ </div>
+ </LinkItem>
+ {auth &&
+ auth.partnerTempo &&
+ (auth.partnerTempo || auth.tempoProgres === 'review') && (
+ <LinkItem href='/my/tempo' active={routeStartWith('/my/tempo')}>
+ <div className='flex gap-x-3 items-center'>
+ <ImageNext
+ src='/images/icon/icon_tempo.svg'
+ width={18}
+ height={20}
+ />
+ <p>Pembayaran Tempo</p>
+ </div>
+ </LinkItem>
+ )}
+ <LinkItem href='/my/wishlist' active={routeStartWith('/my/wishlist')}>
+ <div className='flex gap-x-3 items-center'>
+ <ImageNext
+ src='/images/icon/icon_wishlist.svg'
+ width={18}
+ height={20}
+ />
+ <p>Wishlist</p>
+ </div>
+ </LinkItem>
+ </div>
<div className='mt-4 mb-1 font-medium'>Pusat Bantuan</div>
<LinkItem
@@ -87,40 +173,41 @@ const Menu = () => {
</div>
</LinkItem>
<div className='mt-4 mb-1 font-medium'>Pengaturan Akun</div>
- <LinkItem href='/my/address' active={routeStartWith('/my/address')}>
- <div className='flex gap-x-3 items-center'>
- <ImageNext
- src='/images/icon/icon_daftar_alamat.svg'
- width={18}
- height={20}
- />
- <p>Daftar Alamat</p>
- </div>
- </LinkItem>
- <LinkItem href='/my/profile' active={routeStartWith('/my/profile')}>
- <div className='flex gap-x-3 items-center'>
- <ImageNext
- src='/images/icon/icon_profile.svg'
- width={18}
- height={20}
- />
- <p>Profil Saya</p>
- </div>
- </LinkItem>
- <button
- type='button'
- onClick={logout}
- className='text-gray_r-12/80 p-2 text-left'
- >
- <div className='flex gap-x-3 items-center'>
- <ImageNext
- src='/images/icon/icon_logout.svg'
- width={18}
- height={20}
- />
- <p>Keluar Akun</p>
- </div>
- </button>
+ <div className='flex flex-col gap-y-2'>
+ <LinkItem href='/my/address' active={routeStartWith('/my/address')}>
+ <div className='flex gap-x-3 items-center'>
+ <ImageNext
+ src='/images/icon/icon_daftar_alamat.svg'
+ width={18}
+ height={20}
+ />
+ <p>Daftar Alamat</p>
+ </div>
+ </LinkItem>
+ <LinkItem href='/my/profile' active={routeStartWith('/my/profile')}>
+ <div className='flex gap-x-3 items-center'>
+ <ImageNext
+ src='/images/icon/icon_profile.svg'
+ width={18}
+ height={20}
+ />
+ <p>Profil Saya</p>
+ </div>
+ </LinkItem>
+ <button
+ type='button'
+ className='text-gray_r-12/80 p-2 text-left hover:bg-gray_r-5 '
+ >
+ <div className='flex gap-x-3 items-center'>
+ <ImageNext
+ src='/images/icon/icon_logout.svg'
+ width={18}
+ height={20}
+ />
+ <p>Keluar Akun</p>
+ </div>
+ </button>
+ </div>
</div>
);
};
@@ -128,8 +215,8 @@ const Menu = () => {
const LinkItem = ({ children, ...props }) => (
<Link
{...props}
- className={`!text-gray_r-12/80 !font-normal p-2 rounded ${
- props.active == true ? 'bg-gray_r-3' : ''
+ className={`!text-gray_r-12/80 !font-normal p-2 rounded transition-colors duration-200 ${
+ props.active ? 'bg-gray_r-3' : 'hover:bg-gray_r-5 '
}`}
>
{children}
diff --git a/src/lib/auth/components/PersonalProfile.jsx b/src/lib/auth/components/PersonalProfile.jsx
index b9fb3f5f..3053255d 100644
--- a/src/lib/auth/components/PersonalProfile.jsx
+++ b/src/lib/auth/components/PersonalProfile.jsx
@@ -1,96 +1,112 @@
-import useAuth from '@/core/hooks/useAuth'
-import { setAuth } from '@/core/utils/auth'
-import addressApi from '@/lib/address/api/addressApi'
-import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/24/outline'
-import { useEffect, useState } from 'react'
-import { useForm } from 'react-hook-form'
-import { toast } from 'react-hot-toast'
-import editPersonalProfileApi from '../api/editPersonalProfileApi'
+import useAuth from '@/core/hooks/useAuth';
+import { setAuth } from '@/core/utils/auth';
+import addressApi from '@/lib/address/api/addressApi';
+import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/24/outline';
+import { useEffect, useState } from 'react';
+import { useForm } from 'react-hook-form';
+import { toast } from 'react-hot-toast';
+import editPersonalProfileApi from '../api/editPersonalProfileApi';
const PersonalProfile = () => {
- const auth = useAuth()
- const [isOpen, setIsOpen] = useState(true)
- const toggle = () => setIsOpen(!isOpen)
+ const auth = useAuth();
+ const [isOpen, setIsOpen] = useState(true);
+ const toggle = () => setIsOpen(!isOpen);
const { register, setValue, handleSubmit } = useForm({
defaultValues: {
email: '',
name: '',
mobile: '',
- password: ''
- }
- })
+ password: '',
+ },
+ });
useEffect(() => {
const loadProfile = async () => {
- const dataProfile = await addressApi({ id: auth.partnerId })
- setValue('email', dataProfile?.email)
- setValue('name', dataProfile?.name)
- setValue('mobile', dataProfile?.mobile)
- }
- if (auth) loadProfile()
- }, [auth, setValue])
+ const dataProfile = await addressApi({
+ id: auth.partnerId ? auth.partnerId : auth.partner_id,
+ });
+ setValue('email', dataProfile?.email);
+ setValue('name', dataProfile?.name);
+ setValue('mobile', dataProfile?.mobile);
+ };
+ if (auth) loadProfile();
+ }, [auth, setValue]);
const onSubmitHandler = async (values) => {
- let data = values
- if (!values.password) delete data.password
- const isUpdated = await editPersonalProfileApi({ data })
+ let data = values;
+ if (!values.password) delete data.password;
+ const isUpdated = await editPersonalProfileApi({ data });
if (isUpdated?.user) {
- setAuth(isUpdated.user)
- setValue('password', '')
- toast.success('Berhasil mengubah profil', { duration: 1500 })
- return
+ setAuth(isUpdated.user);
+ setValue('password', '');
+ toast.success('Berhasil mengubah profil', { duration: 1500 });
+ return;
}
- toast.error('Terjadi kesalahan internal')
- }
+ toast.error('Terjadi kesalahan internal');
+ };
return (
<>
- <button type='button' onClick={toggle} className='p-4 flex items-center text-left w-full'>
+ <div type='button' className='p-4 flex items-center text-left w-full'>
<div>
<div className='font-semibold mb-2'>Informasi Akun</div>
<div className='text-gray_r-11'>
- Dibawah ini adalah data diri yang anda masukan, periksa kembali data diri anda
+ Dibawah ini adalah data diri yang anda masukan, periksa kembali data
+ diri anda
</div>
</div>
- <div className='ml-auto p-2 bg-gray_r-3 rounded'>
- {!isOpen && <ChevronDownIcon className='w-6' />}
- {isOpen && <ChevronUpIcon className='w-6' />}
- </div>
- </button>
+ </div>
- {isOpen && (
- <form className='p-4 border-t border-gray_r-6' onSubmit={handleSubmit(onSubmitHandler)}>
- <div className='grid grid-cols-1 sm:grid-cols-2 gap-4'>
- <div>
- <label>Email</label>
- <input {...register('email')} type='text' disabled className='form-input mt-3' />
- </div>
- <div>
- <label>Nama Lengkap</label>
- <input {...register('name')} type='text' className='form-input mt-3' />
- </div>
- <div>
- <label>No. Handphone</label>
- <input {...register('mobile')} type='tel' className='form-input mt-3' />
- </div>
- <div>
- <label>Kata Sandi</label>
- <input
- {...register('password')}
- type='password'
- className='form-input mt-3'
- placeholder='Isi jika ingin mengubah kata sandi'
- />
- </div>
+ <form
+ className='p-4 border-t border-gray_r-6'
+ onSubmit={handleSubmit(onSubmitHandler)}
+ >
+ <div className='grid grid-cols-1 sm:grid-cols-2 gap-4'>
+ <div>
+ <label>Email</label>
+ <input
+ {...register('email')}
+ type='text'
+ disabled
+ className='form-input mt-3'
+ />
+ </div>
+ <div>
+ <label>Nama Lengkap</label>
+ <input
+ {...register('name')}
+ type='text'
+ className='form-input mt-3'
+ />
</div>
- <button type='submit' className='btn-yellow w-full sm:w-fit sm:ml-auto mt-6'>
- Simpan
- </button>
- </form>
- )}
+ <div>
+ <label>No. Handphone</label>
+ <input
+ {...register('mobile')}
+ type='tel'
+ className='form-input mt-3'
+ />
+ </div>
+ <div>
+ <label>Kata Sandi</label>
+ <input
+ {...register('password')}
+ type='password'
+ className='form-input mt-3'
+ placeholder='Isi jika ingin mengubah kata sandi'
+ />
+ </div>
+ </div>
+ <button
+ type='submit'
+ className='btn-yellow w-full sm:w-fit sm:ml-auto mt-6 max-w-28'
+ >
+ Simpan
+ </button>
+ </form>
</>
- )
-}
+ );
+};
-export default PersonalProfile
+export default PersonalProfile;
diff --git a/src/lib/auth/components/StatusSwitchAccount.jsx b/src/lib/auth/components/StatusSwitchAccount.jsx
new file mode 100644
index 00000000..73316616
--- /dev/null
+++ b/src/lib/auth/components/StatusSwitchAccount.jsx
@@ -0,0 +1,5 @@
+const StatusSwitchAccount = ({ status }) => {
+ return <>Perpindahan akun anda masih {status}</>;
+};
+
+export default StatusSwitchAccount;
diff --git a/src/lib/auth/components/SwitchAccount.jsx b/src/lib/auth/components/SwitchAccount.jsx
new file mode 100644
index 00000000..46e57348
--- /dev/null
+++ b/src/lib/auth/components/SwitchAccount.jsx
@@ -0,0 +1,301 @@
+import useAuth from '@/core/hooks/useAuth';
+import { setAuth } from '@/core/utils/auth';
+import addressApi from '@/lib/address/api/addressApi';
+import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/24/outline';
+import { useEffect, useState, useMemo } from 'react';
+import { useForm } from 'react-hook-form';
+import { toast } from 'react-hot-toast';
+import switchAccountApi from '../api/switchAccountApi';
+import FormBisnis from '~/modules/register/components/FormBisnis.tsx';
+import RegistrasiBisnis from '~/modules/register/components/RegistrasiBisnis.tsx';
+import { Radio, RadioGroup, Stack, Divider, Button } from '@chakra-ui/react';
+import { useRegisterStore } from '~/modules/register/stores/useRegisterStore.ts';
+import { registerUser } from '~/services/auth';
+import { useMutation } from 'react-query';
+import { isValid } from 'zod';
+import useDevice from '@/core/hooks/useDevice';
+import BottomPopup from '@/core/components/elements/Popup/BottomPopup';
+const SwitchAccount = ({ company_type }) => {
+ const { isDesktop, isMobile } = useDevice();
+ const auth = useAuth();
+ const [isOpen, setIsOpen] = useState(true);
+ const toggle = () => setIsOpen(!isOpen);
+ const [isPKP, setIsPKP] = useState(true);
+ const [isTerdaftar, setIsTerdaftar] = useState(false);
+ const [isChecked, setIsChecked] = useState(false);
+ const [selectedValueBisnis, setSelectedValueBisnis] = useState('false');
+ const [selectedValue, setSelectedValue] = useState('PKP');
+ const [buttonSubmitClick, setButtonSubmitClick] = useState(false);
+ const [changeConfirmation, setChangeConfirmation] = useState(false);
+ const { register, setValue, handleSubmit } = useForm({
+ defaultValues: {
+ email: '',
+ name: '',
+ phone: '',
+ password: '',
+ },
+ });
+ const mutation = useMutation({
+ mutationFn: (data) => registerUser(data),
+ });
+ const [notValid, setNotValid] = useState(false);
+ const {
+ form,
+ isCheckedTNC,
+ isValidCaptcha,
+ errors,
+ validate,
+ updateForm,
+ resetForm,
+ } = useRegisterStore();
+ const isFormValid = useMemo(() => Object.keys(errors).length === 0, [errors]);
+ useEffect(() => {
+ const loadProfile = async () => {
+ const dataProfile = await addressApi({ id: auth.partnerId });
+ setValue('email', dataProfile?.email);
+ setValue('name', dataProfile?.name);
+ setValue('phone', dataProfile?.phone);
+ };
+ if (auth) loadProfile();
+ }, [auth, setValue]);
+
+ useEffect(() => {
+ if (selectedValue === 'PKP') {
+ updateForm('is_pkp', 'true');
+ validate();
+ } else {
+ updateForm('is_pkp', 'false');
+ validate();
+ }
+ }, [selectedValue]);
+
+ useEffect(() => {
+ if (isTerdaftar) {
+ updateForm('is_terdaftar', 'true');
+ validate();
+ } else {
+ updateForm('is_terdaftar', 'false');
+ validate();
+ }
+ }, [isTerdaftar]);
+ useEffect(() => {
+ const loadProfile = async () => {
+ const dataProfile = await addressApi({
+ id: auth.parentId ? auth.parentId : auth.parent_id,
+ });
+ if (dataProfile?.companyType === 'nonpkp') {
+ setSelectedValue('PKP');
+ }
+ if (auth?.company) {
+ updateForm('email_partner', dataProfile?.email);
+ updateForm('business_name', dataProfile?.name);
+ updateForm('industry_id', `${dataProfile?.industryId}`);
+ updateForm('company_type_id', `${dataProfile?.companyTypeId}`);
+ updateForm('nama_wajib_pajak', dataProfile?.taxName);
+ updateForm('npwp', dataProfile?.npwp);
+ updateForm('sppkp', dataProfile?.sppkp);
+ updateForm('alamat_wajib_pajak', dataProfile?.alamatWajibPajak);
+ updateForm('alamat_bisnis', dataProfile?.alamatBisnis);
+ validate();
+ }
+ };
+ if (auth) loadProfile();
+ }, [auth, setValue]);
+ useEffect(() => {
+ updateForm('name', '-');
+ updateForm('email', 'example@mail.com');
+ updateForm('password', 'example@mail.com');
+ updateForm('phone', '081234567890');
+ validate();
+ }, [buttonSubmitClick, changeConfirmation]);
+
+ const handleChangeBisnis = (value) => {
+ resetForm();
+ setSelectedValueBisnis(value);
+ if (value === 'true') {
+ validate();
+ setIsTerdaftar(true);
+ } else {
+ validate();
+ setIsTerdaftar(false);
+ }
+ };
+ const handleChange = (value) => {
+ setSelectedValue(value);
+ if (value === 'PKP') {
+ validate();
+ setIsPKP(true);
+ } else {
+ validate();
+ setIsPKP(false);
+ setIsPKP(false);
+ }
+ };
+ const onSubmitHandler = async (values) => {
+ toast.loading('Mengubah status akun...');
+ updateForm('parent_id', `${auth.parentId}`);
+ setChangeConfirmation(false);
+ // let data = { ...form, id: `${auth.partnerId}` };
+ const data = form;
+ if (!isFormValid) {
+ setNotValid(true);
+ setButtonSubmitClick(!buttonSubmitClick);
+ return;
+ } else {
+ setButtonSubmitClick(!buttonSubmitClick);
+ setNotValid(false);
+ }
+ // if (!values.password) delete data.password;
+ const isUpdated = await switchAccountApi({ data });
+
+ if (isUpdated?.switch === 'Pending') {
+ // setAuth(isUpdated.user);
+ // setValue('password', '');
+ toast.success('Berhasil mengubah akun', { duration: 1500 });
+ setTimeout(() => {
+ window.location.reload();
+ }, 1500);
+ return;
+ }
+ toast.error('Terjadi kesalahan internal');
+ };
+
+ const onSubmitHandlerCancel = async (values) => {
+ window.location.reload();
+ };
+
+ return (
+ <>
+ <BottomPopup
+ active={changeConfirmation}
+ close={() => setChangeConfirmation(false)}
+ title='Ubah profil Bisnis'
+ >
+ <div className='leading-7 text-gray_r-12/80'>
+ Anda yakin akan merubah profil bisnis anda dari INDIVIDU menjadi{' '}
+ {selectedValue}?
+ </div>
+ <div className='flex mt-6 gap-x-4 md:justify-end'>
+ <button
+ className='btn-solid-red flex-1 md:flex-none'
+ type='button'
+ onClick={onSubmitHandler}
+ >
+ Ya, Ubah
+ </button>
+ <button
+ className='btn-light flex-1 md:flex-none'
+ type='button'
+ onClick={() => setChangeConfirmation(false)}
+ >
+ Batal
+ </button>
+ </div>
+ </BottomPopup>
+ {/* <div type='button' className='ml-4 flex items-center text-left w-full'>
+ <div
+ className={`flex ${
+ isDesktop ? 'flex-row' : 'flex-col gap-y-2'
+ } items-start justify-start bg-slate-50`}
+ >
+ <div className='flex font-semibold mr-2'>Informasi Bisnis</div>
+ <div className='text-red-500 text-xs'>
+ *Perubahan akun tidak dapat diubah kembali
+ </div>
+ </div>
+ </div> */}
+ <div className='px-4 '>
+ <div
+ class='flex items-center p-4 mb-4 text-sm border border-red-500 text-red-800 rounded-lg bg-red-50'
+ role='alert'
+ >
+ <svg
+ class='flex-shrink-0 inline w-4 h-4 mr-3'
+ aria-hidden='true'
+ fill='currentColor'
+ viewBox='0 0 20 20'
+ >
+ <path d='M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z' />
+ </svg>
+ <span class='sr-only'>Info</span>
+ <div className='text-justify'>
+ Mohon diperhatikan bahwa perubahan data akun bisnis akan
+ mengakibatkan perubahan pada informasi yang tertera di faktur pajak
+ dan invoice.
+ </div>
+ </div>
+ </div>
+
+ <div className='px-4 mb-4'>
+ {!auth?.company && company_type === 'nonpkp' && (
+ <>
+ <div className='mb-4'>
+ <p className='text-black font-bold mb-2'>
+ Bisnis Terdaftar di Indoteknik?
+ </p>
+ <RadioGroup
+ onChange={handleChangeBisnis}
+ value={selectedValueBisnis}
+ >
+ <Stack direction='row'>
+ <Radio colorScheme='red' value='true'>
+ Sudah Terdaftar
+ </Radio>
+ <Radio colorScheme='red' value='false' className='ml-2'>
+ Belum Terdaftar
+ </Radio>
+ </Stack>
+ </RadioGroup>
+ </div>
+ {!isTerdaftar && (
+ <div className=''>
+ <p className='text-black font-bold mb-2'>Tipe Bisnis</p>
+ <RadioGroup onChange={handleChange} value={selectedValue}>
+ <Stack direction='row' className='font-bold'>
+ <Radio colorScheme='red' value='PKP'>
+ PKP
+ </Radio>
+ {!auth?.company && company_type === 'nonpkp' && (
+ <Radio colorScheme='red' value='Non-PKP' className='ml-4'>
+ Non-PKP
+ </Radio>
+ )}
+ </Stack>
+ </RadioGroup>
+ </div>
+ )}
+ </>
+ )}
+ <FormBisnis
+ type={isDesktop ? 'profil' : 'bisnis'}
+ required={isTerdaftar}
+ isPKP={isPKP}
+ chekValid={notValid}
+ buttonSubmitClick={buttonSubmitClick}
+ />
+ <div className='flex flex-row justify-end mt-4 '>
+ <div className='mr-4'>
+ <button
+ type='submit'
+ onClick={() => setChangeConfirmation(true)}
+ className='btn-yellow w-full sm:w-fit sm:ml-auto mt-6 mr-8 md:mr-4'
+ >
+ {mutation.isLoading ? 'Loading...' : 'Simpan Perubahan'}
+ </button>
+ </div>
+ <div>
+ <button
+ type='submit'
+ onClick={onSubmitHandlerCancel}
+ className='btn-solid-red w-full sm:w-fit sm:ml-auto mt-6'
+ >
+ {mutation.isLoading ? 'Loading...' : 'Batal'}
+ </button>
+ </div>
+ </div>
+ </div>
+ </>
+ );
+};
+
+export default SwitchAccount;
diff --git a/src/lib/cart/components/Cart.jsx b/src/lib/cart/components/Cart.jsx
index c6aaa596..1e25d959 100644
--- a/src/lib/cart/components/Cart.jsx
+++ b/src/lib/cart/components/Cart.jsx
@@ -27,6 +27,7 @@ import CardProdcuctsList from '@/core/components/elements/Product/cartProductsLi
// import cardProdcuctsList from '@/core/components/elements/Product/cartProductsList'
const Cart = () => {
+ const PPN = process.env.NEXT_PUBLIC_PPN
const router = useRouter()
const [products, setProducts] = useState(null)
const [isLoading, setIsLoading] = useState(true)
@@ -97,7 +98,7 @@ const Cart = () => {
if (product.canBuy == false) {
toggleSelected(product.id)
}
- let priceBeforeTax = product.price.price / 1.11
+ let priceBeforeTax = product.price.price / PPN
calculateTotalPriceBeforeTax += priceBeforeTax * product.quantity
calculateTotalTaxAmount += (product.price.price - priceBeforeTax) * product.quantity
calculateTotalDiscountAmount +=
diff --git a/src/lib/cart/components/CartOld.jsx b/src/lib/cart/components/CartOld.jsx
index 718541af..358efa49 100644
--- a/src/lib/cart/components/CartOld.jsx
+++ b/src/lib/cart/components/CartOld.jsx
@@ -20,6 +20,7 @@ import whatsappUrl from '@/core/utils/whatsappUrl'
import useAuth from '@/core/hooks/useAuth'
const Cart = () => {
+ const PPN = process.env.NEXT_PUBLIC_PPN
const router = useRouter()
const [products, setProducts] = useState(null)
const auth = useAuth()
@@ -67,7 +68,7 @@ const Cart = () => {
})
if (!product.selected) continue
- let priceBeforeTax = product.price.price / 1.11
+ let priceBeforeTax = product.price.price / PPN
calculateTotalPriceBeforeTax += priceBeforeTax * product.quantity
calculateTotalTaxAmount += (product.price.price - priceBeforeTax) * product.quantity
calculateTotalDiscountAmount +=
diff --git a/src/lib/cart/components/Cartheader.jsx b/src/lib/cart/components/Cartheader.jsx
index 1c30bb13..6d4e2679 100644
--- a/src/lib/cart/components/Cartheader.jsx
+++ b/src/lib/cart/components/Cartheader.jsx
@@ -14,6 +14,7 @@ const { ShoppingCartIcon, PhotoIcon } = require('@heroicons/react/24/outline');
const { default: Link } = require('next/link');
const Cardheader = (cartCount) => {
+ const PPN = process.env.NEXT_PUBLIC_PPN
const router = useRouter();
const [subTotal, setSubTotal] = useState(null);
const [buttonLoading, SetButtonTerapkan] = useState(false);
@@ -66,7 +67,7 @@ const Cardheader = (cartCount) => {
for (const product of products) {
if (product.quantity == '') continue;
- let priceBeforeTax = product.price.price / 1.11;
+ let priceBeforeTax = product.price.price / PPN;
calculateTotalPriceBeforeTax += priceBeforeTax * product.quantity;
calculateTotalTaxAmount +=
(product.price.price - priceBeforeTax) * product.quantity;
diff --git a/src/lib/checkout/components/Checkout.jsx b/src/lib/checkout/components/Checkout.jsx
index 6479881d..5f630799 100644
--- a/src/lib/checkout/components/Checkout.jsx
+++ b/src/lib/checkout/components/Checkout.jsx
@@ -55,6 +55,7 @@ function convertToInternational(number) {
}
const Checkout = () => {
+ 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;
@@ -554,7 +555,6 @@ const Checkout = () => {
setItemTnC(item);
SetBottomPopupTnC(true);
};
- // const taxTotal = (totalAmount - totalDiscountAmount - discountVoucher) * 0.11
const hasNoPrice = useMemo(() => {
if (!products) return false;
@@ -1164,7 +1164,7 @@ const Checkout = () => {
<div>{currencyFormat(cartCheckout?.subtotal)}</div>
</div>
<div className='flex gap-x-2 justify-between'>
- <div className='text-gray_r-11'>PPN 11%</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'>
@@ -1472,7 +1472,7 @@ const Checkout = () => {
<div>{currencyFormat(cartCheckout?.subtotal)}</div>
</div>
<div className='flex gap-x-2 justify-between'>
- <div className='text-gray_r-11'>PPN 11%</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'>
diff --git a/src/lib/checkout/components/CheckoutOld.jsx b/src/lib/checkout/components/CheckoutOld.jsx
index 5b479a73..433c5672 100644
--- a/src/lib/checkout/components/CheckoutOld.jsx
+++ b/src/lib/checkout/components/CheckoutOld.jsx
@@ -239,7 +239,7 @@ const Checkout = () => {
setIsLoading(false)
window.location.href = payment.data.redirectUrl
}
- const taxTotal = (totalAmount - totalDiscountAmount) * 0.11
+ const taxTotal = (totalAmount - totalDiscountAmount) * (PPN - 1)
return (
<>
diff --git a/src/lib/checkout/email/FinishCheckoutEmail.jsx b/src/lib/checkout/email/FinishCheckoutEmail.jsx
index d19ba1ca..9a94587e 100644
--- a/src/lib/checkout/email/FinishCheckoutEmail.jsx
+++ b/src/lib/checkout/email/FinishCheckoutEmail.jsx
@@ -17,6 +17,7 @@ import {
import FinishCheckout from '../components/FinishCheckout'
const FinishCheckoutEmail = ({ transaction, payment, statusPayment }) => {
+ const PPN = process.env.NEXT_PUBLIC_PPN
return (
<Html>
@@ -206,9 +207,9 @@ const FinishCheckoutEmail = ({ transaction, payment, statusPayment }) => {
</Column>
</Row>
<Row style={style.descriptionRow}>
- <Column style={style.descriptionLCol}>PPN 11% (Incl.)</Column>
+ <Column style={style.descriptionLCol}>PPN {((PPN - 1) * 100).toFixed(0)}% (Incl.)</Column>
<Column style={style.descriptionRCol}>
- {currencyFormat(transaction.subtotal * 0.11)}
+ {currencyFormat(transaction.subtotal * (PPN - 1))}
</Column>
</Row>
diff --git a/src/lib/flashSale/components/FlashSale.jsx b/src/lib/flashSale/components/FlashSale.jsx
index f4be279e..9c0e713b 100644
--- a/src/lib/flashSale/components/FlashSale.jsx
+++ b/src/lib/flashSale/components/FlashSale.jsx
@@ -9,13 +9,23 @@ import { FlashSaleSkeleton } from '../skeleton/FlashSaleSkeleton';
const FlashSale = () => {
const [flashSales, setFlashSales] = useState(null);
const [isLoading, setIsLoading] = useState(true);
+ const [duration, setDuration] = useState();
+ const calculateRemainingTime = (endDate) => {
+ const currentTime = new Date();
+ const endTime = new Date(endDate);
+ const remainingTimeInSeconds = (endTime - currentTime) / 1000;
+
+ return Math.max(Math.round(remainingTimeInSeconds), 0);
+ };
useEffect(() => {
const loadFlashSales = async () => {
const res = await fetch('/api/flashsale-header');
const { data } = await res.json();
if (data) {
setFlashSales(data);
+ const remainingTime = calculateRemainingTime(data[0]?.endDate);
+ setDuration(remainingTime);
}
setIsLoading(false);
};
@@ -37,7 +47,7 @@ const FlashSale = () => {
<div className='font-medium sm:text-h-lg mt-1.5'>
{flashSale.name}
</div>
- <CountDown initialTime={flashSale.duration} />
+ <CountDown initialTime={duration} />
</div>
<div className='relative'>
@@ -59,7 +69,7 @@ const FlashSale = () => {
/>
<FlashSaleProduct
flashSaleId={flashSale.pricelistId}
- duration={flashSale.duration}
+ duration={duration}
/>
</div>
</div>
diff --git a/src/lib/form/components/KunjunganSales.jsx b/src/lib/form/components/KunjunganSales.jsx
index ffa8f135..0f63de03 100644
--- a/src/lib/form/components/KunjunganSales.jsx
+++ b/src/lib/form/components/KunjunganSales.jsx
@@ -1,17 +1,18 @@
-import odooApi from '@/core/api/odooApi'
-import HookFormSelect from '@/core/components/elements/Select/HookFormSelect'
-import cityApi from '@/lib/address/api/cityApi'
-import { yupResolver } from '@hookform/resolvers/yup'
-import React, { useEffect, useRef, useState } 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 createLeadApi from '../api/createLeadApi'
-import PageContent from '@/lib/content/components/PageContent'
-
-import useAuth from '@/core/hooks/useAuth'
-import { useRouter } from 'next/router'
+import odooApi from '@/core/api/odooApi';
+import HookFormSelect from '@/core/components/elements/Select/HookFormSelect';
+import cityApi from '@/lib/address/api/cityApi';
+import stateApi from '@/lib/address/api/stateApi.js';
+import { yupResolver } from '@hookform/resolvers/yup';
+import React, { useEffect, useRef, useState } 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 createLeadApi from '../api/createLeadApi';
+import PageContent from '@/lib/content/components/PageContent';
+
+import useAuth from '@/core/hooks/useAuth';
+import { useRouter } from 'next/router';
const KunjunganSales = () => {
const {
@@ -19,44 +20,72 @@ const KunjunganSales = () => {
handleSubmit,
formState: { errors },
control,
- reset
+ reset,
+ watch,
+ setValue,
} = useForm({
resolver: yupResolver(validationSchema),
- defaultValues
- })
- const [cities, setCities] = useState([])
- const [companyTypes, setCompanyTypes] = useState([])
- const router = useRouter()
+ defaultValues,
+ });
+ const [cities, setCities] = useState([]);
+ const [state, setState] = useState([]);
+ const [companyTypes, setCompanyTypes] = useState([]);
+ const router = useRouter();
+
+ const auth = useAuth();
- const auth = useAuth()
+ const recaptchaRef = useRef(null);
-
+ if (auth == false) {
+ router.push(`/login?next=${encodeURIComponent('/kunjungan-sales')}`);
+ }
+
+ useEffect(() => {
+ const loadState = async () => {
+ let dataState = await stateApi({ tempo: false });
+ dataState = dataState.map((state) => ({
+ value: state.id,
+ label: state.name,
+ }));
+ setState(dataState);
+ };
+ loadState();
+ }, []);
- const recaptchaRef = useRef(null)
+ const watchState = watch('state');
useEffect(() => {
- if(auth == false) {
- router.push('/login')
+ if (auth == false) {
+ return;
}
const loadCities = async () => {
- let dataCities = await cityApi()
- dataCities = dataCities.map((obj) => ({ value: obj.name, label: obj.name }))
- setCities(dataCities)
- }
+ setValue('city', '');
+ let dataCities = await cityApi({ stateId: watchState });
+ dataCities = dataCities?.map((obj) => ({
+ value: obj.name,
+ label: obj.name,
+ }));
+ setCities(dataCities);
+ };
const loadCompanyTypes = async () => {
- const dataCompanyTypes = await odooApi('GET', '/api/v1/partner/company_type')
- setCompanyTypes(dataCompanyTypes?.map((obj) => ({ value: obj.name, label: obj.name })))
- }
+ const dataCompanyTypes = await odooApi(
+ 'GET',
+ '/api/v1/partner/company_type'
+ );
+ setCompanyTypes(
+ dataCompanyTypes?.map((obj) => ({ value: obj.name, label: obj.name }))
+ );
+ };
- loadCompanyTypes()
- loadCities()
- }, [auth])
+ loadCompanyTypes();
+ loadCities();
+ }, [auth, watchState, setValue]);
const onSubmitHandler = async (values) => {
- const recaptchaValue = recaptchaRef.current.getValue()
+ const recaptchaValue = recaptchaRef.current.getValue();
if (!recaptchaValue) {
- toast.error('Recaptcha harus diisi')
- return
+ toast.error('Recaptcha harus diisi');
+ return;
}
const data = {
@@ -71,29 +100,37 @@ const KunjunganSales = () => {
`Unit Perusahaan: ${values.companyType}`,
`No. Handphone: ${values.mobile}`,
`Alamat Email: ${values.email}`,
- `Keterangan: ${values.description}`
- ].join('\n')
- }
+ `Keterangan: ${values.description}`,
+ ].join('\n'),
+ };
- const createLead = await createLeadApi({ data })
+ const createLead = await createLeadApi({ data });
if (createLead) {
- toast.success('Berhasil mengirimkan formulir kunjungan sales')
- reset()
- recaptchaRef.current.reset()
+ toast.success('Berhasil mengirimkan formulir kunjungan sales');
+ reset();
+ recaptchaRef.current.reset();
}
+ };
+ if (!auth) {
+ return;
}
return (
<div className='container mx-auto p-4 md:p-0 my-0 md:my-10'>
- <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'>Kunjungan Sales</h1>
+ <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'>
+ Kunjungan Sales
+ </h1>
<div className='w-full grid grid-cols-1 md:grid-cols-2 gap-x-2'>
- <form onSubmit={handleSubmit(onSubmitHandler)} className='grid grid-cols-1 gap-y-6'>
+ <form
+ onSubmit={handleSubmit(onSubmitHandler)}
+ className='grid grid-cols-1 gap-y-6'
+ >
<div
className='flex items-center bg-blue-100 border border-blue-300 text-blue-500 font-medium px-4 py-3 rounded leading-6'
role='alert'
>
- Hubungi kami untuk mendapatkan kunjungan sales kami dan dapatkan berbagai kelebihannya
- dengan menjadi pelanggan korporat kami.
+ Hubungi kami untuk mendapatkan kunjungan sales kami dan dapatkan
+ berbagai kelebihannya dengan menjadi pelanggan korporat kami.
</div>
<div>
<label className='form-label mb-2'>Nama Perusahan*</label>
@@ -104,7 +141,9 @@ const KunjunganSales = () => {
className='form-input'
aria-invalid={errors.company?.message}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.company?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.company?.message}
+ </div>
</div>
<div>
@@ -116,7 +155,9 @@ const KunjunganSales = () => {
className='form-input'
aria-invalid={errors.phone?.message}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.phone?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.phone?.message}
+ </div>
</div>
<div>
@@ -128,7 +169,21 @@ const KunjunganSales = () => {
className='form-input'
aria-invalid={errors.address?.message}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.address?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.address?.message}
+ </div>
+ </div>
+
+ <div>
+ <label className='form-label mb-2'>Provinsi*</label>
+ <Controller
+ name='state'
+ control={control}
+ render={(props) => <HookFormSelect {...props} options={state} />}
+ />
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.stateId?.message}
+ </div>
</div>
<div>
@@ -138,7 +193,9 @@ const KunjunganSales = () => {
control={control}
render={(props) => <HookFormSelect {...props} options={cities} />}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.city?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.city?.message}
+ </div>
</div>
<div>
@@ -146,9 +203,13 @@ const KunjunganSales = () => {
<Controller
name='companyType'
control={control}
- render={(props) => <HookFormSelect {...props} options={companyTypes} />}
+ render={(props) => (
+ <HookFormSelect {...props} options={companyTypes} />
+ )}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.companyType?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.companyType?.message}
+ </div>
</div>
<div>
@@ -160,7 +221,9 @@ const KunjunganSales = () => {
className='form-input'
aria-invalid={errors.mobile?.message}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.mobile?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.mobile?.message}
+ </div>
</div>
<div>
@@ -172,34 +235,49 @@ const KunjunganSales = () => {
className='form-input'
aria-invalid={errors.email?.message}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.email?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.email?.message}
+ </div>
</div>
<div>
<label className='form-label mb-2'>Keterangan</label>
- <textarea {...register('description')} type='text' className='form-input' />
+ <textarea
+ {...register('description')}
+ type='text'
+ className='form-input'
+ />
</div>
- <ReCAPTCHA ref={recaptchaRef} sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE} />
+ <ReCAPTCHA
+ ref={recaptchaRef}
+ sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE}
+ />
- <button type='submit' className='btn-yellow w-full md:w-fit ml-0 md:ml-auto'>
+ <button
+ type='submit'
+ className='btn-yellow w-full md:w-fit ml-0 md:ml-auto'
+ >
Simpan
</button>
</form>
<PageContent path='/kunjungan-sales' />
</div>
</div>
- )
-}
+ );
+};
const validationSchema = Yup.object().shape({
- email: Yup.string().email('Format harus seperti contoh@email.com').required('Harus di-isi'),
+ email: Yup.string()
+ .email('Format harus seperti contoh@email.com')
+ .required('Harus di-isi'),
company: Yup.string().required('Harus di-isi'),
phone: Yup.string().required('Harus di-isi'),
mobile: Yup.string().required('Harus di-isi'),
address: Yup.string().required('Harus di-isi'),
- city: Yup.string().required('Harus dipilih')
-})
+ city: Yup.string().required('Harus dipilih'),
+ state: Yup.string().required('Harus dipilih'),
+});
const defaultValues = {
email: '',
@@ -208,8 +286,9 @@ const defaultValues = {
mobile: '',
address: '',
city: '',
+ state: '',
companyType: '',
- description: ''
-}
+ description: '',
+};
-export default KunjunganSales
+export default KunjunganSales;
diff --git a/src/lib/form/components/KunjunganService.jsx b/src/lib/form/components/KunjunganService.jsx
index 5720d14e..a3871054 100644
--- a/src/lib/form/components/KunjunganService.jsx
+++ b/src/lib/form/components/KunjunganService.jsx
@@ -1,16 +1,17 @@
-import HookFormSelect from '@/core/components/elements/Select/HookFormSelect'
-import cityApi from '@/lib/address/api/cityApi'
-import { yupResolver } from '@hookform/resolvers/yup'
-import React, { useEffect, useRef, useState } 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 createLeadApi from '../api/createLeadApi'
-import PageContent from '@/lib/content/components/PageContent'
-import { useRouter } from 'next/router'
+import HookFormSelect from '@/core/components/elements/Select/HookFormSelect';
+import cityApi from '@/lib/address/api/cityApi';
+import stateApi from '@/lib/address/api/stateApi.js';
+import { yupResolver } from '@hookform/resolvers/yup';
+import React, { useEffect, useRef, useState } 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 createLeadApi from '../api/createLeadApi';
+import PageContent from '@/lib/content/components/PageContent';
+import { useRouter } from 'next/router';
-import useAuth from '@/core/hooks/useAuth'
+import useAuth from '@/core/hooks/useAuth';
const CreateKunjunganService = () => {
const {
@@ -18,37 +19,61 @@ const CreateKunjunganService = () => {
handleSubmit,
formState: { errors },
control,
- reset
+ reset,
+ watch,
+ setValue,
} = useForm({
resolver: yupResolver(validationSchema),
- defaultValues
- })
- const [cities, setCities] = useState([])
- const [company_unit, setCompany_unit] = useState([])
-
- const router = useRouter()
+ defaultValues,
+ });
+ const [cities, setCities] = useState([]);
+ const [state, setState] = useState([]);
+ const [company_unit, setCompany_unit] = useState([]);
- const auth = useAuth()
+ const router = useRouter();
- const recaptchaRef = useRef(null)
+ const auth = useAuth();
+ if (auth == false) {
+ router.push(`/login?next=${encodeURIComponent('/kunjungan-service')}`);
+ }
+
+ const recaptchaRef = useRef(null);
+
+ 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) {
- router.push('/login')
+ if (auth == false) {
+ return;
}
const loadCities = async () => {
- let dataCities = await cityApi()
- dataCities = dataCities.map((city) => ({ value: city.id, label: city.name }))
- setCities(dataCities)
- }
- loadCities()
- }, [auth])
+ setValue('city', '');
+ let dataCities = await cityApi({ stateId: watchState });
+ dataCities = dataCities.map((city) => ({
+ value: city.id,
+ label: city.name,
+ }));
+ setCities(dataCities);
+ };
+ loadCities();
+ }, [auth, watchState, setValue]);
const onSubmitHandler = async (values) => {
- const recaptchaValue = recaptchaRef.current.getValue()
+ const recaptchaValue = recaptchaRef.current.getValue();
if (!recaptchaValue) {
- toast.error('Catcha harus diisi')
- return
+ toast.error('Catcha harus diisi');
+ return;
}
const data = {
...values,
@@ -70,164 +95,212 @@ const CreateKunjunganService = () => {
' \r\n Email : ' +
values.email +
' \r\n Keterangan : ' +
- values.description
- }
+ values.description,
+ };
- const create_leads = await createLeadApi({ data })
+ const create_leads = await createLeadApi({ data });
if (create_leads) {
- toast.success('Berhasil menambahkan alamat')
- reset()
- recaptchaRef.current.reset()
+ toast.success('Berhasil menambahkan alamat');
+ reset();
+ recaptchaRef.current.reset();
}
+ };
+ if (!auth) {
+ return;
}
return (
<div className='container mx-auto p-4 md:p-0 my-0 md:my-10'>
- <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'>Kunjungan Service</h1>
+ <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'>
+ Kunjungan Service
+ </h1>
<div className='w-full p-4 bg-white border border-gray_r-6 rounded'>
<div
className='flex items-center bg-blue-100 border border-blue-400 text-blue-500 font-bold px-4 py-3 mb-4'
role='alert'
>
<p>
- Tidak punya waktu untuk melakukan service atau perawatan rutin? Silahkan hubungi teknisi
- kami untuk melakukan kunjungan ke tempat Anda di Jabodetabek.
+ Tidak punya waktu untuk melakukan service atau perawatan rutin?
+ Silahkan hubungi teknisi kami untuk melakukan kunjungan ke tempat
+ Anda di Jabodetabek.
</p>
</div>
<div className='w-full grid grid-cols-2 gap-x-2'>
-
- <form onSubmit={handleSubmit(onSubmitHandler)}>
- <div className=''>
- <div>
- <label className='form-label mb-2'>Nama Perusahan *</label>
- <input
- {...register('company')}
- placeholder='PT.Indoteknik'
- type='text'
- className='form-input'
- />
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.company?.message}</div>
+ <form onSubmit={handleSubmit(onSubmitHandler)}>
+ <div className=''>
+ <div>
+ <label className='form-label mb-2'>Nama Perusahan *</label>
+ <input
+ {...register('company')}
+ placeholder='PT.Indoteknik'
+ type='text'
+ className='form-input'
+ />
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.company?.message}
+ </div>
+ </div>
</div>
- </div>
- <div className=''>
- <div>
- <label className='form-label mb-2'>No. Telp *</label>
- <input
- {...register('phone')}
- placeholder='021-XXXX'
- type='text'
- className='form-input'
- />
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.phone?.message}</div>
+ <div className=''>
+ <div>
+ <label className='form-label mb-2'>No. Telp *</label>
+ <input
+ {...register('phone')}
+ placeholder='021-XXXX'
+ type='text'
+ className='form-input'
+ />
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.phone?.message}
+ </div>
+ </div>
</div>
- </div>
- <div className=''>
- <div>
- <label className='form-label mb-2'>Alamat*</label>
- <input
- {...register('address')}
- placeholder='jl. Bandengan no.31 '
- type='text'
- className='form-input'
- />
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.address?.message}</div>
+ <div className=''>
+ <div>
+ <label className='form-label mb-2'>Alamat*</label>
+ <input
+ {...register('address')}
+ placeholder='jl. Bandengan no.31 '
+ type='text'
+ className='form-input'
+ />
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.address?.message}
+ </div>
+ </div>
</div>
- </div>
- <div className=''>
<div>
- <label className='form-label mb-2'>Kota*</label>
+ <label className='form-label mb-2'>Provinsi*</label>
<Controller
- name='city'
+ name='state'
control={control}
- render={(props) => <HookFormSelect {...props} options={cities} />}
+ render={(props) => (
+ <HookFormSelect {...props} options={state} />
+ )}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.city?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.state?.message}
+ </div>
</div>
- </div>
- <div className=''>
- <div>
- <label className='form-label mb-2'>Contact Person*</label>
- <input
- {...register('cp')}
- placeholder='Jhone doe'
- type='text'
- className='form-input'
- />
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.cp?.message}</div>
+ <div className=''>
+ <div>
+ <label className='form-label mb-2'>Kota*</label>
+ <Controller
+ name='city'
+ control={control}
+ render={(props) => (
+ <HookFormSelect {...props} options={cities} />
+ )}
+ />
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.city?.message}
+ </div>
+ </div>
</div>
- </div>
- <div className=''>
- <div>
- <label className='form-label mb-2'>No HP *</label>
- <input
- {...register('mobile')}
- placeholder='628XXXXXXX'
- type='text'
- className='form-input'
- />
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.mobile?.message}</div>
+ <div className=''>
+ <div>
+ <label className='form-label mb-2'>Contact Person*</label>
+ <input
+ {...register('cp')}
+ placeholder='Jhone doe'
+ type='text'
+ className='form-input'
+ />
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.cp?.message}
+ </div>
+ </div>
</div>
- </div>
- <div className=''>
- <div>
- <label className='form-label mb-2'>Alamat Email *</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>
+ <div className=''>
+ <div>
+ <label className='form-label mb-2'>No HP *</label>
+ <input
+ {...register('mobile')}
+ placeholder='628XXXXXXX'
+ type='text'
+ className='form-input'
+ />
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.mobile?.message}
+ </div>
+ </div>
</div>
- </div>
- <div className=''>
- <div>
- <label className='form-label mb-2'>
- Sebutkan: Merek, Tipe, Permasalahan, Service, Perawatan
- </label>
- <textarea {...register('description')} type='text' className='form-input' />
- <div className='text-caption-2 text-danger-500 mt-1'>
- {errors.description?.message}
+ <div className=''>
+ <div>
+ <label className='form-label mb-2'>Alamat Email *</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>
</div>
</div>
- </div>
- <div className=''>
- <div>
- <ReCAPTCHA ref={recaptchaRef} sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE} />
+ <div className=''>
+ <div>
+ <label className='form-label mb-2'>
+ Sebutkan: Merek, Tipe, Permasalahan, Service, Perawatan
+ </label>
+ <textarea
+ {...register('description')}
+ type='text'
+ className='form-input'
+ />
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.description?.message}
+ </div>
+ </div>
</div>
- </div>
- <div className=''>
- <div>
- <button type='submit' className='btn-yellow w-full md:w-fit mt-6 ml-0 md:ml-auto'>
- Simpan
- </button>
+ <div className=''>
+ <div>
+ <ReCAPTCHA
+ ref={recaptchaRef}
+ sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE}
+ />
+ </div>
+ </div>
+ <div className=''>
+ <div>
+ <button
+ type='submit'
+ className='btn-yellow w-full md:w-fit mt-6 ml-0 md:ml-auto'
+ >
+ Simpan
+ </button>
+ </div>
</div>
- </div>
- </form>
- <PageContent path='/kunjungan-service' />
+ </form>
+ <PageContent path='/kunjungan-service' />
</div>
</div>
</div>
- )
-}
+ );
+};
const validationSchema = Yup.object().shape({
company: Yup.string().required('Harus di-isi'),
- email: Yup.string().email('Format harus seperti contoh@email.com').required('Harus di-isi'),
+ email: Yup.string()
+ .email('Format harus seperti contoh@email.com')
+ .required('Harus di-isi'),
phone: Yup.string().required('Harus di-isi'),
city: Yup.string().required('Harus di-isi'),
+ state: Yup.string().required('Harus dipilih'),
cp: Yup.string().required('Harus di-isi'),
mobile: Yup.string().required('Harus di-isi'),
email: Yup.string().required('Harus di-isi'),
- address: Yup.string().required('Harus di-isi')
-})
+ address: Yup.string().required('Harus di-isi'),
+});
const defaultValues = {
company: '',
email: '',
phone: '',
city: '',
+ state: '',
cp: '',
mobile: '',
email: '',
- address: ''
-}
+ address: '',
+};
-export default CreateKunjunganService
+export default CreateKunjunganService;
diff --git a/src/lib/form/components/Merchant.jsx b/src/lib/form/components/Merchant.jsx
index 85f72bf8..03b8fc84 100644
--- a/src/lib/form/components/Merchant.jsx
+++ b/src/lib/form/components/Merchant.jsx
@@ -1,5 +1,6 @@
import HookFormSelect from '@/core/components/elements/Select/HookFormSelect';
import cityApi from '@/lib/address/api/cityApi';
+import stateApi from '@/lib/address/api/stateApi.js';
import { yupResolver } from '@hookform/resolvers/yup';
import React, { useEffect, useRef, useState } from 'react';
import ReCAPTCHA from 'react-google-recaptcha';
@@ -9,8 +10,7 @@ import * as Yup from 'yup';
import createLeadApi from '../api/createLeadApi';
import PageContent from '@/lib/content/components/PageContent';
import { useRouter } from 'next/router';
-import useAuth from '@/core/hooks/useAuth'
-
+import useAuth from '@/core/hooks/useAuth';
const CreateMerchant = () => {
const {
@@ -19,6 +19,8 @@ const CreateMerchant = () => {
formState: { errors },
control,
reset,
+ watch,
+ setValue,
} = useForm({
resolver: yupResolver(validationSchema),
defaultValues,
@@ -50,27 +52,45 @@ const CreateMerchant = () => {
},
];
const [cities, setCities] = useState([]);
+ const [state, setState] = useState([]);
const [company_unit, setCompany_unit] = useState(list_unit);
const recaptchaRef = useRef(null);
- const router = useRouter()
+ const router = useRouter();
+
+ const auth = useAuth();
+ if (auth == false) {
+ router.push(`/login?next=${encodeURIComponent('/daftar-merchant')}`);
+ }
- const auth = useAuth()
+ 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) {
- router.push('/login')
+ if (auth == false) {
+ return;
}
const loadCities = async () => {
- let dataCities = await cityApi();
- dataCities = dataCities.map((city) => ({
+ setValue('city', '');
+ let dataCities = await cityApi({ stateId: watchState });
+ dataCities = dataCities?.map((city) => ({
value: city.id,
label: city.name,
}));
setCities(dataCities);
};
loadCities();
- }, [auth]);
+ }, [auth, watchState, setValue]);
const onSubmitHandler = async (values) => {
const recaptchaValue = recaptchaRef.current.getValue();
@@ -111,6 +131,9 @@ const CreateMerchant = () => {
recaptchaRef.current.reset();
}
};
+ if (!auth) {
+ return;
+ }
return (
<div className='container mx-auto p-4 md:p-0 my-0 md:my-10'>
<h1 className='text-h-sm md:text-title-sm font-semibold mb-6'>
@@ -172,6 +195,19 @@ const CreateMerchant = () => {
</div>
</div>
</div>
+ <div>
+ <label className='form-label mb-2'>Provinsi*</label>
+ <Controller
+ name='state'
+ control={control}
+ render={(props) => (
+ <HookFormSelect {...props} options={state} />
+ )}
+ />
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.state?.message}
+ </div>
+ </div>
<div className=''>
<div>
<label className='form-label mb-2'>Kota*</label>
@@ -290,6 +326,7 @@ const validationSchema = Yup.object().shape({
.required('Harus di-isi'),
phone: Yup.string().required('Harus di-isi'),
cp: Yup.string().required('Harus di-isi'),
+ state: Yup.string().required('Harus dipilih'),
city: Yup.string().required('Harus di-isi'),
company_unit: Yup.string().required('Harus di-isi'),
address: Yup.string().required('Harus di-isi'),
@@ -300,6 +337,7 @@ const defaultValues = {
company: '',
email: '',
phone: '',
+ state: '',
city: '',
company_unit: '',
cp: '',
diff --git a/src/lib/form/components/RequestForQuotation.jsx b/src/lib/form/components/RequestForQuotation.jsx
index 68b7fa17..8861338f 100644
--- a/src/lib/form/components/RequestForQuotation.jsx
+++ b/src/lib/form/components/RequestForQuotation.jsx
@@ -1,18 +1,19 @@
-import odooApi from '@/core/api/odooApi'
-import HookFormSelect from '@/core/components/elements/Select/HookFormSelect'
-import cityApi from '@/lib/address/api/cityApi'
-import { yupResolver } from '@hookform/resolvers/yup'
-import React, { useEffect, useRef, useState } 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 createLeadApi from '../api/createLeadApi'
-import getFileBase64 from '@/core/utils/getFileBase64'
-import PageContent from '@/lib/content/components/PageContent'
-import { useRouter } from 'next/router'
-
-import useAuth from '@/core/hooks/useAuth'
+import odooApi from '@/core/api/odooApi';
+import HookFormSelect from '@/core/components/elements/Select/HookFormSelect';
+import cityApi from '@/lib/address/api/cityApi';
+import stateApi from '@/lib/address/api/stateApi.js';
+import { yupResolver } from '@hookform/resolvers/yup';
+import React, { useEffect, useRef, useState } 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 createLeadApi from '../api/createLeadApi';
+import getFileBase64 from '@/core/utils/getFileBase64';
+import PageContent from '@/lib/content/components/PageContent';
+import { useRouter } from 'next/router';
+
+import useAuth from '@/core/hooks/useAuth';
const RequestForQuotation = () => {
const {
@@ -20,47 +21,69 @@ const RequestForQuotation = () => {
handleSubmit,
formState: { errors },
control,
- reset
+ reset,
+ watch,
+ setValue,
} = useForm({
resolver: yupResolver(validationSchema),
- defaultValues
- })
- const [cities, setCities] = useState([])
+ defaultValues,
+ });
+ const [cities, setCities] = useState([]);
+ const [state, setState] = useState([]);
- const quotationFileRef = useRef(null)
- const recaptchaRef = useRef(null)
- const router = useRouter()
+ const quotationFileRef = useRef(null);
+ const recaptchaRef = useRef(null);
+ const router = useRouter();
- const auth = useAuth()
+ const auth = useAuth();
+ if (auth == false) {
+ router.push(`/login?next=${encodeURIComponent('/request-for-quotation')}`);
+ }
+ 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) {
- router.push('/login')
+ if (!auth) {
+ return;
}
const loadCities = async () => {
- let dataCities = await cityApi()
- dataCities = dataCities.map((obj) => ({ value: obj.name, label: obj.name }))
- setCities(dataCities)
- }
- loadCities()
- }, [auth])
+ setValue('city', '');
+ let dataCities = await cityApi({ stateId: watchState });
+ dataCities = dataCities?.map((obj) => ({
+ value: obj.name,
+ label: obj.name,
+ }));
+ setCities(dataCities);
+ };
+ loadCities();
+ }, [auth, watchState, setValue]);
const onSubmitHandler = async (values) => {
- const recaptchaValue = recaptchaRef.current.getValue()
+ const recaptchaValue = recaptchaRef.current.getValue();
if (!recaptchaValue) {
- toast.error('Recaptcha harus diisi')
- return
+ toast.error('Recaptcha harus diisi');
+ return;
}
- const file = quotationFileRef.current.files[0]
- let fileBase64 = null
+ const file = quotationFileRef.current.files[0];
+ let fileBase64 = null;
if (typeof file !== 'undefined') {
if (file.size > 5000000) {
- toast.error('Maksimal ukuran file adalah 5MB')
- return
+ toast.error('Maksimal ukuran file adalah 5MB');
+ return;
}
- fileBase64 = await getFileBase64(file)
+ fileBase64 = await getFileBase64(file);
}
const data = {
@@ -73,33 +96,42 @@ const RequestForQuotation = () => {
`Kota: ${values.city}`,
`No. Handphone: ${values.mobile}`,
`Alamat Email: ${values.email}`,
- `Keterangan: ${values.description}`
- ].join('\n')
- }
+ `Keterangan: ${values.description}`,
+ ].join('\n'),
+ };
- if (fileBase64) data.file_quotation = fileBase64
+ if (fileBase64) data.file_quotation = fileBase64;
- const createLead = await createLeadApi({ data })
+ const createLead = await createLeadApi({ data });
if (createLead) {
- toast.success('Berhasil mengirimkan formulir request for quotation')
- reset()
- recaptchaRef.current.reset()
+ toast.success('Berhasil mengirimkan formulir request for quotation');
+ reset();
+ recaptchaRef.current.reset();
}
+ };
+ if (!auth) {
+ return;
}
return (
<div className='container mx-auto p-4 md:p-0 my-0 md:my-10'>
- <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'>Request for Quotation</h1>
+ <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'>
+ Request for Quotation
+ </h1>
<div className='w-full grid grid-cols-1 md:grid-cols-2 gap-x-2'>
- <form onSubmit={handleSubmit(onSubmitHandler)} className='grid grid-cols-1 gap-y-6'>
+ <form
+ onSubmit={handleSubmit(onSubmitHandler)}
+ className='grid grid-cols-1 gap-y-6'
+ >
<div
className='flex items-center bg-blue-100 border border-blue-300 text-blue-500 font-medium px-4 py-3 rounded leading-6'
role='alert'
>
- Halaman untuk pengajuan penawaran harga, lengkapi data di bawah ini dengan jelas untuk
- mempermudah tim support kami melayani kebutuhan Anda. Tim kami akan sesegera mungkin
- untuk membuatkan penawaran harga terbaik, hubungi kami melalui telpon jika ada
- keterlambatan pelayanan.
+ Halaman untuk pengajuan penawaran harga, lengkapi data di bawah ini
+ dengan jelas untuk mempermudah tim support kami melayani kebutuhan
+ Anda. Tim kami akan sesegera mungkin untuk membuatkan penawaran
+ harga terbaik, hubungi kami melalui telpon jika ada keterlambatan
+ pelayanan.
</div>
<div>
<label className='form-label mb-2'>Nama Perusahan*</label>
@@ -110,7 +142,9 @@ const RequestForQuotation = () => {
className='form-input'
aria-invalid={errors.company?.message}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.company?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.company?.message}
+ </div>
</div>
<div>
@@ -122,7 +156,21 @@ const RequestForQuotation = () => {
className='form-input'
aria-invalid={errors.phone?.message}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.phone?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.phone?.message}
+ </div>
+ </div>
+
+ <div>
+ <label className='form-label mb-2'>Provinsi*</label>
+ <Controller
+ name='state'
+ control={control}
+ render={(props) => <HookFormSelect {...props} options={state} />}
+ />
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.state?.message}
+ </div>
</div>
<div>
@@ -132,7 +180,9 @@ const RequestForQuotation = () => {
control={control}
render={(props) => <HookFormSelect {...props} options={cities} />}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.city?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.city?.message}
+ </div>
</div>
<div>
@@ -144,7 +194,9 @@ const RequestForQuotation = () => {
className='form-input'
aria-invalid={errors.contactPerson?.message}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.contactPerson?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.contactPerson?.message}
+ </div>
</div>
<div>
@@ -156,7 +208,9 @@ const RequestForQuotation = () => {
className='form-input'
aria-invalid={errors.mobile?.message}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.mobile?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.mobile?.message}
+ </div>
</div>
<div>
@@ -168,39 +222,59 @@ const RequestForQuotation = () => {
className='form-input'
aria-invalid={errors.email?.message}
/>
- <div className='text-caption-2 text-danger-500 mt-1'>{errors.email?.message}</div>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.email?.message}
+ </div>
</div>
<div>
<label className='form-label mb-2'>Keterangan</label>
- <textarea {...register('description')} type='text' className='form-input' />
+ <textarea
+ {...register('description')}
+ type='text'
+ className='form-input'
+ />
</div>
<div>
<label className='form-label mb-2'>File Daftar Produk</label>
- <input type="file" name="quotationFile" className='form-input' ref={quotationFileRef} />
+ <input
+ type='file'
+ name='quotationFile'
+ className='form-input'
+ ref={quotationFileRef}
+ />
</div>
- <ReCAPTCHA ref={recaptchaRef} sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE} />
+ <ReCAPTCHA
+ ref={recaptchaRef}
+ sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE}
+ />
- <button type='submit' className='btn-yellow w-full md:w-fit ml-0 md:ml-auto'>
+ <button
+ type='submit'
+ className='btn-yellow w-full md:w-fit ml-0 md:ml-auto'
+ >
Simpan
</button>
</form>
<PageContent path='/request-for-quotation' />
</div>
</div>
- )
-}
+ );
+};
const validationSchema = Yup.object().shape({
- email: Yup.string().email('Format harus seperti contoh@email.com').required('Harus di-isi'),
+ email: Yup.string()
+ .email('Format harus seperti contoh@email.com')
+ .required('Harus di-isi'),
company: Yup.string().required('Harus di-isi'),
phone: Yup.string().required('Harus di-isi'),
mobile: Yup.string().required('Harus di-isi'),
city: Yup.string().required('Harus dipilih'),
+ state: Yup.string().required('Harus dipilih'),
contactPerson: Yup.string().required('Harus dipilih'),
-})
+});
const defaultValues = {
email: '',
@@ -209,7 +283,8 @@ const defaultValues = {
mobile: '',
address: '',
city: '',
- description: ''
-}
+ state: '',
+ description: '',
+};
-export default RequestForQuotation
+export default RequestForQuotation;
diff --git a/src/lib/form/components/SuratDukungan.jsx b/src/lib/form/components/SuratDukungan.jsx
index 31e7ee83..fadb2c57 100644
--- a/src/lib/form/components/SuratDukungan.jsx
+++ b/src/lib/form/components/SuratDukungan.jsx
@@ -1,5 +1,6 @@
import HookFormSelect from '@/core/components/elements/Select/HookFormSelect';
import cityApi from '@/lib/address/api/cityApi';
+import stateApi from '@/lib/address/api/stateApi.js';
import { yupResolver } from '@hookform/resolvers/yup';
import React, { useEffect, useRef, useState } from 'react';
import ReCAPTCHA from 'react-google-recaptcha';
@@ -10,7 +11,7 @@ import createLeadsApi from '../api/createLeadApi';
import PageContent from '@/lib/content/components/PageContent';
-import useAuth from '@/core/hooks/useAuth'
+import useAuth from '@/core/hooks/useAuth';
import { useRouter } from 'next/router';
const CreateSuratDukungan = () => {
@@ -20,32 +21,21 @@ const CreateSuratDukungan = () => {
formState: { errors },
control,
reset,
+ watch,
+ setValue,
} = useForm({
resolver: yupResolver(validationSchema),
defaultValues,
});
- const [cities, setCities] = useState([]);
const [company_unit, setCompany_unit] = useState([]);
const recaptchaRef = useRef(null);
- const router = useRouter()
+ const router = useRouter();
- const auth = useAuth()
-
- useEffect(() => {
- if(auth == false) {
- router.push('/login')
- }
- const loadCities = async () => {
- let dataCities = await cityApi();
- dataCities = dataCities.map((city) => ({
- value: city.id,
- label: city.name,
- }));
- setCities(dataCities);
- };
- loadCities();
- }, [auth]);
+ const auth = useAuth();
+ if (auth == false) {
+ router.push(`/login?next=${encodeURIComponent('/surat-dukungan')}`);
+ }
const onSubmitHandler = async (values) => {
const recaptchaValue = recaptchaRef.current.getValue();
@@ -85,6 +75,9 @@ const CreateSuratDukungan = () => {
recaptchaRef.current.reset();
}
};
+ if (!auth) {
+ return;
+ }
return (
<div className='container mx-auto p-4 md:p-0 my-0 md:my-10'>
<h1 className='text-h-sm md:text-title-sm font-semibold mb-6'>
@@ -241,7 +234,7 @@ const CreateSuratDukungan = () => {
</div>
</form>
</div>
- <PageContent path='/surat-dukungan'/>
+ <PageContent path='/surat-dukungan' />
</div>
</div>
</div>
diff --git a/src/lib/home/components/ServiceList.jsx b/src/lib/home/components/ServiceList.jsx
index 6d03a587..e32e8747 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'
+ href='/pembayaran-tempo-detail'
className='border border-gray-200 p-2 flex items-center gap-x-2 rounded-lg'
>
<div className=''>
diff --git a/src/lib/invoice/components/Invoice.jsx b/src/lib/invoice/components/Invoice.jsx
index 81202b1c..15bfa746 100644
--- a/src/lib/invoice/components/Invoice.jsx
+++ b/src/lib/invoice/components/Invoice.jsx
@@ -13,6 +13,7 @@ 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 [totalAmount, setTotalAmount] = useState(0)
@@ -255,8 +256,8 @@ const Invoice = ({ id }) => {
{currencyFormat(invoice.data?.amountTotal)}
</div>
- <div className='text-right'>PPN 11% (Incl.)</div>
- <div className='text-right font-medium'>{currencyFormat(totalAmount * 0.11)}</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>
diff --git a/src/lib/pengajuan-tempo/api/createPengajuanTempoApi.js b/src/lib/pengajuan-tempo/api/createPengajuanTempoApi.js
new file mode 100644
index 00000000..af1d6c3a
--- /dev/null
+++ b/src/lib/pengajuan-tempo/api/createPengajuanTempoApi.js
@@ -0,0 +1,14 @@
+import odooApi from '@/core/api/odooApi';
+import { getAuth } from '@/core/utils/auth';
+
+const createPengajuanTempoApi = async (data) => {
+ const auth = getAuth();
+ const dataPengajuanTempo = await odooApi(
+ 'POST',
+ `/api/v1/partner/pengajuan_tempo`,
+ data
+ );
+ return dataPengajuanTempo;
+};
+
+export default createPengajuanTempoApi;
diff --git a/src/lib/pengajuan-tempo/api/editAuthTempo.js b/src/lib/pengajuan-tempo/api/editAuthTempo.js
new file mode 100644
index 00000000..5fa3786b
--- /dev/null
+++ b/src/lib/pengajuan-tempo/api/editAuthTempo.js
@@ -0,0 +1,13 @@
+import odooApi from '@/core/api/odooApi';
+import { getAuth } from '@/core/utils/auth';
+
+const editAuthTempo = async () => {
+ const auth = getAuth();
+ const dataProfile = await odooApi(
+ 'PUT',
+ `/api/v1/user/${auth.id}/after_request_tempo`
+ );
+ return dataProfile;
+};
+
+export default editAuthTempo;
diff --git a/src/lib/pengajuan-tempo/component/Dokumen.jsx b/src/lib/pengajuan-tempo/component/Dokumen.jsx
new file mode 100644
index 00000000..f986d47a
--- /dev/null
+++ b/src/lib/pengajuan-tempo/component/Dokumen.jsx
@@ -0,0 +1,1351 @@
+import React, { useState, useEffect, useMemo, useRef } from 'react';
+import { Controller, set, useForm } from 'react-hook-form';
+import { usePengajuanTempoStoreDokumen } from '../../../../src-migrate/modules/register/stores/usePengajuanTempoStore';
+import ProgressBar from '@ramonak/react-progress-bar';
+import { UseToastOptions } from '@chakra-ui/react';
+import { toast } from 'react-hot-toast';
+import getFileBase64 from '@/core/utils/getFileBase64';
+import useDevice from '@/core/hooks/useDevice';
+import imageCompression from 'browser-image-compression';
+const Dokumen = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
+ const { control, watch } = useForm();
+ const {
+ formDokumen,
+ errorsDokumen,
+ validateDokumen,
+ updateFormDokumen,
+ getJumlahDokumenDiisi,
+ } = usePengajuanTempoStoreDokumen();
+ const { isDesktop, isMobile } = useDevice();
+ // const handleInputChange = (event) => {
+ // const { name, value } = event.target;
+ // updateFormDokumen(name, value);
+ // validateDokumen();
+ // };
+ const handleInputChange = 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);
+ updateFormDokumen(name, file.name, fileExtension, fileBase64);
+ }
+ validateDokumen();
+ }
+ };
+
+ const isFormValid = useMemo(
+ () => Object.keys(errorsDokumen).length === 0,
+ [errorsDokumen]
+ );
+ const dokumenNibRef = useRef(null);
+ const dokumenNpwpRef = useRef(null);
+ const dokumenSppkpRef = useRef(null);
+ const dokumenAktaPerubahanRef = useRef(null);
+ const dokumenKtpDirutRef = useRef(null);
+ const dokumenAktaPendirianRef = useRef(null);
+ const dokumenLaporanKeuanganRef = useRef(null);
+ const dokumenFotoKantorRef = useRef(null);
+ const dokumenTempatBekerjaRef = useRef(null);
+
+ useEffect(() => {
+ validateDokumen();
+ // window.scrollTo({
+ // top: 0,
+ // behavior: 'smooth',
+ // });
+ const loadIndustries = async () => {
+ if (!isFormValid) {
+ const options = {
+ behavior: 'smooth',
+ block: 'center',
+ };
+ if (errorsDokumen.dokumenNib && dokumenNibRef.current) {
+ dokumenNibRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsDokumen.dokumenNpwp && dokumenNpwpRef.current) {
+ dokumenNpwpRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsDokumen.dokumenSppkp && dokumenSppkpRef.current) {
+ dokumenSppkpRef.current.scrollIntoView(options);
+ return;
+ }
+ if (
+ errorsDokumen.dokumenAktaPerubahan &&
+ dokumenAktaPerubahanRef.current
+ ) {
+ dokumenAktaPerubahanRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsDokumen.dokumenKtpDirut && dokumenKtpDirutRef.current) {
+ dokumenKtpDirutRef.current.scrollIntoView(options);
+ return;
+ }
+ if (
+ errorsDokumen.dokumenAktaPendirian &&
+ dokumenAktaPendirianRef.current
+ ) {
+ dokumenAktaPendirianRef.current.scrollIntoView(options);
+ return;
+ }
+ if (
+ errorsDokumen.dokumenLaporanKeuangan &&
+ dokumenLaporanKeuanganRef.current
+ ) {
+ dokumenLaporanKeuanganRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsDokumen.dokumenFotoKantor && dokumenFotoKantorRef.current) {
+ dokumenFotoKantorRef.current.scrollIntoView(options);
+ return;
+ }
+ if (
+ errorsDokumen.dokumenTempatBekerja &&
+ dokumenTempatBekerjaRef.current
+ ) {
+ dokumenTempatBekerjaRef.current.scrollIntoView(options);
+ return;
+ }
+ }
+ };
+ loadIndustries();
+ }, [buttonSubmitClick, chekValid]);
+
+ useEffect(() => {
+ validateDokumen();
+ }, [buttonSubmitClick]);
+ return (
+ <>
+ {isDesktop && (
+ <div>
+ <h1 className={`font-bold ${isKonfirmasi ? 'text-xl' : ''}`}>
+ Dokumen
+ </h1>
+ <form className='flex mt-4 flex-col w-full '>
+ <div className='w-full grid grid-cols-[1fr_auto_1fr] gap-5'>
+ <div className='kolom-kiri w-full grid grid-rows-2 gap-7 '>
+ <div
+ className='w-full grid grid-cols-2 gap-5'
+ ref={dokumenNpwpRef}
+ >
+ <div>
+ <label className='form-label text-nowrap'>
+ NPWP Perusahaan
+ </label>
+ <span className='text-xs opacity-60 text-red-500'>
+ Format: pdf, jpeg, jpg, png. max file size 2MB
+ </span>
+ </div>
+ <div className=''>
+ <div className='flex flex-col items-start'>
+ <label
+ htmlFor='dokumenNpwp'
+ className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+ >
+ {formDokumen?.dokumenNpwp?.name
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenNpwp}
+ id='dokumenNpwp'
+ name='dokumenNpwp'
+ type='file'
+ title=' '
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenNpwp}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2'>
+ {formDokumen?.dokumenNpwp?.name}
+ </span>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenNpwp}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div
+ className='w-full grid grid-cols-2 gap-5'
+ ref={dokumenSppkpRef}
+ >
+ <div>
+ <label className='form-label text-nowrap'>SPPKP</label>
+ <span className='text-xs opacity-60 text-red-500'>
+ Format: pdf, jpeg, jpg, png. max file size 2MB
+ </span>
+ </div>
+ <div className=''>
+ <div className='flex flex-col items-start'>
+ <label
+ htmlFor='dokumenSppkp'
+ className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+ >
+ {formDokumen?.dokumenSppkp?.name.length > 0
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenSppkp}
+ id='dokumenSppkp'
+ name='dokumenSppkp'
+ type='file'
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenSppkp}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2'>
+ {formDokumen?.dokumenSppkp?.name}
+ </span>
+ </div>
+
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenSppkp}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div
+ className='w-full grid grid-cols-2 gap-5'
+ ref={dokumenNibRef}
+ >
+ <div>
+ <label className='form-label text-nowrap'>
+ NIB
+ <span className='text-danger-500 text-xl'>*</span>
+ </label>
+ <span className='text-xs opacity-60 text-red-500'>
+ Format: pdf, jpeg, jpg, png. max file size 2MB
+ </span>
+ </div>
+ <div className=''>
+ <div className='flex flex-col items-start'>
+ <label
+ htmlFor='dokumenNib'
+ className='cursor-pointer bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded min-w-40 text-center'
+ >
+ {formDokumen?.dokumenNib?.name
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenNib'
+ name='dokumenNib'
+ type='file'
+ title=' '
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenNib}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2'>
+ {formDokumen?.dokumenNib?.name}
+ </span>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenNib}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='w-full grid grid-cols-2 gap-5'>
+ <div>
+ <label className='form-label text-nowrap'>
+ SIUP <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=''>
+ <div className='flex flex-col items-start '>
+ <label
+ htmlFor='dokumenSiup'
+ className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+ >
+ {formDokumen?.dokumenSiup?.name.length > 0
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenAktaPerubahan}
+ id='dokumenSiup'
+ name='dokumenSiup'
+ type='file'
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenAktaPerubahan}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2'>
+ {formDokumen?.dokumenSiup?.name}
+ </span>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenSiup}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='w-full grid grid-cols-2 gap-5'>
+ <div>
+ <label className='form-label text-nowrap'>
+ TDP <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=''>
+ <div className='flex flex-col items-start'>
+ <label
+ htmlFor='dokumenTdp'
+ className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+ >
+ {formDokumen?.dokumenTdp?.name.length > 0
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenAktaPerubahan}
+ id='dokumenTdp'
+ name='dokumenTdp'
+ type='file'
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenTdp}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2'>
+ {formDokumen?.dokumenTdp?.name}
+ </span>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenTdp}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='w-full grid grid-cols-2 gap-5'>
+ <div>
+ <label className='form-label text-nowrap'>
+ SKDP <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=''>
+ <div className='flex flex-col items-start'>
+ <label
+ htmlFor='dokumenSkdp'
+ className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+ >
+ {formDokumen?.dokumenSkdp?.name.length > 0
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenAktaPerubahan}
+ id='dokumenSkdp'
+ name='dokumenSkdp'
+ type='file'
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenSkdp}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2'>
+ {formDokumen?.dokumenSkdp?.name}
+ </span>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenSkdp}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='w-full grid grid-cols-2 gap-5'>
+ <div>
+ <label className='form-label text-nowrap'>
+ SKT <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=''>
+ <div className='flex flex-col items-start'>
+ <label
+ htmlFor='dokumenSkt'
+ className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+ >
+ {formDokumen?.dokumenSkt?.name.length > 0
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenAktaPerubahan}
+ id='dokumenSkt'
+ name='dokumenSkt'
+ type='file'
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenSkt}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2'>
+ {formDokumen?.dokumenSkt?.name}
+ </span>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenSkt}
+ </div>
+ )}
+ </div>
+ </div>
+ </div>
+ <div className='w-px bg-gray-300'></div>
+ <div
+ className='kolom kanan w-full grid grid-rows-2 gap-10 '
+ ref={dokumenAktaPendirianRef}
+ >
+ <div className='w-full grid grid-cols-2 gap-5'>
+ <div>
+ <label className='form-label text-nowrap'>
+ Akta Pendirian{' '}
+ <span className=' opacity-60'>(Opsional)</span>{' '}
+ <span className='text-danger-500 text-xl'>*</span>
+ </label>
+ <span className='text-xs opacity-60 text-red-500'>
+ Format: pdf, jpeg, jpg, png. max file size 2MB
+ </span>
+ </div>
+ <div className=''>
+ <div className='flex flex-col items-start'>
+ <label
+ htmlFor='dokumenAktaPendirian'
+ className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+ >
+ {formDokumen?.dokumenAktaPendirian?.name
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenAktaPendirian}
+ id='dokumenAktaPendirian'
+ name='dokumenAktaPendirian'
+ type='file'
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenAktaPendirian}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2'>
+ {formDokumen?.dokumenAktaPendirian?.name}
+ </span>
+ </div>
+
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenAktaPendirian}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div
+ className='w-full grid grid-cols-2 gap-5'
+ ref={dokumenAktaPerubahanRef}
+ >
+ <div>
+ <label className='form-label text-nowrap'>
+ Akta Perubahan{' '}
+ <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=''>
+ <div className='flex flex-col items-start'>
+ <label
+ htmlFor='dokumenAktaPerubahan'
+ className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+ >
+ {formDokumen?.dokumenAktaPerubahan?.name.length > 0
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenAktaPerubahan}
+ id='dokumenAktaPerubahan'
+ name='dokumenAktaPerubahan'
+ type='file'
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenAktaPerubahan}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2'>
+ {formDokumen?.dokumenAktaPerubahan?.name}
+ </span>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenAktaPerubahan}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div
+ className='w-full grid grid-cols-2 gap-5'
+ ref={dokumenTempatBekerjaRef}
+ >
+ <div>
+ <label className='form-label text-nowrap'>
+ Foto tempat kerja
+ </label>
+ <span className='text-xs opacity-60 text-red-500'>
+ Format: pdf, jpeg, jpg, png. max file size 2MB
+ </span>
+ </div>
+ <div className=''>
+ <div className='flex flex-col items-start'>
+ <label
+ htmlFor='dokumenTempatBekerja'
+ className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+ >
+ {formDokumen?.dokumenTempatBekerja?.name
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenTempatBekerja}
+ id='dokumenTempatBekerja'
+ name='dokumenTempatBekerja'
+ type='file'
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenTempatBekerja}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2'>
+ {formDokumen?.dokumenTempatBekerja?.name}
+ </span>
+ </div>
+
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenTempatBekerja}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div
+ className='w-full grid grid-cols-2 gap-5'
+ ref={dokumenFotoKantorRef}
+ >
+ <div>
+ <label className='form-label text-nowrap'>
+ Foto Kantor (Tampak Depan)
+ </label>
+ <span className='text-xs opacity-60 text-red-500'>
+ Format: pdf, jpeg, jpg, png. max file size 2MB
+ </span>
+ </div>
+ <div className=''>
+ <div className='flex flex-col items-start'>
+ <label
+ htmlFor='dokumenFotoKantor'
+ className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+ >
+ {formDokumen?.dokumenFotoKantor?.name
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenFotoKantor}
+ id='dokumenFotoKantor'
+ name='dokumenFotoKantor'
+ type='file'
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenFotoKantor}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2'>
+ {formDokumen?.dokumenFotoKantor?.name}
+ </span>
+ </div>
+
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenFotoKantor}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div
+ className='w-full grid grid-cols-2 gap-5'
+ ref={dokumenKtpDirutRef}
+ >
+ <div>
+ <label className='form-label text-nowrap'>
+ KTP Dirut/Direktur{' '}
+ <span className=' opacity-60'>(Opsional)</span>{' '}
+ <span className='text-danger-500 text-xl'>*</span>
+ </label>
+ <span className='text-xs opacity-60 text-red-500'>
+ Format: pdf, jpeg, jpg, png. max file size 2MB
+ </span>
+ </div>
+ <div className=''>
+ <div className='flex flex-col items-start'>
+ <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'
+ >
+ {formDokumen?.dokumenKtpDirut?.name
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenKtpDirut}
+ id='dokumenKtpDirut'
+ name='dokumenKtpDirut'
+ type='file'
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenKtpDirut}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2'>
+ {formDokumen?.dokumenKtpDirut?.name}
+ </span>
+ </div>
+
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenKtpDirut}
+ </div>
+ )}
+ </div>
+ </div>
+ <div
+ className='w-full grid grid-cols-2 gap-5'
+ ref={dokumenLaporanKeuanganRef}
+ >
+ <div>
+ <label className='form-label text-nowrap'>
+ Laporan Keuangan
+ <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=''>
+ <div className='flex flex-col items-start'>
+ <label
+ htmlFor='dokumenLaporanKeuangan'
+ className='cursor-pointer min-w-40 text-center bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded'
+ >
+ {formDokumen?.dokumenLaporanKeuangan?.name
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenLaporanKeuangan}
+ id='dokumenLaporanKeuangan'
+ name='dokumenLaporanKeuangan'
+ type='file'
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenLaporanKeuangan}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2'>
+ {formDokumen?.dokumenLaporanKeuangan?.name}
+ </span>
+ </div>
+
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenLaporanKeuangan}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div></div>
+ </div>
+ </div>
+ </form>
+ <div className='w-1/2 mt-8 flex gap-3 flex-col'>
+ <span className='opacity-75 text-danger-500 text-sm'>
+ *Jika anda ingin kredit limit lebih dari 25 Juta, harap lampirkan
+ dokumen yang diberi tanda bintang *)
+ </span>
+ <div className='flex justify-between'>
+ <p className='font-bold'>Upload Progress</p>
+ <p>
+ <span className='text-red-500 font-bold'>
+ {parseInt((getJumlahDokumenDiisi() / 13) * 100)} %
+ </span>
+ <span className='ml-2 opacity-60'>
+ {getJumlahDokumenDiisi() >= 5
+ ? getJumlahDokumenDiisi() == 13
+ ? 'Selesai'
+ : 'Sedikit Lagi'
+ : ''}
+ </span>
+ </p>
+ </div>
+ {/* 50 keatas baru muncul kata kata sedikit lagi */}
+ <ProgressBar
+ completed={getJumlahDokumenDiisi()}
+ bgColor='#ef4444'
+ labelAlignment='outside'
+ isLabelVisible={false}
+ baseBgColor='#E5E7EB'
+ labelColor='#e80909'
+ labelSize='0'
+ maxCompleted={13}
+ height='14px'
+ />
+ <span className='opacity-75'>
+ Tingkatin sedikit lagi agar pengajuan tempo kamu dapat kami proses
+ dengan cepat
+ </span>
+ </div>
+ </div>
+ )}
+ {isMobile && (
+ <div>
+ <h1
+ className={`font-bold py-4 mt-8 ${
+ isKonfirmasi ? 'text-xl' : 'text-xl'
+ }`}
+ >
+ Dokumen
+ </h1>
+ <form className='flex mt-4 flex-col w-full '>
+ <div className='w-full flex flex-col gap-4'>
+ <div className='w-full flex flex-col gap-2' ref={dokumenNpwpRef}>
+ <label className='form-label text-nowrap'>
+ NPWP Perusahaan
+ </label>
+ <div className='flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenNpwp'
+ className='cursor-pointer bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded w-fit min-w-40 text-nowrap text-center flex items-center justify-center'
+ >
+ {formDokumen?.dokumenNpwp?.name
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenNpwp}
+ id='dokumenNpwp'
+ name='dokumenNpwp'
+ type='file'
+ title=' '
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenNpwp}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2 truncate'>
+ {formDokumen?.dokumenNpwp?.name}
+ </span>
+ </div>
+ <span className='text-xs opacity-60 text-red-500'>
+ Format: pdf, jpeg, jpg, png. max file size 2MB
+ </span>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenNpwp}
+ </div>
+ )}
+ </div>
+
+ <div className='w-full flex flex-col gap-2' ref={dokumenSppkpRef}>
+ <label className='form-label text-nowrap'>SPPKP</label>
+ <div className='flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenSppkp'
+ className='cursor-pointer bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded w-fit min-w-40 text-nowrap text-center flex items-center justify-center'
+ >
+ {formDokumen?.dokumenSppkp?.name
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenNpwp}
+ id='dokumenSppkp'
+ name='dokumenSppkp'
+ type='file'
+ title=' '
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenSppkp}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2 truncate'>
+ {formDokumen?.dokumenSppkp?.name}
+ </span>
+ </div>
+ <span className='text-xs opacity-60 text-red-500'>
+ Format: pdf, jpeg, jpg, png. max file size 2MB
+ </span>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenSppkp}
+ </div>
+ )}
+ </div>
+ <div className='w-full flex flex-col gap-2' ref={dokumenNibRef}>
+ <label className='form-label text-nowrap'>
+ NIB
+ <span className='text-danger-500 text-xl'>*</span>
+ </label>
+ <div className='flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenNib'
+ className='cursor-pointer bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded w-fit min-w-40 text-nowrap text-center flex items-center justify-center'
+ >
+ {formDokumen?.dokumenNib?.name
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenNib'
+ name='dokumenNib'
+ type='file'
+ title=' '
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenNib}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2 truncate'>
+ {formDokumen?.dokumenNib?.name}
+ </span>
+ </div>
+ <span className='text-xs opacity-60 text-red-500'>
+ Format: pdf, jpeg, jpg, png. max file size 2MB
+ </span>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenNib}
+ </div>
+ )}
+ </div>
+
+ <div className='w-full flex flex-col gap-2'>
+ <label className='form-label text-nowrap'>
+ SIUP <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ <div className='flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenSiup'
+ className='cursor-pointer bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded w-fit min-w-40 text-nowrap text-center flex items-center justify-center'
+ >
+ {formDokumen?.dokumenSiup?.name
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenNpwp}
+ id='dokumenSiup'
+ name='dokumenSiup'
+ type='file'
+ title=' '
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenSiup}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2 truncate'>
+ {formDokumen?.dokumenSiup?.name}
+ </span>
+ </div>
+ <span className='text-xs opacity-60 text-red-500'>
+ Format: pdf, jpeg, jpg, png. max file size 2MB
+ </span>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenSiup}
+ </div>
+ )}
+ </div>
+
+ <div className='w-full flex flex-col gap-2'>
+ <label className='form-label text-nowrap'>
+ TDP <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ <div className='flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenTdp'
+ className='cursor-pointer bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded w-fit min-w-40 text-nowrap text-center flex items-center justify-center'
+ >
+ {formDokumen?.dokumenTdp?.name
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenNpwp}
+ id='dokumenTdp'
+ name='dokumenTdp'
+ type='file'
+ title=' '
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenTdp}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2 truncate'>
+ {formDokumen?.dokumenTdp?.name}
+ </span>
+ </div>
+ <span className='text-xs opacity-60 text-red-500'>
+ Format: pdf, jpeg, jpg, png. max file size 2MB
+ </span>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenTdp}
+ </div>
+ )}
+ </div>
+ <div className='w-full flex flex-col gap-2'>
+ <label className='form-label text-nowrap'>
+ SKDP <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ <div className='flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenSkdp'
+ className='cursor-pointer bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded w-fit min-w-40 text-nowrap text-center flex items-center justify-center'
+ >
+ {formDokumen?.dokumenTdp?.name
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenNpwp}
+ id='dokumenSkdp'
+ name='dokumenSkdp'
+ type='file'
+ title=' '
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenSkdp}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2 truncate'>
+ {formDokumen?.dokumenSkdp?.name}
+ </span>
+ </div>
+ <span className='text-xs opacity-60 text-red-500'>
+ Format: pdf, jpeg, jpg, png. max file size 2MB
+ </span>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenSkdp}
+ </div>
+ )}
+ </div>
+ <div className='w-full flex flex-col gap-2'>
+ <label className='form-label text-nowrap'>
+ SKT <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ <div className='flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenSkt'
+ className='cursor-pointer bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded w-fit min-w-40 text-nowrap text-center flex items-center justify-center'
+ >
+ {formDokumen?.dokumenSkt?.name
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenNpwp}
+ id='dokumenSkt'
+ name='dokumenSkt'
+ type='file'
+ title=' '
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenSkt}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2 truncate'>
+ {formDokumen?.dokumenSkt?.name}
+ </span>
+ </div>
+ <span className='text-xs opacity-60 text-red-500'>
+ Format: pdf, jpeg, jpg, png. max file size 2MB
+ </span>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenSkdp}
+ </div>
+ )}
+ </div>
+ <div
+ className='w-full flex flex-col gap-2'
+ ref={dokumenAktaPendirianRef}
+ >
+ <label className='form-label text-nowrap'>
+ Akta Pendirian <span className=' opacity-60'>(Opsional)</span>{' '}
+ <span className='text-danger-500 text-xl'>*</span>
+ </label>
+ <div className='flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenAktaPendirian'
+ className='cursor-pointer bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded w-fit min-w-40 text-nowrap text-center flex items-center justify-center'
+ >
+ {formDokumen?.dokumenAktaPendirian?.name
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenNpwp}
+ id='dokumenAktaPendirian'
+ name='dokumenAktaPendirian'
+ type='file'
+ title=' '
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenAktaPendirian}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2 truncate'>
+ {formDokumen?.dokumenAktaPendirian?.name}
+ </span>
+ </div>
+ <span className='text-xs opacity-60 text-red-500'>
+ Format: pdf, jpeg, jpg, png. max file size 2MB
+ </span>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenAktaPendirian}
+ </div>
+ )}
+ </div>
+ <div
+ className='w-full flex flex-col gap-2'
+ ref={dokumenAktaPerubahanRef}
+ >
+ <label className='form-label text-nowrap'>
+ Akta Perubahan <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ <div className='flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenAktaPerubahan'
+ className='cursor-pointer bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded w-fit min-w-40 text-nowrap text-center flex items-center justify-center'
+ >
+ {formDokumen?.dokumenAktaPerubahan?.name
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenNpwp}
+ id='dokumenAktaPerubahan'
+ name='dokumenAktaPerubahan'
+ type='file'
+ title=' '
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenAktaPerubahan}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2 truncate'>
+ {formDokumen?.dokumenAktaPerubahan?.name}
+ </span>
+ </div>
+ <span className='text-xs opacity-60 text-red-500'>
+ Format: pdf, jpeg, jpg, png. max file size 2MB
+ </span>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenAktaPerubahan}
+ </div>
+ )}
+ </div>
+
+ <div
+ className='w-full flex flex-col gap-2'
+ ref={dokumenTempatBekerjaRef}
+ >
+ <label className='form-label text-nowrap'>
+ Foto tempat kerja
+ </label>
+ <div className='flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenTempatBekerja'
+ className='cursor-pointer bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded w-fit min-w-40 text-nowrap text-center flex items-center justify-center'
+ >
+ {formDokumen?.dokumenTempatBekerja?.name
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenNpwp}
+ id='dokumenTempatBekerja'
+ name='dokumenTempatBekerja'
+ type='file'
+ title=' '
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenTempatBekerja}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2 truncate'>
+ {formDokumen?.dokumenTempatBekerja?.name}
+ </span>
+ </div>
+ <span className='text-xs opacity-60 text-red-500'>
+ Format: pdf, jpeg, jpg, png. max file size 2MB
+ </span>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenTempatBekerja}
+ </div>
+ )}
+ </div>
+ <div
+ className='w-full flex flex-col gap-2'
+ ref={dokumenFotoKantorRef}
+ >
+ <label className='form-label text-nowrap'>
+ Foto Kantor (Tampak Depan)
+ </label>
+ <div className='flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenFotoKantor'
+ className='cursor-pointer bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded w-fit min-w-40 text-nowrap text-center flex items-center justify-center'
+ >
+ {formDokumen?.dokumenFotoKantor?.name
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenNpwp}
+ id='dokumenFotoKantor'
+ name='dokumenFotoKantor'
+ type='file'
+ title=' '
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenFotoKantor}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2 truncate'>
+ {formDokumen?.dokumenFotoKantor?.name}
+ </span>
+ </div>
+ <span className='text-xs opacity-60 text-red-500'>
+ Format: pdf, jpeg, jpg, png. max file size 2MB
+ </span>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenFotoKantor}
+ </div>
+ )}
+ </div>
+ <div
+ className='w-full flex flex-col gap-2'
+ ref={dokumenKtpDirutRef}
+ >
+ <label className='form-label text-nowrap'>
+ KTP Dirut/Direktur{' '}
+ <span className=' opacity-60'>(Opsional)</span>{' '}
+ <span className='text-danger-500 text-xl'>*</span>
+ </label>
+ <div className='flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenKtpDirut'
+ className='cursor-pointer bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded w-fit min-w-40 text-nowrap text-center flex items-center justify-center'
+ >
+ {formDokumen?.dokumenKtpDirut?.name
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenNpwp}
+ id='dokumenKtpDirut'
+ name='dokumenKtpDirut'
+ type='file'
+ title=' '
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenKtpDirut}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 line-clamp-2 truncate'>
+ {formDokumen?.dokumenKtpDirut?.name}
+ </span>
+ </div>
+ <span className='text-xs opacity-60 text-red-500'>
+ Format: pdf, jpeg, jpg, png. max file size 2MB
+ </span>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenKtpDirut}
+ </div>
+ )}
+ </div>
+
+ <div
+ className='w-full flex flex-col gap-2'
+ ref={dokumenLaporanKeuanganRef}
+ >
+ <label className='form-label text-nowrap'>
+ Laporan Keuangan{' '}
+ <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ <div className='flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenLaporanKeuangan'
+ className='cursor-pointer bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded w-fit min-w-40 text-nowrap text-center flex items-center justify-center'
+ >
+ {formDokumen?.dokumenLaporanKeuangan?.name
+ ? 'Ubah Dokumen'
+ : 'Upload Dokumen'}
+ </label>
+ <input
+ // value={formDokumen.dokumenNpwp}
+ id='dokumenLaporanKeuangan'
+ name='dokumenLaporanKeuangan'
+ type='file'
+ title=' '
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenLaporanKeuangan}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <span className='mt-2 text-gray-600 truncate'>
+ {formDokumen?.dokumenLaporanKeuangan?.name}
+ </span>
+ </div>
+ <span className='text-xs opacity-60 text-red-500'>
+ Format: pdf, jpeg, jpg, png. max file size 2MB
+ </span>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenLaporanKeuangan}
+ </div>
+ )}
+ </div>
+ </div>
+ </form>
+ <div className='w-full mt-8 flex gap-3 flex-col'>
+ <span className='opacity-75 text-danger-500 text-xs'>
+ *Jika anda ingin kredit limit lebih dari 25 Juta, harap lampirkan
+ dokumen yang diberi tanda bintang *)
+ </span>
+ <div className='flex justify-between'>
+ <p className='font-bold'>Upload Progress</p>
+ <p>
+ <span className='text-red-500 font-bold'>
+ {parseInt((getJumlahDokumenDiisi() / 13) * 100)} %
+ </span>
+ <span className='ml-2 opacity-60'>
+ {getJumlahDokumenDiisi() >= 5
+ ? getJumlahDokumenDiisi() == 13
+ ? 'Selesai'
+ : 'Sedikit Lagi'
+ : ''}
+ </span>
+ </p>
+ </div>
+ {/* 50 keatas baru muncul kata kata sedikit lagi */}
+ <ProgressBar
+ completed={getJumlahDokumenDiisi()}
+ bgColor='#ef4444'
+ labelAlignment='outside'
+ isLabelVisible={false}
+ baseBgColor='#E5E7EB'
+ labelColor='#e80909'
+ labelSize='0'
+ maxCompleted={13}
+ height='10px'
+ />
+ <span className='opacity-75 text-xs text-red-500'>
+ Tingkatin sedikit lagi agar pengajuan tempo kamu dapat kami proses
+ dengan cepat
+ </span>
+ </div>
+ </div>
+ )}
+ </>
+ );
+};
+
+export default Dokumen;
diff --git a/src/lib/pengajuan-tempo/component/FinishTempo.jsx b/src/lib/pengajuan-tempo/component/FinishTempo.jsx
new file mode 100644
index 00000000..bfcd0909
--- /dev/null
+++ b/src/lib/pengajuan-tempo/component/FinishTempo.jsx
@@ -0,0 +1,112 @@
+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();
+ const so_order = query?.order_id?.replaceAll('-', '/');
+ useEffect(() => {
+ const fetchData = async () => {
+ const fetchedData = await odooApi(
+ 'GET',
+ `/api/v1/sale_order_number?sale_number=${so_order}`
+ );
+ setData(fetchedData[0]);
+ };
+ fetchData();
+ }, [query]);
+
+ 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'
+ }`}
+ >
+ {query?.status == 'finish' &&
+ 'Form Pengajuan Tempo kamu Telah Berhasil Didaftarkan Mohon menunggu hingga Proses Verifikasi Selesai Dilakukan'}
+ {query?.status == 'switch-account' &&
+ 'Form Pengajuan Tempo Kamu Gagal Dilakukan'}
+ {query?.status == 'review' &&
+ 'Pengajuan Tempo dalam Proses Verifikasi'}
+ {query?.status == 'approve' && 'Pengajuan Tempo Berhasil'}
+ </h1>
+ </div>
+ {query?.status == 'finish' && (
+ <Image
+ src='/images/REGISTRASI-TEMPO.svg'
+ alt='Registrasi Tempo'
+ width={isMobile ? 300 : 550}
+ height={isMobile ? 300 : 550}
+ />
+ )}
+ {query?.status == 'switch-account' && (
+ <Image
+ src='/images/ICON-TEMPO.png'
+ alt='Registrasi Tempo'
+ width={isMobile ? 300 : 550}
+ height={isMobile ? 300 : 550}
+ />
+ )}
+ {query?.status == 'review' && (
+ <Image
+ src='/images/ICON-DOKUMEN-VERIFIKASI.png'
+ alt='Registrasi Tempo'
+ width={isMobile ? 300 : 550}
+ height={isMobile ? 300 : 550}
+ />
+ )}
+ {query?.status == 'approve' && (
+ <Image
+ src='/images/ICON-WEBSITE-TELAH-MENDAFTAR-AKUN-TEMPO.svg'
+ alt='Registrasi Tempo'
+ width={isMobile ? 300 : 600}
+ 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'
+ }`}
+ >
+ {query?.status == 'switch-account' &&
+ 'Terima kasih atas minat anda untuk mendaftar Tempo, namun sayangnya akun anda bukan merupakan akun bisnis. Segera ubah akun anda menjadi Bisnis untuk menggunakan fitur ini'}
+ {query?.status == 'finish' &&
+ 'Mohon menunggu untuk verifikasi dokumen dan kelengkapan data yang telah anda berikan. Proses approval pembayaran tempo kamu berhasil atau tidak akan diinfokan melalui email perusahaan / email yang mendaftar'}
+ {query?.status == 'review' &&
+ 'Proses pengajuan tempo anda saat ini sedang dalam tahapan proses verifikasi oleh tim indoteknik, mohon menunggu'}
+ {query?.status == 'approve' &&
+ 'Proses pengajuan tempo anda sudah berhasil terdaftar di indoteknik.com. Nikmati pembelian anda di website indoteknik dengan menggunakan pembayaran tempo'}
+ </div>
+ <Link
+ href={query?.status == 'switch-account' ? `/my/profile` : `/my/tempo/`}
+ className='btn-solid-red rounded-md text-base flex flex-row items-center justify-center'
+ >
+ {query?.status == 'switch-account'
+ ? 'Ubah Akun'
+ : query?.status == 'approve'
+ ? 'Lihat Detail Tempo'
+ : 'Lihat Status Pendaftaran'}
+ <ChevronRightIcon className='w-5' />
+ </Link>
+ </div>
+ );
+};
+
+export default FinishTempo;
diff --git a/src/lib/pengajuan-tempo/component/Konfirmasi.jsx b/src/lib/pengajuan-tempo/component/Konfirmasi.jsx
new file mode 100644
index 00000000..0e593ac4
--- /dev/null
+++ b/src/lib/pengajuan-tempo/component/Konfirmasi.jsx
@@ -0,0 +1,297 @@
+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 {
+ usePengajuanTempoStoreDokumen,
+ usePengajuanTempoStore,
+} from '../../../../src-migrate/modules/register/stores/usePengajuanTempoStore';
+import KontakPerusahaan from './KontakPerusahaan';
+import ProgressBar from '@ramonak/react-progress-bar';
+import { 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 Pengiriman from './Pengiriman';
+import KonfirmasiDokumen from './KonfirmasiDokumen';
+import useDevice from '@/core/hooks/useDevice';
+import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/24/outline';
+const Konfirmasi = ({ chekValid, buttonSubmitClick }) => {
+ const { control, watch, setValue, getValues } = 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 [industries, setIndustries] = useState([]);
+ const {
+ formDokumen,
+ errorsDokumen,
+ validateDokumen,
+ updateFormDokumen,
+ getJumlahDokumenDiisi,
+ } = usePengajuanTempoStoreDokumen();
+ const { form, errors, validate, updateForm } = usePengajuanTempoStore();
+ const handleInputChange = 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();
+
+ if (!fileExtension || !allowedExtensions.includes(fileExtension)) {
+ toast.error(
+ 'Format file yang diijinkan adalah .pdf, .png, .jpg, atau .jpeg',
+ { duration: 4000 }
+ );
+
+ event.target.value = '';
+ return;
+ }
+ if (file.size > 2000000) {
+ toast.error('Maksimal ukuran file adalah 2MB', { duration: 4000 });
+
+ event.target.value = '';
+ return;
+ }
+
+ fileBase64 = await getFileBase64(file);
+ updateFormDokumen(name, file.name, fileExtension, fileBase64);
+ validateDokumen();
+ }
+ };
+
+ const isFormValid = useMemo(
+ () => Object.keys(errorsDokumen).length === 0,
+ [errorsDokumen]
+ );
+ const dokumenNibRef = useRef(null);
+ const dokumenNpwpRef = useRef(null);
+ const dokumenSppkpRef = useRef(null);
+ const dokumenAktaPerubahanRef = useRef(null);
+ const dokumenKtpDirutRef = useRef(null);
+ const dokumenAktaPendirianRef = useRef(null);
+ const dokumenLaporanKeuanganRef = useRef(null);
+ const dokumenFotoKantorRef = useRef(null);
+ const dokumenTempatBekerjaRef = useRef(null);
+
+ useEffect(() => {
+ const loadIndustries = async () => {
+ if (!isFormValid) {
+ const options = {
+ behavior: 'smooth',
+ block: 'center',
+ };
+ if (errorsDokumen.dokumenNib && dokumenNibRef.current) {
+ dokumenNibRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsDokumen.dokumenNpwp && dokumenNpwpRef.current) {
+ dokumenNpwpRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsDokumen.dokumenSppkp && dokumenSppkpRef.current) {
+ dokumenSppkpRef.current.scrollIntoView(options);
+ return;
+ }
+ if (
+ errorsDokumen.dokumenAktaPerubahan &&
+ dokumenAktaPerubahanRef.current
+ ) {
+ dokumenAktaPerubahanRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsDokumen.dokumenKtpDirut && dokumenKtpDirutRef.current) {
+ dokumenKtpDirutRef.current.scrollIntoView(options);
+ return;
+ }
+ if (
+ errorsDokumen.dokumenAktaPendirian &&
+ dokumenAktaPendirianRef.current
+ ) {
+ dokumenAktaPendirianRef.current.scrollIntoView(options);
+ return;
+ }
+ if (
+ errorsDokumen.dokumenLaporanKeuangan &&
+ dokumenLaporanKeuanganRef.current
+ ) {
+ dokumenLaporanKeuanganRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsDokumen.dokumenFotoKantor && dokumenFotoKantorRef.current) {
+ dokumenFotoKantorRef.current.scrollIntoView(options);
+ return;
+ }
+ if (
+ errorsDokumen.dokumenTempatBekerja &&
+ dokumenTempatBekerjaRef.current
+ ) {
+ dokumenTempatBekerjaRef.current.scrollIntoView(options);
+ return;
+ }
+ }
+ };
+ loadIndustries();
+ }, [buttonSubmitClick, chekValid]);
+
+ useEffect(() => {
+ validateDokumen();
+ }, [buttonSubmitClick]);
+
+ useEffect(() => {
+ const loadIndustries = async () => {
+ const dataIndustries = await odooApi('GET', '/api/v1/partner/industry');
+ setIndustries(
+ dataIndustries?.map((o) => ({
+ value: o.id,
+ label: o.name,
+ category: o.category,
+ }))
+ );
+ };
+ loadIndustries();
+ }, []);
+
+ useEffect(() => {
+ const selectedIndustryType = industries.find(
+ (industry) => industry.value === watch('industry_id')
+ );
+ if (selectedIndustryType) {
+ updateForm('industry_id', `${selectedIndustryType?.value}`);
+ validate();
+ }
+ }, [watch('industry_id'), industries]);
+
+ useEffect(() => {
+ if (form.industry_id) {
+ setValue('industry_id', parseInt(form.industry_id));
+ }
+ }, [form]);
+
+ 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} />
+ </div>
+ <div className='h-px bg-gray-300'></div>
+ <div className=''>
+ <KontakPerusahaan isKonfirmasi={true} />
+ </div>
+ </div>
+
+ <div className='w-px bg-gray-300'></div>
+ <div className='w-full grid grid-rows-[1fc_auto_1fc] gap-5'>
+ <div>
+ <Pengiriman isKonfirmasi={true} />
+ </div>
+ <div className='h-px bg-gray-300'></div>
+ <div>
+ <KonfirmasiDokumen isKonfirmasi={true} />
+ </div>
+ </div>
+ </div>
+ </form>
+ )}
+ {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'>Kontak Person</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 && <KontakPerusahaan 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'>Pengiriman</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 && <Pengiriman 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 && <KonfirmasiDokumen isKonfirmasi={true} />}
+ </div>
+ </form>
+ )}
+ </>
+ );
+};
+
+export default Konfirmasi;
diff --git a/src/lib/pengajuan-tempo/component/KonfirmasiDokumen.jsx b/src/lib/pengajuan-tempo/component/KonfirmasiDokumen.jsx
new file mode 100644
index 00000000..66d33b43
--- /dev/null
+++ b/src/lib/pengajuan-tempo/component/KonfirmasiDokumen.jsx
@@ -0,0 +1,1677 @@
+import React, { useState, useEffect, useMemo, useRef } from 'react';
+import { Controller, set, useForm } from 'react-hook-form';
+import { usePengajuanTempoStoreDokumen } from '../../../../src-migrate/modules/register/stores/usePengajuanTempoStore';
+import ProgressBar from '@ramonak/react-progress-bar';
+import { UseToastOptions } from '@chakra-ui/react';
+import { toast } from 'react-hot-toast';
+import getFileBase64 from '@/core/utils/getFileBase64';
+import Image from 'next/image';
+import BottomPopup from '@/core/components/elements/Popup/BottomPopup';
+import { EyeIcon } from '@heroicons/react/24/outline';
+import useDevice from '@/core/hooks/useDevice';
+import imageCompression from 'browser-image-compression';
+const KonfirmasiDokumen = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
+ const { control, watch } = useForm();
+ const [isExample, setIsExample] = useState(false);
+ const { isDesktop, isMobile } = useDevice();
+ const [base64, setBase64] = useState();
+ const [pdfData, setPdfData] = useState();
+ const [format, setFormat] = useState();
+ const [url, setUrl] = useState();
+ const [popUpSource, setSource] = useState();
+ const {
+ formDokumen,
+ errorsDokumen,
+ validateDokumen,
+ updateFormDokumen,
+ getJumlahDokumenDiisi,
+ } = usePengajuanTempoStoreDokumen();
+
+ const handleConfirmSubmit = (format, base64) => {
+ if (format == 'pdf') {
+ setFormat(`application/${format}`);
+ } else if (format == 'png' || format == 'jpg' || format == 'jpeg') {
+ setFormat(`image/${format}`);
+ } else {
+ setFormat(format);
+ }
+ setBase64(
+ base64.trim().replace(/^"+/, '').replace(/"+$/, '').replaceAll('\\', '')
+ );
+ setIsExample(!isExample);
+ };
+ const handleInputChange = 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 > 500000) {
+ 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 });
+ }
+ } else {
+ // Convert file to Base64
+ fileBase64 = await getFileBase64(file);
+ updateFormDokumen(name, file.name, fileExtension, fileBase64);
+ }
+ validateDokumen();
+ }
+ };
+
+ const isFormValid = useMemo(
+ () => Object.keys(errorsDokumen).length === 0,
+ [errorsDokumen]
+ );
+ const dokumenNibRef = useRef(null);
+ const dokumenNpwpRef = useRef(null);
+ const dokumenSppkpRef = useRef(null);
+ const dokumenAktaPerubahanRef = useRef(null);
+ const dokumenKtpDirutRef = useRef(null);
+ const dokumenAktaPendirianRef = useRef(null);
+ const dokumenLaporanKeuanganRef = useRef(null);
+ const dokumenFotoKantorRef = useRef(null);
+ const dokumenTempatBekerjaRef = useRef(null);
+
+ useEffect(() => {
+ const loadIndustries = async () => {
+ if (!isFormValid) {
+ const options = {
+ behavior: 'smooth',
+ block: 'center',
+ };
+ if (errorsDokumen.dokumenNib && dokumenNibRef.current) {
+ dokumenNibRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsDokumen.dokumenNpwp && dokumenNpwpRef.current) {
+ dokumenNpwpRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsDokumen.dokumenSppkp && dokumenSppkpRef.current) {
+ dokumenSppkpRef.current.scrollIntoView(options);
+ return;
+ }
+ if (
+ errorsDokumen.dokumenAktaPerubahan &&
+ dokumenAktaPerubahanRef.current
+ ) {
+ dokumenAktaPerubahanRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsDokumen.dokumenKtpDirut && dokumenKtpDirutRef.current) {
+ dokumenKtpDirutRef.current.scrollIntoView(options);
+ return;
+ }
+ if (
+ errorsDokumen.dokumenAktaPendirian &&
+ dokumenAktaPendirianRef.current
+ ) {
+ dokumenAktaPendirianRef.current.scrollIntoView(options);
+ return;
+ }
+ if (
+ errorsDokumen.dokumenLaporanKeuangan &&
+ dokumenLaporanKeuanganRef.current
+ ) {
+ dokumenLaporanKeuanganRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsDokumen.dokumenFotoKantor && dokumenFotoKantorRef.current) {
+ dokumenFotoKantorRef.current.scrollIntoView(options);
+ return;
+ }
+ if (
+ errorsDokumen.dokumenTempatBekerja &&
+ dokumenTempatBekerjaRef.current
+ ) {
+ dokumenTempatBekerjaRef.current.scrollIntoView(options);
+ return;
+ }
+ }
+ };
+ loadIndustries();
+ }, [buttonSubmitClick, chekValid]);
+
+ useEffect(() => {
+ validateDokumen();
+ }, [buttonSubmitClick]);
+ return (
+ <>
+ {isDesktop && (
+ <div>
+ <h1 className={`font-bold ${isKonfirmasi ? 'text-xl' : ''}`}>
+ Dokumen
+ </h1>
+ <form className='flex flex-col w-full gap-5 '>
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5 flex'>
+ <label className='form-label text-nowrap'>
+ NPWP Perusahaan
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-3/5 text-gray-600 truncate max-w-48'>
+ {formDokumen?.dokumenNpwp?.name}
+ </span>
+ <div className='w-2/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenNpwp'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen?.dokumenNpwp?.name ? 'Ubah' : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenNpwp'
+ name='dokumenNpwp'
+ type='file'
+ title=' '
+ ref={dokumenNpwpRef}
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenNpwp}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenNpwp.format,
+ formDokumen.dokumenNpwp.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenNpwp}
+ </div>
+ )}
+ </div>
+ </div>
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>SPPKP</label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-3/5 text-gray-600 truncate max-w-48'>
+ {formDokumen?.dokumenSppkp?.name}
+ </span>
+ <div className='w-2/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenSppkp'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen?.dokumenSppkp?.name.length > 0
+ ? 'Ubah'
+ : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenSppkp'
+ name='dokumenSppkp'
+ type='file'
+ title=' '
+ ref={dokumenSppkpRef}
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenSppkp}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenSppkp.format,
+ formDokumen.dokumenSppkp.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenSppkp}
+ </div>
+ )}
+ </div>
+ </div>
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>NIB</label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-3/5 text-gray-600 truncate max-w-48'>
+ {formDokumen?.dokumenNib?.name}
+ </span>
+ <div className='w-2/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenNib'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen?.dokumenNib?.name ? 'Ubah' : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenNib'
+ name='dokumenNib'
+ type='file'
+ title=' '
+ ref={dokumenNibRef}
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenNib}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenNib.format,
+ formDokumen.dokumenNib.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenNib}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ SIUP <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-3/5 text-gray-600 truncate max-w-48'>
+ {formDokumen?.dokumenSiup?.name}
+ </span>
+ <div className='w-2/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenSiup'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen.dokumenSiup.name ? 'Ubah' : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenSiup'
+ name='dokumenSiup'
+ type='file'
+ title=' '
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenSiup}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenSiup.format,
+ formDokumen.dokumenSiup.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenSiup}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ TDP <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-3/5 text-gray-600 truncate max-w-48'>
+ {formDokumen?.dokumenTdp?.name}
+ </span>
+ <div className='w-2/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenTdp'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen.dokumenTdp.name ? 'Ubah' : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenTdp'
+ name='dokumenTdp'
+ type='file'
+ title=' '
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenTdp}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenTdp.format,
+ formDokumen.dokumenTdp.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenTdp}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ SKDP <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-3/5 text-gray-600 truncate max-w-48'>
+ {formDokumen?.dokumenSkdp?.name}
+ </span>
+ <div className='w-2/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenSkdp'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen.dokumenSkdp.name ? 'Ubah' : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenSkdp'
+ name='dokumenSkdp'
+ type='file'
+ title=' '
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenSkdp}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenSkdp.format,
+ formDokumen.dokumenSkdp.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenSkdp}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ SKT <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-3/5 text-gray-600 truncate max-w-48'>
+ {formDokumen?.dokumenSkt?.name}
+ </span>
+ <div className='w-2/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenSkt'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen.dokumenSkt.name ? 'Ubah' : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenSkt'
+ name='dokumenSkt'
+ type='file'
+ title=' '
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenSkt}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenSkt.format,
+ formDokumen.dokumenSkt.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenSkt}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ Akta Pendirian <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-3/5 text-gray-600 truncate max-w-48'>
+ {formDokumen?.dokumenAktaPendirian?.name}
+ </span>
+ <div className='w-2/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenAktaPendirian'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen?.dokumenAktaPendirian?.name
+ ? 'Ubah'
+ : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenAktaPendirian'
+ name='dokumenAktaPendirian'
+ type='file'
+ title=' '
+ ref={dokumenAktaPendirianRef}
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenAktaPendirian}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenAktaPendirian.format,
+ formDokumen.dokumenAktaPendirian.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenAktaPendirian}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ {' '}
+ Akta Perubahan <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-3/5 text-gray-600 truncate max-w-48'>
+ {formDokumen?.dokumenAktaPerubahan?.name}
+ </span>
+ <div className='w-2/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenAktaPerubahan'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen.dokumenAktaPerubahan.name
+ ? 'Ubah'
+ : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenAktaPerubahan'
+ name='dokumenAktaPerubahan'
+ type='file'
+ title=' '
+ ref={dokumenAktaPerubahanRef}
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenAktaPerubahan}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenAktaPerubahan.format,
+ formDokumen.dokumenAktaPerubahan.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenAktaPerubahan}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ Foto tempat kerja
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-3/5 text-gray-600 truncate max-w-48'>
+ {formDokumen?.dokumenTempatBekerja?.name}
+ </span>
+ <div className='w-2/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenTempatBekerja'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen?.dokumenTempatBekerja?.name
+ ? 'Ubah'
+ : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenTempatBekerja'
+ name='dokumenTempatBekerja'
+ type='file'
+ title=' '
+ ref={dokumenTempatBekerjaRef}
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenTempatBekerja}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenTempatBekerja.format,
+ formDokumen.dokumenTempatBekerja.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenTempatBekerja}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ Foto Kantor (Tampak Depan)
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-3/5 text-gray-600 truncate max-w-48'>
+ {formDokumen?.dokumenFotoKantor?.name}
+ </span>
+ <div className='w-2/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenFotoKantor'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen?.dokumenFotoKantor?.name ? 'Ubah' : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenFotoKantor'
+ name='dokumenFotoKantor'
+ type='file'
+ title=' '
+ ref={dokumenFotoKantorRef}
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenFotoKantor}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenFotoKantor.format,
+ formDokumen.dokumenFotoKantor.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenFotoKantor}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ KTP Dirut/Direktur{' '}
+ <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-3/5 text-gray-600 truncate max-w-48'>
+ {formDokumen?.dokumenKtpDirut?.name}
+ </span>
+ <div className='w-2/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenKtpDirut'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen?.dokumenKtpDirut?.name ? 'Ubah' : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenKtpDirut'
+ name='dokumenKtpDirut'
+ type='file'
+ title=' '
+ ref={dokumenKtpDirutRef}
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenKtpDirut}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenKtpDirut.format,
+ formDokumen.dokumenKtpDirut.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenKtpDirut}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ Laporan Keuangan
+ <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-3/5 text-gray-600 truncate max-w-48'>
+ {formDokumen?.dokumenLaporanKeuangan?.name}
+ </span>
+ <div className='w-2/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenLaporanKeuangan'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen?.dokumenLaporanKeuangan?.name
+ ? 'Ubah'
+ : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenLaporanKeuangan'
+ name='dokumenLaporanKeuangan'
+ type='file'
+ title=' '
+ ref={dokumenLaporanKeuanganRef}
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenLaporanKeuangan}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenLaporanKeuangan.format,
+ formDokumen.dokumenLaporanKeuangan.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenLaporanKeuangan}
+ </div>
+ )}
+ </div>
+ </div>
+ </form>
+ </div>
+ )}
+ {isMobile && (
+ <div className=''>
+ <h1 className={`font-bold ${isKonfirmasi ? 'hidden' : 'text-xl'}`}>
+ Dokumen
+ </h1>
+ <form className='flex flex-col w-full gap-5 text-sm'>
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-wrap'>NPWP Perusahaan</label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-2/5 text-gray-600 truncate '>
+ {formDokumen?.dokumenNpwp?.name}
+ </span>
+ <div className='w-3/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenNpwp'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen?.dokumenNpwp?.name ? 'Sudah' : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenNpwp'
+ name='dokumenNpwp'
+ type='file'
+ title=' '
+ ref={dokumenNpwpRef}
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenNpwp}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenNpwp.format,
+ formDokumen.dokumenNpwp.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenNpwp}
+ </div>
+ )}
+ </div>
+ </div>
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-wrap'>SPPKP</label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-2/5 text-gray-600 truncate '>
+ {formDokumen?.dokumenSppkp?.name}
+ </span>
+ <div className='w-3/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenSppkp'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen?.dokumenSppkp?.name ? 'Ubah' : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenSppkp'
+ name='dokumenSppkp'
+ type='file'
+ title=' '
+ ref={dokumenSppkpRef}
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenSppkp}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenSppkp.format,
+ formDokumen.dokumenSppkp.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenSppkp}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-wrap'>NIB</label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-center justify-start'>
+ <span className='w-2/5 text-gray-600 truncate '>
+ {formDokumen?.dokumenNib?.name}
+ </span>
+ <div className='w-3/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenNib'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen?.dokumenNib?.name ? 'Ubah' : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenNib'
+ name='dokumenNib'
+ type='file'
+ title=' '
+ ref={dokumenNibRef}
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenNib}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenNib.format,
+ formDokumen.dokumenNib.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenNib}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-wrap'>
+ SIUP <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-2/5 text-gray-600 truncate '>
+ {formDokumen?.dokumenSiup?.name}
+ </span>
+ <div className='w-3/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenSiup'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen?.dokumenSiup?.name ? 'Ubah' : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenSiup'
+ name='dokumenSiup'
+ type='file'
+ title=' '
+ ref={dokumenAktaPerubahanRef}
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenSiup}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenSiup.format,
+ formDokumen.dokumenSiup.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenSiup}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-wrap'>
+ TDP <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-2/5 text-gray-600 truncate '>
+ {formDokumen?.dokumenTdp?.name}
+ </span>
+ <div className='w-3/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenTdp'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen?.dokumenTdp?.name ? 'Ubah' : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenTdp'
+ name='dokumenTdp'
+ type='file'
+ title=' '
+ ref={dokumenAktaPerubahanRef}
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenTdp}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenTdp.format,
+ formDokumen.dokumenTdp.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenTdp}
+ </div>
+ )}
+ </div>
+ </div>
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-wrap'>
+ SKDP <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-2/5 text-gray-600 truncate '>
+ {formDokumen?.dokumenSkdp?.name}
+ </span>
+ <div className='w-3/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenSkdp'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen?.dokumenSkdp?.name ? 'Ubah' : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenSkdp'
+ name='dokumenSkdp'
+ type='file'
+ title=' '
+ ref={dokumenAktaPerubahanRef}
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenSkdp}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenSkdp.format,
+ formDokumen.dokumenSkdp.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenSkdp}
+ </div>
+ )}
+ </div>
+ </div>
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-wrap'>
+ SKT <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-2/5 text-gray-600 truncate '>
+ {formDokumen?.dokumenSkt?.name}
+ </span>
+ <div className='w-3/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenSkt'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen?.dokumenSkt?.name ? 'Ubah' : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenSkt'
+ name='dokumenSkt'
+ type='file'
+ title=' '
+ ref={dokumenAktaPerubahanRef}
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenSkt}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenSkt.format,
+ formDokumen.dokumenSkt.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenSkt}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-wrap'>
+ {' '}
+ Akta Perubahan <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-2/5 text-gray-600 truncate '>
+ {formDokumen?.dokumenAktaPerubahan?.name}
+ </span>
+ <div className='w-3/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenAktaPerubahan'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen?.dokumenAktaPerubahan?.name
+ ? 'Ubah'
+ : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenAktaPerubahan'
+ name='dokumenAktaPerubahan'
+ type='file'
+ title=' '
+ ref={dokumenAktaPerubahanRef}
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenAktaPerubahan}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenAktaPerubahan.format,
+ formDokumen.dokumenAktaPerubahan.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenAktaPerubahan}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-wrap'>
+ KTP Dirut/Direktur{' '}
+ <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-2/5 text-gray-600 truncate '>
+ {formDokumen?.dokumenKtpDirut?.name}
+ </span>
+ <div className='w-3/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenKtpDirut'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen?.dokumenKtpDirut?.name ? 'Ubah' : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenKtpDirut'
+ name='dokumenKtpDirut'
+ type='file'
+ title=' '
+ ref={dokumenKtpDirutRef}
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenKtpDirut}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenKtpDirut.format,
+ formDokumen.dokumenKtpDirut.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenKtpDirut}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-wrap'>
+ Akta Pendirian <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-2/5 text-gray-600 truncate '>
+ {formDokumen?.dokumenAktaPendirian?.name}
+ </span>
+ <div className='w-3/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenAktaPendirian'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen?.dokumenAktaPendirian?.name
+ ? 'Ubah'
+ : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenAktaPendirian'
+ name='dokumenAktaPendirian'
+ type='file'
+ title=' '
+ ref={dokumenAktaPendirianRef}
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenAktaPendirian}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenAktaPendirian.format,
+ formDokumen.dokumenAktaPendirian.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenAktaPendirian}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-wrap'>
+ Laporan Keuangan
+ <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-2/5 text-gray-600 truncate '>
+ {formDokumen?.dokumenLaporanKeuangan?.name}
+ </span>
+ <div className='w-3/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenLaporanKeuangan'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen?.dokumenLaporanKeuangan?.name
+ ? 'Ubah'
+ : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenLaporanKeuangan'
+ name='dokumenLaporanKeuangan'
+ type='file'
+ title=' '
+ ref={dokumenLaporanKeuanganRef}
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenLaporanKeuangan}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenLaporanKeuangan.format,
+ formDokumen.dokumenLaporanKeuangan.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenLaporanKeuangan}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-wrap'>
+ Foto Kantor (Tampak Depan)
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-2/5 text-gray-600 truncate '>
+ {formDokumen?.dokumenFotoKantor?.name}
+ </span>
+ <div className='w-3/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenFotoKantor'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20 text-center'
+ >
+ {formDokumen?.dokumenFotoKantor?.name ? 'Ubah' : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenFotoKantor'
+ name='dokumenFotoKantor'
+ type='file'
+ title=' '
+ ref={dokumenFotoKantorRef}
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenFotoKantor}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenFotoKantor.format,
+ formDokumen.dokumenFotoKantor.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenFotoKantor}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='w-full flex flex-row items-center '>
+ <div className='w-2/5'>
+ <label className='form-label text-wrap'>
+ Foto tempat kerja
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex flex-row items-start justify-between'>
+ <span className='w-2/5 text-gray-600 truncate'>
+ {formDokumen?.dokumenTempatBekerja?.name}
+ </span>
+ <div className='w-3/5 flex flex-row gap-2'>
+ <label
+ htmlFor='dokumenTempatBekerja'
+ className='cursor-pointer bg-red-500 hover:bg-red-600 text-white py-1 px-3 text-sm rounded min-w-20'
+ >
+ {formDokumen?.dokumenTempatBekerja?.name
+ ? 'Ubah'
+ : 'Upload'}
+ </label>
+ <input
+ // value={formDokumen?.dokumenNib?.name}
+ id='dokumenTempatBekerja'
+ name='dokumenTempatBekerja'
+ type='file'
+ title=' '
+ ref={dokumenTempatBekerjaRef}
+ className='hidden'
+ aria-invalid={errorsDokumen.dokumenTempatBekerja}
+ onChange={handleInputChange}
+ accept='.pdf,.png,.jpg,.jpeg'
+ />
+ <button
+ className='rounded text-white p-2 flex flex-row bg-red-500 hover:cursor-pointer hover:bg-red-400'
+ type='button'
+ onClick={() =>
+ handleConfirmSubmit(
+ formDokumen.dokumenTempatBekerja.format,
+ formDokumen.dokumenTempatBekerja.base64
+ )
+ }
+ >
+ <EyeIcon className={`w-4 ${isDesktop && ''}`} />
+ </button>
+ </div>
+ {/* <Image
+ src={`data:image/png;base64, ${formDokumen.dokumenNib.base64}`}
+ alt='Contoh SPPKP'
+ className='w-full h-full '
+ width={800}
+ height={800}
+ quality={85}
+ /> */}
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsDokumen.dokumenTempatBekerja}
+ </div>
+ )}
+ </div>
+ </div>
+ </form>
+ </div>
+ )}
+ {isDesktop && (
+ <BottomPopup
+ className='w-full h-full'
+ title='Dokumen'
+ active={isExample}
+ close={() => setIsExample(false)}
+ >
+ <div className='flex justify-center items-center p-2'>
+ <iframe
+ // src={`${
+ // format == null ? url : `data:${format};base64,${base64}`
+ // }`}
+ // src={`http://192.168.23.244:8069` + `${format}`}
+ src={`data:${format};base64,${base64}`}
+ width='800px'
+ height='600px'
+ title='Document'
+ ></iframe>
+ {/* <embed
+ src={`data:${format};base64,${base64}`}
+ width='100%'
+ height='600px'
+ /> */}
+ </div>
+ </BottomPopup>
+ )}
+ {isMobile && (
+ <BottomPopup
+ className='!container'
+ title='Dokumen'
+ active={isExample}
+ close={() => setIsExample(false)}
+ >
+ <div className='flex justify-center items-center p-2'>
+ {/* <iframe
+ // src={`${
+ // format == null ? url : `data:${format};base64,${base64}`
+ // }`}
+ src={`http://192.168.23.244:8069` + `${format}`}
+ // src={format}
+ width='800px'
+ height='600px'
+ title='Document'
+ ></iframe> */}
+ <embed
+ src={`data:${format};base64,${base64}`}
+ width='100%'
+ height='600px'
+ />
+ </div>
+ </BottomPopup>
+ )}
+ </>
+ );
+};
+
+export default KonfirmasiDokumen;
+
+{
+ /* <iframe
+ src={`data:application/octet-stream;base64, ${base64}`}
+ // src={`data:application/${format};base64, ${base64}`}
+ width='800px'
+ height='600px'
+ title='NPWP File'
+ frameBorder='0'
+ sandbox
+ ></iframe> */
+}
+{
+ /* <Image
+ src={`data:image/${format};base64, ${base64}`}
+ alt='Contoh SPPKP'
+ className='w-full h-full '
+ width={1200}
+ height={1200}
+ quality={85}
+ /> */
+}
+
+// <div className='flex justify-center items-center p-2'>
+// {!format.includes('undifined') ? (
+// <object
+// data={`data:${format};base64,${base64}`}
+// type={format}
+// width='100%'
+// height='100%'
+// onError={(e) => {
+// // Jika <object> gagal menampilkan PDF, unduh otomatis
+// const link = document.createElement('a');
+// link.href = `data:${format};base64,${base64}`;
+// link.download = 'document.pdf';
+// link.click();
+// }}
+// >
+// PDF tidak dapat ditampilkan.
+// <a
+// href={`data:${format};base64,${base64}`}
+// download='document.pdf'
+// >
+// {' '}
+// Klik <span className='text-red-500'>di sini</span> untuk
+// mengunduh PDF
+// </a>
+// </object>
+// ) : (
+// <embed
+// src={
+// '/web/image/2540181?unique=7050288dd34d7318bf059e7f362540f6ad5533f9'
+// }
+// width='100%'
+// height='600px'
+// />
+// )}
+// {/* <embed
+// src={
+// '/web/image/2540181?unique=7050288dd34d7318bf059e7f362540f6ad5533f9'
+// }
+// width='100%'
+// height='600px'
+// /> */}
+// </div>
diff --git a/src/lib/pengajuan-tempo/component/KontakPerusahaan.jsx b/src/lib/pengajuan-tempo/component/KontakPerusahaan.jsx
new file mode 100644
index 00000000..b8ca3202
--- /dev/null
+++ b/src/lib/pengajuan-tempo/component/KontakPerusahaan.jsx
@@ -0,0 +1,684 @@
+import React, { useEffect, useMemo, useRef } from 'react';
+import { usePengajuanTempoStoreKontakPerson } from '../../../../src-migrate/modules/register/stores/usePengajuanTempoStore';
+import useDevice from '@/core/hooks/useDevice';
+const KontakPerusahaan = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
+ const {
+ formKontakPerson,
+ errorsKontakPerson,
+ validateKontakPerson,
+ updateFormKontakPerson,
+ } = usePengajuanTempoStoreKontakPerson();
+ const { isDesktop, isMobile } = useDevice();
+ const handleInputChange = (event) => {
+ const { name, value } = event.target;
+ updateFormKontakPerson(name, value);
+ validateKontakPerson();
+ };
+
+ const isFormValid = useMemo(
+ () => Object.keys(errorsKontakPerson).length === 0,
+ [errorsKontakPerson]
+ );
+
+ const direkturNameRef = useRef(null);
+ const direkturMobileRef = useRef(null);
+ const direkturEmailRef = useRef(null);
+ const purchasingNameRef = useRef(null);
+ const purchasingEmailRef = useRef(null);
+ const purchasingMobileRef = useRef(null);
+ const financeNameRef = useRef(null);
+ const financeMobileRef = useRef(null);
+ const financeEmailRef = useRef(null);
+
+ useEffect(() => {
+ const loadIndustries = async () => {
+ if (!isFormValid) {
+ const options = {
+ behavior: 'smooth',
+ block: 'center',
+ };
+ if (errorsKontakPerson.direkturName && direkturNameRef.current) {
+ direkturNameRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsKontakPerson.direkturMobile && direkturMobileRef.current) {
+ direkturMobileRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsKontakPerson.direkturEmail && direkturEmailRef.current) {
+ direkturEmailRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsKontakPerson.purchasingName && purchasingNameRef.current) {
+ purchasingNameRef.current.scrollIntoView(options);
+ return;
+ }
+ if (
+ errorsKontakPerson.purchasingMobile &&
+ purchasingMobileRef.current
+ ) {
+ purchasingMobileRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsKontakPerson.purchasingEmail && purchasingEmailRef.current) {
+ purchasingEmailRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsKontakPerson.financeName && financeNameRef.current) {
+ financeNameRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsKontakPerson.financeMobile && financeMobileRef.current) {
+ financeMobileRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsKontakPerson.financeEmail && financeEmailRef.current) {
+ financeEmailRef.current.scrollIntoView(options);
+ return;
+ }
+ }
+ };
+ loadIndustries();
+ }, [buttonSubmitClick, chekValid]);
+
+ useEffect(() => {
+ validateKontakPerson();
+ }, [buttonSubmitClick]);
+ return (
+ <>
+ {isDesktop && (
+ <div className=''>
+ <h1 className={`font-bold ${isKonfirmasi ? 'text-xl' : ''}`}>
+ Kontak Person
+ </h1>
+ <form className='flex flex-col w-full '>
+ <div className='w-full grid grid-row-2 gap-5'>
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ Nama Lengkap Direktur
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex items-center border border-gray-300 rounded-md'>
+ {/* Dropdown untuk memilih "Bpk" atau "Ibu" */}
+ <select
+ value={formKontakPerson.direkturTittle || ''}
+ id='direkturTittle'
+ name='direkturTittle'
+ className=' p-2 rounded-l-md'
+ onChange={handleInputChange}
+ >
+ <option value='' disabled>
+ Pilih
+ </option>
+ <option value='Bpk'>Bpk</option>
+ <option value='Ibu'>Ibu</option>
+ </select>
+
+ {/* Input untuk nama */}
+ <input
+ value={formKontakPerson.direkturName}
+ id='direkturName'
+ name='direkturName'
+ placeholder='Masukkan nama direktur anda'
+ type='text'
+ className='form-input border-l border-r-0 border-t-0 border-b-0 border-gray-300 p-2 ml-2 flex-grow rounded-none'
+ aria-invalid={errorsKontakPerson.direkturName}
+ ref={direkturNameRef}
+ onChange={handleInputChange}
+ />
+ </div>
+ {chekValid && (
+ <>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsKontakPerson.direkturName}{' '}
+ {errorsKontakPerson.direkturTittle}
+ </div>
+ </>
+ )}
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-wrap'>
+ No. HP Direktur{' '}
+ <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <input
+ id='direkturMobile'
+ name='direkturMobile'
+ ref={direkturMobileRef}
+ placeholder='Masukkan nomor direktur anda'
+ type='tel'
+ value={formKontakPerson.direkturMobile}
+ className='form-input'
+ aria-invalid={errorsKontakPerson.direkturMobile}
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsKontakPerson.direkturMobile}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ Email Direktur
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <input
+ id='direkturEmail'
+ name='direkturEmail'
+ ref={direkturEmailRef}
+ placeholder='contoh@email.com'
+ type='email'
+ value={formKontakPerson.direkturEmail}
+ className='form-input'
+ aria-invalid={errorsKontakPerson.direkturEmail}
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsKontakPerson.direkturEmail}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ Nama Purchasing
+ </label>
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ isi nama purchasing yang bertanggung jawab di perusahaan
+ anda
+ </span>
+ )}
+ </div>
+ <div className='w-3/5'>
+ <div className='flex items-center border border-gray-300 rounded-md'>
+ <select
+ value={formKontakPerson.purchasingTittle || ''}
+ id='purchasingTittle'
+ name='purchasingTittle'
+ className=' p-2 rounded-l-md'
+ onChange={handleInputChange}
+ >
+ <option value='' disabled>
+ Pilih
+ </option>
+ <option value='Bpk'>Bpk</option>
+ <option value='Ibu'>Ibu</option>
+ </select>
+ <input
+ id='purchasingName'
+ name='purchasingName'
+ ref={purchasingNameRef}
+ placeholder='Masukkan nama purchasing anda'
+ value={formKontakPerson.purchasingName}
+ type='text'
+ className='form-input border-l border-r-0 border-t-0 border-b-0 border-gray-300 p-2 ml-2 flex-grow rounded-none'
+ aria-invalid={errorsKontakPerson.purchasingName}
+ onChange={handleInputChange}
+ />
+ </div>
+ {chekValid && (
+ <>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsKontakPerson.purchasingName}{' '}
+ {errorsKontakPerson.purchasingTittle}
+ </div>
+ </>
+ )}
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ No. HP Purchasing
+ </label>
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ isi nomor purchasing yang bertanggung jawab diperusahaan
+ anda
+ </span>
+ )}
+ </div>
+ <div className='w-3/5'>
+ <input
+ id='purchasingMobile'
+ name='purchasingMobile'
+ ref={financeMobileRef}
+ placeholder='Masukkan nomor purchasing anda'
+ value={formKontakPerson.purchasingMobile}
+ type='tel'
+ className='form-input'
+ aria-invalid={errorsKontakPerson.purchasingMobile}
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsKontakPerson.purchasingMobile}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ Email Purchasing
+ </label>
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ isi email purchasing yang bertanggung jawab perusahaan
+ anda
+ </span>
+ )}
+ </div>
+ <div className='w-3/5'>
+ <input
+ id='purchasingEmail'
+ name='purchasingEmail'
+ ref={purchasingEmailRef}
+ placeholder='contoh@email.com'
+ value={formKontakPerson.purchasingEmail}
+ type='email'
+ className='form-input'
+ aria-invalid={errorsKontakPerson.purchasingEmail}
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsKontakPerson.purchasingEmail}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ Nama Finance
+ </label>
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ isi nama finance yang bertanggung jawab di perusahaan anda
+ </span>
+ )}
+ </div>
+ <div className='w-3/5'>
+ <div className='flex items-center border border-gray-300 rounded-md'>
+ <select
+ value={formKontakPerson.financeTittle || ''}
+ id='financeTittle'
+ name='financeTittle'
+ className=' p-2 rounded-l-md'
+ onChange={handleInputChange}
+ >
+ <option value='' disabled>
+ Pilih
+ </option>
+ <option value='Bpk'>Bpk</option>
+ <option value='Ibu'>Ibu</option>
+ </select>
+ <input
+ id='financeName'
+ name='financeName'
+ ref={financeNameRef}
+ placeholder='Masukkan nama finance'
+ value={formKontakPerson.financeName}
+ type='text'
+ className='form-input border-l border-r-0 border-t-0 border-b-0 border-gray-300 p-2 ml-2 flex-grow rounded-none'
+ aria-invalid={errorsKontakPerson.financeName}
+ onChange={handleInputChange}
+ />
+ </div>
+ {chekValid && (
+ <>
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsKontakPerson.financeName}{' '}
+ {errorsKontakPerson.financeTittle}
+ </div>
+ </>
+ )}
+ </div>
+ </div>
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ No. HP Finance
+ </label>
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ isi nomor finance yang bertanggung jawab di perusahaan
+ anda
+ </span>
+ )}
+ </div>
+ <div className='w-3/5'>
+ <input
+ id='financeMobile'
+ name='financeMobile'
+ ref={financeMobileRef}
+ placeholder='Masukkan nomor finance'
+ value={formKontakPerson.financeMobile}
+ type='tel'
+ className='form-input'
+ aria-invalid={errorsKontakPerson.financeMobile}
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsKontakPerson.financeMobile}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ Email Finance
+ </label>
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ isi email finance yang bertanggung jawab diperusahaan anda
+ </span>
+ )}
+ </div>
+ <div className='w-3/5'>
+ <input
+ id='financeEmail'
+ name='financeEmail'
+ ref={financeEmailRef}
+ placeholder='contoh@email.com'
+ type='email'
+ value={formKontakPerson.financeEmail}
+ className='form-input'
+ aria-invalid={errorsKontakPerson.financeEmail}
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsKontakPerson.financeEmail}
+ </div>
+ )}
+ </div>
+ </div>
+ </div>
+ </form>
+ </div>
+ )}
+ {isMobile && (
+ <div className='text-sm'>
+ <h1
+ className={`font-bold py-4 mt-8 ${
+ isKonfirmasi ? 'hidden' : 'text-xl'
+ }`}
+ >
+ Kontak Person
+ </h1>
+ <form className='flex flex-col w-full '>
+ <div className='w-full grid grid-row-2 gap-4'>
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label text-nowrap'>
+ Nama Lengkap Direktur
+ </label>
+ <div className='flex items-center border border-gray-300 rounded-md w-full'>
+ <select
+ value={formKontakPerson.direkturTittle || ''}
+ id='direkturTittle'
+ name='direkturTittle'
+ className=' p-2 rounded-l-md'
+ onChange={handleInputChange}
+ >
+ <option value='' disabled>
+ Pilih
+ </option>
+ <option value='Bpk'>Bpk</option>
+ <option value='Ibu'>Ibu</option>
+ </select>
+ <input
+ value={formKontakPerson.direkturName}
+ id='direkturName'
+ name='direkturName'
+ placeholder='Masukkan nama direktur anda'
+ type='text'
+ className='form-input border-l border-r-0 border-t-0 border-b-0 border-gray-300 p-2 ml-2 flex-grow rounded-none'
+ aria-invalid={errorsKontakPerson.direkturName}
+ ref={direkturNameRef}
+ onChange={handleInputChange}
+ />
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsKontakPerson.direkturName}{' '}
+ {errorsKontakPerson.direkturTittle}
+ </div>
+ )}
+ </div>
+
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label text-nowrap'>
+ No. HP Direktur{' '}
+ <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ <input
+ id='direkturMobile'
+ name='direkturMobile'
+ ref={direkturMobileRef}
+ placeholder='Masukkan nomor direktur anda'
+ type='tel'
+ value={formKontakPerson.direkturMobile}
+ className='form-input'
+ aria-invalid={errorsKontakPerson.direkturMobile}
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsKontakPerson.direkturMobile}
+ </div>
+ )}
+ </div>
+
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label text-nowrap'>
+ Email Direktur
+ </label>
+ <input
+ id='direkturEmail'
+ name='direkturEmail'
+ ref={direkturEmailRef}
+ placeholder='contoh@email.com'
+ type='email'
+ value={formKontakPerson.direkturEmail}
+ className='form-input'
+ aria-invalid={errorsKontakPerson.direkturEmail}
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsKontakPerson.direkturEmail}
+ </div>
+ )}
+ </div>
+
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label text-nowrap'>
+ Nama Purchasing
+ </label>
+ <div className='flex items-center border border-gray-300 rounded-md w-full'>
+ <select
+ value={formKontakPerson.purchasingTittle || ''}
+ id='purchasingTittle'
+ name='purchasingTittle'
+ className=' p-2 rounded-l-md'
+ onChange={handleInputChange}
+ >
+ <option value='' disabled>
+ Pilih
+ </option>
+ <option value='Bpk'>Bpk</option>
+ <option value='Ibu'>Ibu</option>
+ </select>
+ <input
+ id='purchasingName'
+ name='purchasingName'
+ ref={purchasingNameRef}
+ placeholder='Masukkan nama purchasing anda'
+ value={formKontakPerson.purchasingName}
+ type='text'
+ className='form-input border-l border-r-0 border-t-0 border-b-0 border-gray-300 p-2 ml-2 flex-grow rounded-none'
+ aria-invalid={errorsKontakPerson.purchasingName}
+ onChange={handleInputChange}
+ />
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsKontakPerson.purchasingName}{' '}
+ {errorsKontakPerson.purchasingTittle}
+ </div>
+ )}
+ </div>
+
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label text-nowrap'>
+ No. HP Purchasing
+ </label>
+ <input
+ id='purchasingMobile'
+ name='purchasingMobile'
+ ref={financeMobileRef}
+ placeholder='Masukkan nomor purchasing anda'
+ value={formKontakPerson.purchasingMobile}
+ type='tel'
+ className='form-input'
+ aria-invalid={errorsKontakPerson.purchasingMobile}
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsKontakPerson.purchasingMobile}
+ </div>
+ )}
+ </div>
+
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label text-nowrap'>
+ Email Purchasing
+ </label>
+ <input
+ id='purchasingEmail'
+ name='purchasingEmail'
+ ref={purchasingEmailRef}
+ placeholder='contoh@email.com'
+ value={formKontakPerson.purchasingEmail}
+ type='email'
+ className='form-input'
+ aria-invalid={errorsKontakPerson.purchasingEmail}
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsKontakPerson.purchasingEmail}
+ </div>
+ )}
+ </div>
+
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label text-nowrap'>Nama Finance</label>
+ <div className='flex items-center border border-gray-300 rounded-md w-full'>
+ <select
+ value={formKontakPerson.financeTittle || ''}
+ id='financeTittle'
+ name='financeTittle'
+ className=' p-2 rounded-l-md'
+ onChange={handleInputChange}
+ >
+ <option value='' disabled>
+ Pilih
+ </option>
+ <option value='Bpk'>Bpk</option>
+ <option value='Ibu'>Ibu</option>
+ </select>
+ <input
+ id='financeName'
+ name='financeName'
+ ref={financeNameRef}
+ placeholder='Masukkan nama finance'
+ value={formKontakPerson.financeName}
+ type='text'
+ className='form-input border-l border-r-0 border-t-0 border-b-0 border-gray-300 p-2 ml-2 flex-grow rounded-none'
+ aria-invalid={errorsKontakPerson.financeName}
+ onChange={handleInputChange}
+ />
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsKontakPerson.financeName}{' '}
+ {errorsKontakPerson.financeTittle}
+ </div>
+ )}
+ </div>
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label text-nowrap'>
+ No. HP Finance
+ </label>
+ <input
+ id='financeMobile'
+ name='financeMobile'
+ ref={financeMobileRef}
+ placeholder='Masukkan nomor finance'
+ value={formKontakPerson.financeMobile}
+ type='tel'
+ className='form-input'
+ aria-invalid={errorsKontakPerson.financeMobile}
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsKontakPerson.financeMobile}
+ </div>
+ )}
+ </div>
+
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label text-nowrap'>Email Finance</label>
+ <input
+ id='financeEmail'
+ name='financeEmail'
+ ref={financeEmailRef}
+ placeholder='contoh@email.com'
+ type='email'
+ value={formKontakPerson.financeEmail}
+ className='form-input'
+ aria-invalid={errorsKontakPerson.financeEmail}
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsKontakPerson.financeEmail}
+ </div>
+ )}
+ </div>
+ </div>
+ </form>
+ </div>
+ )}
+ </>
+ );
+};
+
+export default KontakPerusahaan;
diff --git a/src/lib/pengajuan-tempo/component/PengajuanTempo.jsx b/src/lib/pengajuan-tempo/component/PengajuanTempo.jsx
new file mode 100644
index 00000000..5bef5134
--- /dev/null
+++ b/src/lib/pengajuan-tempo/component/PengajuanTempo.jsx
@@ -0,0 +1,716 @@
+import React from 'react';
+import { useMemo, useState, useEffect, useRef } from 'react';
+import Image from '~/components/ui/image';
+import Stepper from './Stepper';
+import InformasiPerusahaan from './informasiPerusahaan';
+import KontakPerusahaan from './KontakPerusahaan';
+import Pengiriman from './Pengiriman';
+import Referensi from './Referensi';
+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 {
+ usePengajuanTempoStore,
+ usePengajuanTempoStoreKontakPerson,
+ usePengajuanTempoStorePengiriman,
+ usePengajuanTempoStoreSupplier,
+ usePengajuanTempoStoreDokumen,
+} from '../../../../src-migrate/modules/register/stores/usePengajuanTempoStore';
+import { ChevronRightIcon, ChevronLeftIcon } from '@heroicons/react/24/outline';
+import createPengajuanTempoApi from '../api/createPengajuanTempoApi';
+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 editAuthTempo from '../api/editAuthTempo';
+import BottomPopup from '@/core/components/elements/Popup/BottomPopup';
+import PageContent from '@/lib/content/components/PageContent';
+const PengajuanTempo = () => {
+ const { isDesktop, isMobile } = useDevice();
+ const [currentStep, setCurrentStep] = React.useState(0);
+ const NUMBER_OF_STEPS = 6;
+ const [isLoading, setIsLoading] = useState(false);
+ const [bigData, setBigData] = useState();
+ const [idTempo, setIdTempo] = useState(0);
+ const { form, errors, validate, updateForm } = usePengajuanTempoStore();
+ const { control, watch, setValue } = useForm();
+ const auth = useAuth();
+ const router = useRouter();
+ const [BannerTempo, setBannerTempo] = useState();
+ const { formDokumen, errorsDokumen, validateDokumen, updateFormDokumen } =
+ usePengajuanTempoStoreDokumen();
+ const {
+ formKontakPerson,
+ errorsKontakPerson,
+ validateKontakPerson,
+ updateFormKontakPerson,
+ } = usePengajuanTempoStoreKontakPerson();
+ const {
+ formSupplier,
+ errorsSupplier,
+ validateSupplier,
+ updateFormSupplier,
+ hasSavedata,
+ updateHasSave,
+ } = usePengajuanTempoStoreSupplier();
+ const {
+ formPengiriman,
+ errorsPengiriman,
+ validatePengiriman,
+ updateFormPengiriman,
+ } = usePengajuanTempoStorePengiriman();
+ const [notValid, setNotValid] = useState(false);
+ const [buttonSubmitClick, setButtonSubmitClick] = useState(false);
+ const stepDivs = [
+ <InformasiPerusahaan
+ key='informasi-perusahaan'
+ chekValid={notValid}
+ buttonSubmitClick={buttonSubmitClick}
+ />,
+ <KontakPerusahaan
+ key='kontak-perusahaan'
+ chekValid={notValid}
+ buttonSubmitClick={buttonSubmitClick}
+ />,
+ <Pengiriman
+ key='pengiriman'
+ chekValid={notValid}
+ buttonSubmitClick={buttonSubmitClick}
+ />,
+ <Referensi
+ key='referensi'
+ chekValid={notValid}
+ buttonSubmitClick={buttonSubmitClick}
+ data={bigData?.supplierIds}
+ />,
+ <Dokumen
+ key='dokumen'
+ chekValid={notValid}
+ buttonSubmitClick={buttonSubmitClick}
+ />,
+ <Konfirmasi
+ key='konfirmasi'
+ chekValid={notValid}
+ buttonSubmitClick={buttonSubmitClick}
+ />,
+ ];
+
+ const stepDivsError = [
+ errors,
+ errorsKontakPerson,
+ errorsPengiriman,
+ errorsSupplier,
+ errorsDokumen,
+ <div key='konfirmasi'>Konfirmasi</div>,
+ ];
+ const stepDivsForm = [
+ form,
+ formKontakPerson,
+ formPengiriman,
+ formSupplier,
+ formDokumen,
+ '',
+ ];
+ const stepDivsUpdateForm = [
+ updateForm,
+ updateFormKontakPerson,
+ updateFormPengiriman,
+ updateFormSupplier,
+ updateFormDokumen,
+ <div key='konfirmasi'>Konfirmasi</div>,
+ ];
+ const stepLabels = [
+ 'informasi_perusahaan',
+ 'kontak_person',
+ 'Pengiriman',
+ 'Referensi',
+ 'Dokumen',
+ 'Konfirmasi',
+ ];
+ const isFormValid = useMemo(
+ () => Object.keys(stepDivsError[currentStep]).length === 0,
+ [stepDivsError[currentStep]]
+ );
+ useEffect(() => {
+ validate();
+ validateKontakPerson();
+ validatePengiriman();
+ validateDokumen();
+ updateHasSave(true);
+ if (isFormValid) {
+ window.scrollTo({
+ top: 0,
+ behavior: 'smooth',
+ });
+ }
+ }, [currentStep, buttonSubmitClick]);
+ useEffect(() => {
+ window.scrollTo({
+ top: 0,
+ behavior: 'smooth',
+ });
+ }, [currentStep]);
+
+ useEffect(() => {
+ const loadBigData = async () => {
+ const toCamelCase = (str) =>
+ str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
+
+ const transformKeysToCamelCase = (data) => {
+ if (Array.isArray(data)) {
+ return data.map((item) => transformKeysToCamelCase(item));
+ } else if (data && typeof data === 'object') {
+ return Object.keys(data).reduce((acc, key) => {
+ const camelKey = toCamelCase(key); // Ubah kunci menjadi camelCase
+ acc[camelKey] = transformKeysToCamelCase(data[key]); // Rekursif untuk nested object atau array
+ return acc;
+ }, {});
+ }
+ return data; // Jika bukan object atau array, kembalikan nilai aslinya
+ };
+
+ try {
+ const dataPaymentTerm = await odooApi(
+ 'GET',
+ `/api/v1/partner/detail-tempo/${auth.parentId}`
+ );
+ const transformedData = transformKeysToCamelCase(dataPaymentTerm);
+ setBigData(transformedData);
+ } catch (error) {
+ console.error('Error loading dataPaymentTerm:', error);
+ }
+ };
+
+ loadBigData();
+ }, [auth]);
+
+ useEffect(() => {
+ const cachedData = bigData;
+
+ const loadBigData = async () => {
+ if (cachedData) {
+ // Ambil kunci-kunci yang relevan berdasarkan currentStep dari stepDivsForm
+ const formKeys = stepDivsForm[currentStep]
+ ? Object.keys(stepDivsForm[currentStep])
+ : [];
+ if (currentStep === 3) {
+ stepDivsUpdateForm[currentStep](cachedData.supplierIds);
+ } else if (currentStep === 4) {
+ formKeys.forEach((key) => {
+ if (cachedData[key]) {
+ // Proses hanya kunci yang ada di cachedData
+ const { name, format, base64 } = cachedData[key];
+ stepDivsUpdateForm[currentStep](key, name, format, base64);
+ }
+ });
+ } else {
+ formKeys.forEach((key) => {
+ if (bigData[key]) {
+ // Ubah data menjadi string
+ const stringData =
+ typeof bigData[key] === 'object'
+ ? JSON.stringify(bigData[key]) // Untuk objek atau array
+ : String(bigData[key]); // Untuk tipe primitif
+ // Kirim data yang sudah diubah ke string ke stepDivsUpdateForm
+ stepDivsUpdateForm[currentStep](key, stringData);
+ }
+ });
+ }
+ }
+ };
+ loadBigData();
+ validate();
+ validateKontakPerson();
+ validatePengiriman();
+ validateDokumen();
+ validateSupplier();
+ updateHasSave(true);
+ }, [currentStep, bigData, auth]);
+
+ const goToNextStep = () => {
+ if (!isFormValid) {
+ setNotValid(true);
+ setButtonSubmitClick(!buttonSubmitClick);
+ return;
+ } else {
+ // saveToLocalStorage(stepLabels[currentStep], stepDivsForm[currentStep]);
+ if (currentStep == 3) {
+ handleDaftarTempoSupplier();
+ } else if (currentStep == 4) {
+ handleDaftarTempoDokumen();
+ } else {
+ handleDaftarTempoPerPage(
+ stepLabels[currentStep],
+ stepDivsForm[currentStep]
+ );
+ }
+ setButtonSubmitClick(!buttonSubmitClick);
+ setNotValid(false);
+ }
+ setCurrentStep((prev) => (prev === NUMBER_OF_STEPS - 1 ? prev : prev + 1));
+ };
+
+ const goPrevStep = () => {
+ // if (!isFormValid) {
+ // setNotValid(true);
+ // setButtonSubmitClick(!buttonSubmitClick);
+ // return;
+ // } else {
+ // // saveToLocalStorage(stepLabels[currentStep], stepDivsForm[currentStep]);
+ // if (currentStep == 3) {
+ // handleDaftarTempoSupplier();
+ // } else if (currentStep == 4) {
+ // handleDaftarTempoDokumen();
+ // } else {
+ // handleDaftarTempoPerPage(
+ // stepLabels[currentStep],
+ // stepDivsForm[currentStep]
+ // );
+ // }
+ // setButtonSubmitClick(!buttonSubmitClick);
+ // setNotValid(false);
+ // }
+ setCurrentStep((prev) => (prev === NUMBER_OF_STEPS - 1 ? prev : prev - 1));
+ };
+
+ const handleDaftarTempoPerPage = async (
+ label,
+ formData,
+ tempoRequest = false
+ ) => {
+ for (const error of stepDivsError) {
+ if (error.length > 0) {
+ return;
+ }
+ }
+ const toastId = toast.loading('Mengirimkan formulir pengajuan tempo...');
+ setIsLoading(true);
+ try {
+ const address = await createPengajuanTempoApi({
+ id: idTempo,
+ user_id: auth.parentId,
+ partner_id: auth.partnerId,
+ section: label,
+ tempo_request: tempoRequest,
+ ...formData,
+ });
+ if (address.id) {
+ setIdTempo(address.id);
+ }
+ toast.dismiss(toastId);
+ setIsLoading(false);
+ } catch (error) {
+ toast.dismiss(toastId);
+ setIsLoading(false);
+
+ toast.error('Terjadi kesalahan dalam pengiriman formulir');
+ console.error(error);
+ }
+ };
+ const handleDaftarTempoDokumen = async () => {
+ for (const error of stepDivsError) {
+ if (error.length > 0) {
+ return;
+ }
+ }
+
+ // Filter hanya dokumen dengan `format` yang tidak undefined
+ const formattedDokumen = Object.entries(formDokumen)
+ .filter(([_, doc]) => doc.format !== undefined) // Hanya dokumen dengan `format` tidak undefined
+ .map(([key, doc]) => ({
+ documentName: key,
+ details: {
+ name: doc.name,
+ format: doc.format,
+ base64: doc.base64,
+ },
+ }));
+
+ const toastId = toast.loading('Mengirimkan formulir pengajuan tempo...');
+ if (formattedDokumen.length === 0) {
+ // toast.error('Tidak ada dokumen valid untuk dikirim.');
+ return;
+ }
+
+ setIsLoading(true);
+ try {
+ const address = await createPengajuanTempoApi({
+ id: idTempo,
+ user_id: auth.parentId,
+ partner_id: auth.partnerId,
+ formDocs: JSON.stringify(formattedDokumen),
+ });
+ if (address.id) {
+ setIdTempo(address.id);
+ }
+ toast.dismiss(toastId);
+ setIsLoading(false);
+ } catch (error) {
+ toast.dismiss(toastId);
+ setIsLoading(false);
+
+ toast.error('Terjadi kesalahan dalam pengiriman formulir');
+ console.error(error);
+ }
+ };
+
+ const isSupplierDataChanged = (formSupplier, supplierIds) => {
+ if (formSupplier.length !== supplierIds.length) {
+ return true;
+ }
+ return formSupplier.some((supplier, index) => {
+ const original = supplierIds[index];
+ return (
+ supplier.supplier !== original.supplier ||
+ supplier.pic !== original.pic ||
+ supplier.telepon !== original.telepon ||
+ supplier.durasiTempo !== original.durasiTempo ||
+ supplier.creditLimit !== original.creditLimit
+ );
+ });
+ };
+
+ const handleDaftarTempoSupplier = async () => {
+ for (const error of stepDivsError) {
+ if (error.length > 0) {
+ return;
+ }
+ }
+
+ const productOrder = formSupplier.map((product) => ({
+ supplier: product.supplier,
+ pic: product.pic,
+ telepon: product.telepon,
+ durasiTempo: product.durasiTempo,
+ creditLimit: product.creditLimit,
+ }));
+
+ // Periksa perubahan
+ const toastId = toast.loading('Mengirimkan formulir pengajuan tempo...');
+ if (bigData?.supplierIds) {
+ if (!isSupplierDataChanged(productOrder, bigData.supplierIds)) {
+ return;
+ }
+ }
+
+ setIsLoading(true);
+ try {
+ const address = await createPengajuanTempoApi({
+ id: idTempo,
+ user_id: auth.parentId,
+ partner_id: auth.partnerId,
+ formSupplier: JSON.stringify(productOrder),
+ });
+ if (address.id) {
+ setIdTempo(address.id);
+ }
+ toast.dismiss(toastId);
+ setIsLoading(false);
+ } catch (error) {
+ toast.dismiss(toastId);
+ setIsLoading(false);
+
+ toast.error('Terjadi kesalahan dalam pengiriman formulir');
+ console.error(error);
+ }
+ };
+
+ const handleDaftarTempo = async () => {
+ for (const error of stepDivsError) {
+ if (error.length > 0) {
+ return;
+ }
+ }
+
+ // Filter hanya dokumen dengan `format` yang tidak undefined
+ const formattedDokumen = Object.entries(formDokumen)
+ .filter(([_, doc]) => doc.format !== undefined) // Hanya dokumen dengan `format` tidak undefined
+ .map(([key, doc]) => ({
+ documentName: key,
+ details: {
+ name: doc.name,
+ format: doc.format,
+ base64: doc.base64,
+ },
+ }));
+
+ const toastId = toast.loading('Mengirimkan formulir pengajuan tempo...');
+ setIsLoading(true);
+ try {
+ let address4;
+ let address3;
+ const address = await createPengajuanTempoApi({
+ id: 0,
+ partner_id: auth.partnerId,
+ user_id: auth.parentId,
+ tempo_request: false,
+ ...form,
+ });
+ if (address.id) {
+ const address2 = await createPengajuanTempoApi({
+ id: address.id,
+ partner_id: auth.partnerId,
+ user_id: address.userId,
+ tempo_request: false,
+ ...formKontakPerson,
+ });
+ if (address2.id) {
+ address3 = await createPengajuanTempoApi({
+ id: address2.id,
+ partner_id: auth.partnerId,
+ user_id: address2.userId,
+ tempo_request: false,
+ ...formPengiriman,
+ });
+ if (address3.id && formattedDokumen.length > 0) {
+ // Kirim dokumen yang sudah difilter
+ address4 = await createPengajuanTempoApi({
+ id: address3.id,
+ partner_id: auth.partnerId,
+ user_id: address3.userId,
+ tempo_request: true,
+ formDocs: JSON.stringify(formattedDokumen),
+ });
+ } else {
+ address4 = await createPengajuanTempoApi({
+ id: address3.id,
+ partner_id: auth.partnerId,
+ user_id: address3.userId,
+ tempo_request: true,
+ });
+ }
+ }
+ }
+
+ if (address4?.id) {
+ toast.success('Pengajuan tempo berhasil dilakukan');
+ const toastId = toast.loading('Mengubah status akun...');
+ const isUpdated = await editAuthTempo();
+ if (isUpdated?.user) {
+ const update = await setAuth(isUpdated.user);
+ if (update) {
+ toast.dismiss(toastId);
+ setIsLoading(false);
+ toast.success('Berhasil mengubah status akun', { duration: 1000 });
+ router.push('/pengajuan-tempo/finish');
+ } else {
+ toast.dismiss(toastId);
+ setIsLoading(false);
+ toast.success('Pengajuan tempo berhasil dilakukan');
+ toast.error('Gagal mengubah status akun', { duration: 1000 });
+ router.push('/pengajuan-tempo');
+ }
+ removeFromLocalStorage();
+ return;
+ }
+ } else {
+ toast.dismiss(toastId);
+ setIsLoading(false);
+
+ toast.error('Terjadi kesalahan dalam pengiriman formulir');
+ }
+ } catch (error) {
+ toast.dismiss(toastId);
+ setIsLoading(false);
+
+ toast.error('Terjadi kesalahan dalam pengiriman formulir');
+ console.error(error);
+ }
+ };
+
+ const removeFromLocalStorage = () => {
+ for (const key of stepLabels) {
+ localStorage.removeItem(key);
+ }
+ };
+
+ const [isCheckedTNC, setIsCheckedTNC] = useState(false);
+
+ const handleCheckChange = (checked) => {
+ setIsCheckedTNC(checked);
+ };
+
+ 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 Pengajuan Tempo
+ </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-20'} bg-gray_r-3`}
+ />
+
+ <div className={`container ${isMobile ? 'mt-4' : 'mt-10'} flex flex-col`}>
+ <div className='flex items-center justify-center'>
+ <Stepper currentStep={currentStep} numberOfSteps={NUMBER_OF_STEPS} />
+ </div>
+ <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`}
+ >
+ <span className='text-xs opacity-60'>
+ *Pastikan data yang anda masukan sudah benar dan sesuai
+ </span>
+ <div
+ className={` flex flex-row ${
+ currentStep > 0 && currentStep < 5
+ ? 'justify-between'
+ : 'justify-end'
+ } items-center w-full ${isMobile ? 'gap-x-4 ' : ''}`}
+ >
+ {currentStep < 5 && currentStep > 0 && (
+ <Button
+ colorScheme='yellow'
+ w={`${isMobile ? 'full' : 'fit'}`}
+ onClick={goPrevStep}
+ >
+ {<ChevronLeftIcon className='w-5' />}
+ <span className={`font-medium ${isMobile ? 'text-xs' : ''} `}>
+ Langkah Sebelumnya
+ </span>
+ </Button>
+ )}
+ {currentStep < 5 && (
+ <>
+ <Tooltip
+ label={clsxm({
+ 'Klik simpan data terlebih dahulu':
+ currentStep === 3 && !hasSavedata,
+ })}
+ >
+ <Button
+ colorScheme='red'
+ w={`${isMobile ? 'full' : 'fit'}`}
+ isDisabled={
+ (currentStep === 3 && !hasSavedata) ||
+ currentStep === NUMBER_OF_STEPS - 1
+ }
+ onClick={goToNextStep}
+ >
+ <span className={`${isMobile ? 'text-xs' : ''} `}>
+ Langkah Selanjutnya
+ </span>
+ {<ChevronRightIcon className='w-5' />}
+ </Button>
+ </Tooltip>
+ </>
+ )}
+ </div>
+ {currentStep == 5 && (
+ <div
+ className={`flex flex-col ${
+ isMobile ? 'items-start' : 'items-end'
+ } w-full justify-start gap-4`}
+ >
+ <TempoTermCondition onCheckChange={handleCheckChange} />
+ <Button
+ colorScheme='red'
+ w={`${isMobile ? 'full' : '36'}`}
+ isDisabled={!isCheckedTNC}
+ onClick={handleDaftarTempo}
+ >
+ Daftar Tempo {<ChevronRightIcon className='w-5' />}
+ </Button>
+ </div>
+ )}
+ </div>
+ </div>
+ </>
+ );
+};
+const TempoTermCondition = ({ onCheckChange }) => {
+ const [isCheckedTNC, SetIsCheckedTNC] = useState(false);
+ const [openTNC, SetOpenTNC] = useState(false);
+ const { isDesktop, isMobile } = useDevice();
+
+ const openTNCHandle = () => {
+ SetOpenTNC(!openTNC);
+ };
+
+ const toggleCheckTNC = () => {
+ const newValue = !isCheckedTNC;
+ SetIsCheckedTNC(newValue);
+ if (onCheckChange) {
+ onCheckChange(newValue);
+ }
+ };
+ return (
+ <>
+ <div className='flex items-center gap-x-2'>
+ <Checkbox
+ id='tnc'
+ name='tnc'
+ colorScheme='red'
+ isChecked={isCheckedTNC}
+ onChange={toggleCheckTNC}
+ />
+ <div className={`${isMobile ? 'text-xs' : ''}`}>
+ <label htmlFor='tnc' className='cursor-pointer'>
+ Dengan ini saya menyetujui
+ </label>{' '}
+ <span
+ className='font-medium text-danger-500 cursor-pointer'
+ onClick={openTNCHandle}
+ >
+ syarat dan ketentuan
+ </span>
+ <label htmlFor='tnc' className='ml-2 cursor-pointer'>
+ yang berlaku
+ </label>
+ </div>
+ </div>
+
+ <BottomPopup
+ active={openTNC}
+ close={() => SetOpenTNC(false)}
+ title='Syarat & Ketentuan Pendaftaran Tempo'
+ >
+ <PageContent path='/tempoTnd' />
+ </BottomPopup>
+ </>
+ );
+};
+
+export default PengajuanTempo;
diff --git a/src/lib/pengajuan-tempo/component/Pengiriman.jsx b/src/lib/pengajuan-tempo/component/Pengiriman.jsx
new file mode 100644
index 00000000..755cf45d
--- /dev/null
+++ b/src/lib/pengajuan-tempo/component/Pengiriman.jsx
@@ -0,0 +1,2078 @@
+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 odooApi from '~/libs/odooApi';
+import stateApi from '@/lib/address/api/stateApi.js';
+import cityApi from '@/lib/address/api/cityApi';
+import districtApi from '@/lib/address/api/districtApi';
+import subDistrictApi from '@/lib/address/api/subDistrictApi';
+import { Radio, RadioGroup, Stack, Checkbox } from '@chakra-ui/react';
+import {
+ usePengajuanTempoStorePengiriman,
+ usePengajuanTempoStore,
+} from '../../../../src-migrate/modules/register/stores/usePengajuanTempoStore';
+import { toast } from 'react-hot-toast';
+import useDevice from '@/core/hooks/useDevice';
+const Pengiriman = ({ chekValid, buttonSubmitClick, isKonfirmasi }) => {
+ const { control, watch, setValue } = useForm();
+ const { isDesktop, isMobile } = useDevice();
+ const {
+ formPengiriman,
+ errorsPengiriman,
+ validatePengiriman,
+ updateFormPengiriman,
+ } = usePengajuanTempoStorePengiriman();
+ const { form } = usePengajuanTempoStore();
+ const [states, setState] = useState([]);
+ const [cities, setCities] = useState([]);
+ const [districts, setDistricts] = useState([]);
+ const [subDistricts, setSubDistricts] = useState([]);
+ const [zips, setZips] = useState([]);
+ const [citiesInvoice, setCitiesInvoice] = useState([]);
+ const [districtsInvoice, setDistrictsInvoice] = useState([]);
+ const [subDistrictsInvoice, setSubDistrictsInvoice] = useState([]);
+ const [zipsInvoice, setZipsInvoice] = useState([]);
+ const [sameAddress, setSameAddress] = useState(false);
+ const [sameAddressStreet, setSameAddressStreet] = useState(false);
+
+ const [selectedIds, setSelectedIds] = useState(
+ formPengiriman.dokumenPengiriman
+ ? formPengiriman.dokumenPengiriman.split(',').map(Number)
+ : []
+ );
+ const [selectedIdsDokumenInvoice, setSelectedIdsselectedIdsDokumenInvoice] =
+ useState(
+ formPengiriman.dokumenPengirimanInvoice
+ ? formPengiriman.dokumenPengirimanInvoice.split(',').map(Number)
+ : []
+ );
+
+ const handleCheckboxChange = (id) => {
+ const updatedSelected = selectedIds.includes(id)
+ ? selectedIds.filter((selectedId) => selectedId !== id)
+ : [...selectedIds, id];
+
+ setSelectedIds(updatedSelected);
+
+ // Jika checkbox 'Lainnya' dipilih, aktifkan input custom
+
+ updateFormPengiriman('dokumenPengiriman', updatedSelected.join(','));
+ validatePengiriman();
+ };
+ const handleCheckboxChangeDokumenPengirimanInvoice = (id) => {
+ const updatedSelected = selectedIdsDokumenInvoice.includes(id)
+ ? selectedIdsDokumenInvoice.filter((selectedId) => selectedId !== id)
+ : [...selectedIdsDokumenInvoice, id];
+
+ setSelectedIdsselectedIdsDokumenInvoice(updatedSelected);
+
+ // Jika checkbox 'Lainnya' dipilih, aktifkan input custom
+
+ updateFormPengiriman('dokumenPengirimanInvoice', updatedSelected.join(','));
+ validatePengiriman();
+ };
+
+ useEffect(() => {
+ if (formPengiriman.dokumenPengiriman) {
+ setSelectedIds(formPengiriman.dokumenPengiriman.split(',').map(Number)); // Parse string menjadi array angka
+ }
+ }, [formPengiriman.dokumenPengiriman]);
+ useEffect(() => {
+ if (formPengiriman.dokumenPengirimanInvoice) {
+ setSelectedIdsselectedIdsDokumenInvoice(
+ formPengiriman.dokumenPengirimanInvoice.split(',').map(Number)
+ ); // Parse string menjadi array angka
+ }
+ }, [formPengiriman.dokumenPengirimanInvoice]);
+
+ // Fungsi untuk memeriksa apakah item sudah dipilih
+ const isChecked = (id) => selectedIds.includes(id);
+ const isCheckedInvoice = (id) => selectedIdsDokumenInvoice.includes(id);
+
+ useEffect(() => {
+ const loadState = async () => {
+ let dataState = await stateApi({ tempo: true });
+ dataState = dataState.map((state) => ({
+ value: state.id,
+ label: state.name,
+ }));
+ setState(dataState);
+ };
+ loadState();
+ }, []);
+
+ const watchState = watch('statePengiriman');
+ useEffect(() => {
+ // updateFormPengiriman('cityPengiriman', '');
+ // setValue('cityPengiriman', '');
+ // setValue('districtPengiriman', '');
+ // setValue('subDistrictPengiriman', '');
+ // setValue('zipPengiriman', '');
+ if (watchState) {
+ updateFormPengiriman('statePengiriman', `${watchState}`);
+ validatePengiriman();
+ const loadCities = async () => {
+ let dataCities = await cityApi({ stateId: watchState });
+ dataCities = dataCities.map((city) => ({
+ value: city.id,
+ label: city.name,
+ }));
+ setCities(dataCities);
+ };
+ loadCities();
+ }
+ }, [watchState]);
+
+ const watchCity = watch('cityPengiriman');
+
+ useEffect(() => {
+ if (watchCity) {
+ updateFormPengiriman('cityPengiriman', `${watchCity}`);
+ validatePengiriman();
+ const loadDistricts = async () => {
+ let dataDistricts = await districtApi({ cityId: watchCity });
+ dataDistricts = dataDistricts.map((district) => ({
+ value: district.id,
+ label: district.name,
+ }));
+ setDistricts(dataDistricts);
+ };
+ loadDistricts();
+ }
+ }, [watchCity, setValue]);
+
+ const watchDistrict = watch('districtPengiriman');
+ useEffect(() => {
+ // setValue('subDistrictPengiriman', '');
+ if (watchDistrict) {
+ updateFormPengiriman('districtPengiriman', `${watchDistrict}`);
+ validatePengiriman();
+ const loadSubDistricts = async () => {
+ let dataSubDistricts = await subDistrictApi({
+ districtId: watchDistrict,
+ });
+ dataSubDistricts = dataSubDistricts.map((district) => ({
+ value: district.id,
+ label: district.name,
+ }));
+ setSubDistricts(dataSubDistricts);
+ };
+ loadSubDistricts();
+ }
+ }, [watchDistrict, setValue]);
+
+ const watchsubDistrict = watch('subDistrictPengiriman');
+
+ useEffect(() => {
+ let kelurahan = '';
+
+ if (watchsubDistrict) {
+ updateFormPengiriman('subDistrictPengiriman', `${watchsubDistrict}`);
+ validatePengiriman();
+ 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}`
+ );
+
+ const result = await response.json();
+ const dataZips = result.result.map((zip) => ({
+ value: parseInt(zip.kodepos),
+ label: zip.kodepos,
+ }));
+ setZips(dataZips);
+ };
+ loadZip();
+ }
+ }, [watchsubDistrict, setValue, subDistricts]);
+
+ const watchZip = watch('zipPengiriman');
+ useEffect(() => {
+ if (watchZip) {
+ updateFormPengiriman('zipPengiriman', `${watchZip}`);
+ validatePengiriman();
+ }
+ }, [watchZip, setValue]);
+
+ const watchStateInvoice = watch('stateInvoice');
+ useEffect(() => {
+ // setValue('cityInvoice', '');
+ // setValue('districtInvoice', '');
+ // setValue('subDistrictInvoice', '');
+ // setValue('zipInvoice', '');
+ if (watchStateInvoice) {
+ updateFormPengiriman('stateInvoice', `${watchStateInvoice}`);
+ validatePengiriman();
+ const loadCities = async () => {
+ let dataCities = await cityApi({ stateId: watchStateInvoice });
+ dataCities = dataCities.map((city) => ({
+ value: city.id,
+ label: city.name,
+ }));
+ setCitiesInvoice(dataCities);
+ };
+ loadCities();
+ }
+ }, [watchStateInvoice]);
+
+ const watchCityInvoice = watch('cityInvoice');
+ useEffect(() => {
+ if (watchCityInvoice) {
+ updateFormPengiriman('cityInvoice', `${watchCityInvoice}`);
+ validatePengiriman();
+ const loadDistricts = async () => {
+ let dataDistricts = await districtApi({ cityId: watchCityInvoice });
+ dataDistricts = dataDistricts.map((district) => ({
+ value: district.id,
+ label: district.name,
+ }));
+ setDistrictsInvoice(dataDistricts);
+ };
+ loadDistricts();
+ }
+ }, [watchCityInvoice]);
+
+ const watchDistrictInvoice = watch('districtInvoice');
+ useEffect(() => {
+ // setValue('subDistrictInvoice', '');
+ if (watchDistrictInvoice) {
+ updateFormPengiriman('districtInvoice', `${watchDistrictInvoice}`);
+ validatePengiriman();
+ const loadSubDistricts = async () => {
+ let dataSubDistricts = await subDistrictApi({
+ districtId: watchDistrictInvoice,
+ });
+ dataSubDistricts = dataSubDistricts.map((district) => ({
+ value: district.id,
+ label: district.name,
+ }));
+ setSubDistrictsInvoice(dataSubDistricts);
+ };
+ loadSubDistricts();
+ }
+ }, [watchDistrictInvoice, setValue]);
+
+ const watchsubDistrictInvoice = watch('subDistrictInvoice');
+
+ useEffect(() => {
+ let kelurahan = '';
+
+ if (watchsubDistrictInvoice) {
+ updateFormPengiriman('subDistrictInvoice', `${watchsubDistrictInvoice}`);
+ validatePengiriman();
+ for (const data in subDistrictsInvoice) {
+ if (subDistrictsInvoice[data].value == watchsubDistrictInvoice) {
+ kelurahan = subDistrictsInvoice[data].label.toLowerCase();
+ }
+ }
+ const loadZip = async () => {
+ const response = await fetch(
+ `https://alamat.thecloudalert.com/api/cari/index/?keyword=${kelurahan}`
+ );
+
+ const result = await response.json();
+ const dataZips = result.result.map((zip) => ({
+ value: parseInt(zip.kodepos),
+ label: zip.kodepos,
+ }));
+ setZipsInvoice(dataZips);
+ };
+ loadZip();
+ }
+ }, [watchsubDistrictInvoice, setValue, subDistrictsInvoice]);
+
+ const watchZipInvoice = watch('zipInvoice');
+ useEffect(() => {
+ if (watchZipInvoice) {
+ updateFormPengiriman('zipInvoice', `${watchZipInvoice}`);
+ validatePengiriman();
+ }
+ }, [watchZipInvoice, setValue]);
+
+ const handleInputChange = (event) => {
+ const { name, value } = event.target;
+ updateFormPengiriman(name, value);
+ validatePengiriman();
+ };
+
+ const isFormValid = useMemo(
+ () => Object.keys(errorsPengiriman).length === 0,
+ [errorsPengiriman]
+ );
+
+ const PICNameRef = useRef(null);
+ const streetPengirimanRef = useRef(null);
+ const statePengirimanRef = useRef(null);
+ const cityPengirimanRef = useRef(null);
+ const districtPengirimanRef = useRef(null);
+ const subDistrictPengirimanRef = useRef(null);
+ const zipRef = useRef(null);
+ const invoicePicRef = useRef(null);
+ const streetInvoiceRef = useRef(null);
+ const stateInvoiceRef = useRef(null);
+ const cityInvoiceRef = useRef(null);
+ const districtInvoiceRef = useRef(null);
+ const subDistrictInvoiceRef = useRef(null);
+ const zipInvoiceRef = useRef(null);
+ const everyWeekdayInputRef = useRef(null);
+ const everyWeekInputRef = useRef(null);
+ const dokumenPengirimanRef = useRef(null);
+ const dokumenPengirimanInputRef = useRef(null);
+ const dokumenPengirimanInvoiceRef = useRef(null);
+ const dokumenPengirimanInvoiceInputRef = useRef(null);
+
+ useEffect(() => {
+ const loadIndustries = async () => {
+ if (!isFormValid) {
+ const options = {
+ behavior: 'smooth',
+ block: 'center',
+ };
+ if (errorsPengiriman.PICName && PICNameRef.current) {
+ PICNameRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsPengiriman.streetPengiriman && streetPengirimanRef.current) {
+ streetPengirimanRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsPengiriman.statePengiriman && statePengirimanRef.current) {
+ statePengirimanRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsPengiriman.cityPengiriman && cityPengirimanRef.current) {
+ cityPengirimanRef.current.scrollIntoView(options);
+ return;
+ }
+ if (
+ errorsPengiriman.districtsPengiriman &&
+ districtPengirimanRef.current
+ ) {
+ districtPengirimanRef.current.scrollIntoView(options);
+ return;
+ }
+ if (
+ errorsPengiriman.subDistrictsPengiriman &&
+ subDistrictPengirimanRef.current
+ ) {
+ subDistrictPengirimanRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsPengiriman.zip && zipRef.current) {
+ zipRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsPengiriman.invoicePic && invoicePicRef.current) {
+ invoicePicRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsPengiriman.streetInvoice && streetInvoiceRef.current) {
+ streetInvoiceRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsPengiriman.stateInvoice && stateInvoiceRef.current) {
+ stateInvoiceRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsPengiriman.cityInvoice && cityInvoiceRef.current) {
+ cityInvoiceRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsPengiriman.districtsInvoice && districtInvoiceRef.current) {
+ districtInvoiceRef.current.scrollIntoView(options);
+ return;
+ }
+ if (
+ errorsPengiriman.subDistrictsInvoice &&
+ subDistrictInvoiceRef.current
+ ) {
+ subDistrictInvoiceRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsPengiriman.zipInvoice && zipInvoiceRef.current) {
+ zipInvoiceRef.current.scrollIntoView(options);
+ return;
+ }
+ if (
+ errorsPengiriman.everyWeekdayInput &&
+ everyWeekdayInputRef.current
+ ) {
+ everyWeekdayInputRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errorsPengiriman.everyWeekInput && everyWeekInputRef.current) {
+ everyWeekInputRef.current.scrollIntoView(options);
+ return;
+ }
+ if (
+ errorsPengiriman.dokumenPengiriman &&
+ dokumenPengirimanRef.current
+ ) {
+ dokumenPengirimanRef.current.scrollIntoView(options);
+ return;
+ }
+ if (
+ errorsPengiriman.dokumenInvoice &&
+ dokumenPengirimanInvoiceRef.current
+ ) {
+ dokumenPengirimanInvoiceRef.current.scrollIntoView(options);
+ return;
+ }
+ if (
+ selectedIds &&
+ formPengiriman.dokumenPengirimanInput === '' &&
+ dokumenPengirimanInputRef.current
+ ) {
+ dokumenPengirimanInputRef.current.scrollIntoView(options);
+ return;
+ }
+ if (
+ selectedIdsDokumenInvoice &&
+ formPengiriman.dokumenPengirimanInvoiceInput === '' &&
+ dokumenPengirimanInvoiceInputRef.current
+ ) {
+ dokumenPengirimanInvoiceInputRef.current.scrollIntoView(options);
+ return;
+ }
+ }
+ };
+ loadIndustries();
+ }, [buttonSubmitClick, chekValid]);
+
+ useEffect(() => {
+ if (formPengiriman.isSameAddreesStreet == 'true') {
+ setSameAddressStreet(true);
+ } else {
+ setSameAddressStreet(false);
+ }
+ }, [formPengiriman.isSameAddreesStreet]);
+ useEffect(() => {
+ if (formPengiriman.isSameAddrees == 'true') {
+ setSameAddress(true);
+ } else {
+ setSameAddress(false);
+ }
+ }, [formPengiriman.isSameAddrees]);
+
+ // useEffect(() => {
+ // validatePengiriman();
+ // if (formPengiriman.isSameAddrees) {
+ // const isSame = formPengiriman.isSameAddrees;
+ // if (isSame == 'true') {
+ // setSameAddress(true);
+ // } else {
+ // setSameAddress(false);
+ // }
+ // }
+ // if (formPengiriman.isSameAddreesStreet) {
+ // const isSame = formPengiriman.isSameAddreesStreet;
+ // if (isSame == 'true') {
+ // setSameAddressStreet(true);
+ // } else {
+ // setSameAddressStreet(false);
+ // }
+ // }
+ // validatePengiriman();
+ // }, [buttonSubmitClick]);
+ // useEffect(() => {
+ // if (formPengiriman.isSameAddrees) {
+ // const isSame = formPengiriman.isSameAddrees;
+ // if (isSame == 'true') {
+ // setSameAddress(true);
+ // } else {
+ // setSameAddress(false);
+ // }
+ // }
+ // if (formPengiriman.isSameAddreesStreet) {
+ // const isSame = formPengiriman.isSameAddreesStreet;
+ // if (isSame == 'true') {
+ // setSameAddressStreet(true);
+ // } else {
+ // setSameAddressStreet(false);
+ // }
+ // }
+ // validatePengiriman();
+ // }, [formPengiriman.isSameAddrees, formPengiriman.isSameAddreesStreet]);
+
+ useEffect(() => {
+ if (sameAddressStreet) {
+ updateFormPengiriman('streetPengiriman', form.street);
+ updateFormPengiriman('statePengiriman', form.state);
+ setValue('statePengiriman', parseInt(form.state));
+ updateFormPengiriman('cityPengiriman', form.city);
+ setValue('cityPengiriman', parseInt(form.city));
+ updateFormPengiriman('districtPengiriman', form.district);
+ setValue('districtPengiriman', parseInt(form.district));
+ updateFormPengiriman('subDistrictPengiriman', form.subDistrict);
+ setValue('subDistrictPengiriman', parseInt(form.subDistrict));
+ updateFormPengiriman('zipPengiriman', form.zip);
+ setValue('zipPengiriman', parseInt(form.zip));
+ }
+ // else {
+ // updateFormPengiriman('streetPengiriman', '');
+ // updateFormPengiriman('statePengiriman', '');
+ // updateFormPengiriman('cityPengiriman', '');
+ // updateFormPengiriman('zipPengiriman', '');
+ // setValue('streetPengiriman', '');
+ // setValue('statePengiriman', '');
+ // setValue('cityPengiriman', '');
+ // }
+ validatePengiriman();
+ }, [
+ sameAddressStreet,
+ form.state,
+ form.city,
+ form.district,
+ form.subDistrict,
+ form.zip,
+ ]);
+
+ useEffect(() => {
+ if (sameAddress) {
+ updateFormPengiriman('streetInvoice', form.street);
+ updateFormPengiriman('stateInvoice', form.state);
+ setValue('stateInvoice', parseInt(form.state));
+ updateFormPengiriman('cityInvoice', form.city);
+ setValue('cityInvoice', parseInt(form.city));
+ updateFormPengiriman('districtInvoice', form.district);
+ setValue('districtInvoice', parseInt(form.district));
+ updateFormPengiriman('subDistrictInvoice', form.subDistrict);
+ setValue('subDistrictInvoice', parseInt(form.subDistrict));
+ updateFormPengiriman('zipInvoice', form.zip);
+ setValue('zipInvoice', parseInt(form.zip));
+ } else {
+ // updateFormPengiriman('streetInvoice', '');
+ // updateFormPengiriman('stateInvoice', '');
+ // updateFormPengiriman('cityInvoice', '');
+ // setValue('streetInvoice', '');
+ // setValue('stateInvoice', '');
+ // setValue('cityInvoice', '');
+ }
+ validatePengiriman();
+ }, [
+ sameAddress,
+ form.street,
+ form.state,
+ form.city,
+ form.district,
+ form.subDistrict,
+ ]);
+
+ // useEffect(() => {
+ // if (sameAddress) {
+ // updateFormPengiriman('streetInvoice', formPengiriman.streetPengiriman);
+ // updateFormPengiriman('stateInvoice', formPengiriman.statePengiriman);
+ // setValue('stateInvoice', parseInt(formPengiriman.statePengiriman));
+ // updateFormPengiriman('cityInvoice', formPengiriman.cityPengiriman);
+ // setValue('cityInvoice', parseInt(formPengiriman.cityPengiriman));
+
+ // updateFormPengiriman('isSameAddrees', `${sameAddress}`);
+ // } else {
+ // // updateFormPengiriman('stateInvoice', formPengiriman.stateInvoice);
+ // // setValue('stateInvoice', parseInt(formPengiriman.stateInvoice));
+ // // updateFormPengiriman('cityInvoice', formPengiriman.cityInvoice);
+ // // setValue('cityInvoice', parseInt(formPengiriman.cityInvoice));
+ // }
+ // validatePengiriman();
+ // }, [
+ // sameAddress,
+ // formPengiriman.streetPengiriman,
+ // formPengiriman.statePengiriman,
+ // formPengiriman.cityPengiriman,
+ // ]);
+
+ // useEffect(() => {
+ // if (formPengiriman.sameAddressStreet?.toLowerCase().includes('true')) {
+ // setSameAddressStreet(true);
+ // } else {
+ // setSameAddressStreet(false);
+ // }
+ // }, [formPengiriman.sameAddressStreet]);
+
+ useEffect(() => {
+ if (formPengiriman.statePengiriman) {
+ setValue('statePengiriman', parseInt(formPengiriman.statePengiriman));
+ }
+ }, [formPengiriman.statePengiriman]);
+ useEffect(() => {
+ if (formPengiriman.cityPengiriman) {
+ setValue('cityPengiriman', parseInt(formPengiriman.cityPengiriman));
+ }
+ }, [formPengiriman.cityPengiriman]);
+ useEffect(() => {
+ if (formPengiriman.districtPengiriman) {
+ setValue(
+ 'districtPengiriman',
+ parseInt(formPengiriman.districtPengiriman)
+ );
+ }
+ }, [formPengiriman.districtPengiriman]);
+ useEffect(() => {
+ if (formPengiriman.subDistrictPengiriman) {
+ setValue(
+ 'subDistrictPengiriman',
+ parseInt(formPengiriman.subDistrictPengiriman)
+ );
+ }
+ }, [formPengiriman.subDistrictPengiriman]);
+ useEffect(() => {
+ if (formPengiriman.zipPengiriman) {
+ setValue('zipPengiriman', parseInt(formPengiriman.zipPengiriman));
+ }
+ }, [formPengiriman.zipPengiriman]);
+ useEffect(() => {
+ if (formPengiriman.stateInvoice) {
+ setValue('stateInvoice', parseInt(formPengiriman.stateInvoice));
+ }
+ }, [formPengiriman.stateInvoice]);
+ useEffect(() => {
+ if (formPengiriman.cityInvoice) {
+ setValue('cityInvoice', parseInt(formPengiriman.cityInvoice));
+ }
+ }, [formPengiriman.cityInvoice]);
+ useEffect(() => {
+ if (formPengiriman.districtInvoice) {
+ setValue('districtInvoice', parseInt(formPengiriman.districtInvoice));
+ }
+ }, [formPengiriman.districtInvoice]);
+ useEffect(() => {
+ if (formPengiriman.subDistrictInvoice) {
+ setValue(
+ 'subDistrictInvoice',
+ parseInt(formPengiriman.subDistrictInvoice)
+ );
+ }
+ }, [formPengiriman.subDistrictInvoice]);
+ useEffect(() => {
+ if (formPengiriman.zipInvoice) {
+ setValue('zipInvoice', parseInt(formPengiriman.zipInvoice));
+ }
+ }, [formPengiriman.zipInvoice]);
+
+ const getFromLocalStorage = (key) => {
+ const itemStr = localStorage.getItem(key);
+ if (!itemStr) return null;
+
+ const item = JSON.parse(itemStr);
+ return item;
+ };
+
+ // const cachedData = getFromLocalStorage('Pengiriman');
+ // useEffect(() => {
+ // if (cachedData) {
+ // setValue('cityPengiriman', parseInt(cachedData?.cityPengiriman));
+ // updateFormPengiriman('cityPengiriman', `${cachedData?.cityPengiriman}`);
+ // }
+ // if (cachedData?.statePengiriman) {
+ // setValue('statePengiriman', parseInt(cachedData?.statePengiriman));
+ // }
+ // if (cachedData?.stateInvoice) {
+ // setValue('stateInvoice', parseInt(cachedData?.stateInvoice));
+ // }
+ // if (cachedData?.cityInvoice) {
+ // setValue('cityInvoice', parseInt(cachedData?.cityInvoice));
+ // }
+ // if (cachedData?.isSameAddrees) {
+ // updateFormPengiriman('isSameAddrees', `${cachedData?.isSameAddrees}`);
+ // }
+ // validatePengiriman();
+ // }, [cachedData?.cityPengiriman]);
+ // useEffect(() => {
+ // if (cachedData?.everyWeek) {
+ // updateFormPengiriman('everyWeek', cachedData?.everyWeek);
+ // setEveryWeek(cachedData?.everyWeek);
+ // }
+ // if (cachedData?.everyWeekday) {
+ // updateFormPengiriman('everyWeekday', cachedData?.everyWeekday);
+ // setEveryWeekday(cachedData?.everyWeekday);
+ // }
+ // if (cachedData?.tukarInvoice) {
+ // updateFormPengiriman('tukarInvoice', cachedData?.tukarInvoice);
+ // setTukarInvoice(cachedData?.tukarInvoice);
+ // }
+ // if (cachedData?.everyWeekPembayaran) {
+ // updateFormPengiriman(
+ // 'everyWeekPembayaran',
+ // cachedData?.everyWeekPembayaran
+ // );
+ // setEveryWeekPembayaran(cachedData?.everyWeekPembayaran);
+ // }
+ // if (cachedData?.everyWeekdayPembayaran) {
+ // updateFormPengiriman(
+ // 'everyWeekdayPembayaran',
+ // cachedData?.everyWeekdayPembayaran
+ // );
+ // setEveryWeekdayPembayaran(cachedData?.everyWeekdayPembayaran);
+ // }
+ // if (cachedData?.tukarInvoicePembayaran) {
+ // updateFormPengiriman(
+ // 'tukarInvoicePembayaran',
+ // cachedData?.tukarInvoicePembayaran
+ // );
+ // setTukarInvoicePembayaran(cachedData?.tukarInvoicePembayaran);
+ // }
+ // validatePengiriman();
+ // }, [
+ // cachedData?.everyWeek,
+ // cachedData?.everyWeekday,
+ // cachedData?.tukarInvoice,
+ // cachedData?.everyWeekdayPembayaran,
+ // cachedData?.everyWeekPembayaran,
+ // cachedData?.tukarInvoicePembayaran,
+ // ]);
+ const handleChangeSameAddress = () => {
+ // setSameAddress(!sameAddress);
+ // if (sameAddress) {
+ // updateFormPengiriman('sameAddress', `true`);
+ // } else {
+ // }
+ updateFormPengiriman('isSameAddrees', `${!sameAddress}`);
+ validatePengiriman();
+ };
+ const handleChangeSameAddressStreet = () => {
+ // updateFormPengiriman('sameAddressStreet', `${!sameAddressStreet}`);
+ // setSameAddressStreet(!sameAddressStreet);
+ // if (sameAddressStreet == false) {
+ // updateFormPengiriman('streetPengiriman', '');
+ // updateFormPengiriman('statePengiriman', '');
+ // updateFormPengiriman('cityPengiriman', '');
+ // updateFormPengiriman('zipPengiriman', '');
+ // setValue('streetPengiriman', '');
+ // setValue('statePengiriman', '');
+ // setValue('cityPengiriman', '');
+ // }
+ updateFormPengiriman('isSameAddreesStreet', `${!sameAddressStreet}`);
+ validatePengiriman();
+ };
+
+ return (
+ <>
+ {isDesktop && (
+ <div>
+ <h1 className={`font-bold ${isKonfirmasi ? 'text-xl' : ''}`}>
+ Pengiriman
+ </h1>
+ <form className='flex flex-col w-full '>
+ <div className='w-full grid grid-row-2 gap-5'>
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ Nama PIC Penerimaan Barang
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex items-center border border-gray-300 rounded-md'>
+ <select
+ value={formPengiriman.PICTittle || ''}
+ id='PICTittle'
+ name='PICTittle'
+ className=' p-2 rounded-l-md'
+ onChange={handleInputChange}
+ >
+ <option value='' disabled>
+ Pilih
+ </option>
+ <option value='Bpk'>Bpk</option>
+ <option value='Ibu'>Ibu</option>
+ </select>
+ <input
+ value={formPengiriman.PICName}
+ id='PICName'
+ name='PICName'
+ placeholder='Masukkan nama pic penerimaan barang disini'
+ type='text'
+ className='form-input border-l border-r-0 border-t-0 border-b-0 border-gray-300 p-2 ml-2 flex-grow rounded-none'
+ aria-invalid={errorsPengiriman.PICName}
+ ref={PICNameRef}
+ aria-label='PICName'
+ onChange={handleInputChange}
+ />
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.PICName} {errorsPengiriman.PICTittle}
+ </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 && (
+ <span className='text-xs opacity-60'>
+ pastikan alamat yang anda isi sesuai dengan alamat kirim
+ barang
+ </span>
+ )}
+ </div>
+ <div className='w-3/5 flex gap-3 flex-col'>
+ <Checkbox
+ colorScheme='red'
+ isChecked={sameAddressStreet}
+ onChange={handleChangeSameAddressStreet}
+ >
+ Alamat Pengiriman sama dengan alamat perusahaan
+ </Checkbox>
+
+ <>
+ <div>
+ <input
+ id='streetPengiriman'
+ name='streetPengiriman'
+ ref={streetPengirimanRef}
+ aria-label='streetPengiriman'
+ placeholder='Masukkan alamat lengkap pengiriman barang'
+ type='text'
+ value={formPengiriman.streetPengiriman}
+ className='form-input'
+ onChange={handleInputChange}
+ disabled={sameAddressStreet}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.streetPengiriman}
+ </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='w-full'
+ ref={statePengirimanRef}
+ aria-label='statePengiriman'
+ >
+ <Controller
+ name='statePengiriman'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ disabled={sameAddressStreet}
+ options={states}
+ placeholder='Provinsi'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.statePengiriman}
+ </div>
+ )}
+ </div>
+ <div className='w-full' ref={cityPengirimanRef}>
+ <Controller
+ name='cityPengiriman'
+ aria-label='cityPengiriman'
+ disabled={sameAddressStreet}
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={cities}
+ disabled={!watchState || sameAddressStreet}
+ placeholder='Kota'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.cityPengiriman}
+ </div>
+ )}
+ </div>
+ </div>
+ <div
+ className={`flex-row flex gap-2 justify-between ${
+ isKonfirmasi ? 'w-full' : 'w-3/5'
+ }`}
+ >
+ <div className='w-full' ref={districtPengirimanRef}>
+ <Controller
+ name='districtPengiriman'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={districts}
+ disabled={
+ !watchState || !watchCity || sameAddressStreet
+ }
+ placeholder='Kelurahan'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.subDistrictPengiriman}
+ </div>
+ )}
+ </div>
+ <div className='w-full' ref={subDistrictPengirimanRef}>
+ <Controller
+ name='subDistrictPengiriman'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={subDistricts}
+ disabled={!watchDistrict || sameAddressStreet}
+ placeholder='Kelurahan'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.subDistrictPengiriman}
+ </div>
+ )}
+ </div>
+ <div className='w-full' ref={zipRef}>
+ <Controller
+ name='zipPengiriman'
+ control={control}
+ render={(props) => (
+ <>
+ {/* Jika zips tidak kosong, tampilkan dropdown */}
+ {zips.length > 0 ? (
+ <HookFormSelect
+ {...props}
+ options={zips}
+ disabled={
+ !watchsubDistrict || sameAddressStreet
+ }
+ placeholder='Zip'
+ />
+ ) : (
+ // Jika zips kosong, tampilkan input manual
+ <input
+ id='zipPengiriman'
+ name='zipPengiriman'
+ ref={zipRef}
+ placeholder='Kode Pos'
+ type='number'
+ disabled={
+ !watchsubDistrict || sameAddressStreet
+ }
+ value={formPengiriman.zipPengiriman}
+ className='form-input'
+ onChange={handleInputChange}
+ />
+ )}
+ </>
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.zipPengiriman}
+ </div>
+ )}
+ </div>
+ </div>
+ </div>
+ </>
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ Nama PIC Penerimaan Invoice
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='flex items-center border border-gray-300 rounded-md'>
+ <select
+ value={formPengiriman.invoicePicTittle || ''}
+ id='invoicePicTittle'
+ name='invoicePicTittle'
+ className=' p-2 rounded-l-md'
+ onChange={handleInputChange}
+ >
+ <option value='' disabled>
+ Pilih
+ </option>
+ <option value='Bpk'>Bpk</option>
+ <option value='Ibu'>Ibu</option>
+ </select>
+ <input
+ value={formPengiriman.invoicePic}
+ id='invoicePic'
+ name='invoicePic'
+ aria-label='invoicePic'
+ placeholder='Masukkan nama pic invoice disini'
+ type='text'
+ className='form-input border-l border-r-0 border-t-0 border-b-0 border-gray-300 p-2 ml-2 flex-grow rounded-none'
+ aria-invalid={errorsPengiriman.invoicePic}
+ ref={invoicePicRef}
+ onChange={handleInputChange}
+ />
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.invoicePic}{' '}
+ {errorsPengiriman.invoicePicTittle}
+ </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>
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ Pastikan alamat yang anda isi sesuai dengan alamat kirim
+ invoice
+ </span>
+ )}
+ </div>
+ <div className='w-3/5 flex gap-3 flex-col'>
+ <div>
+ <Checkbox
+ colorScheme='red'
+ isChecked={sameAddress}
+ onChange={handleChangeSameAddress}
+ >
+ Alamat invoice sama dengan alamat perusahaan
+ </Checkbox>
+ </div>
+
+ <>
+ <div>
+ <input
+ id='streetInvoice'
+ name='streetInvoice'
+ aria-label='streetInvoice'
+ ref={streetInvoiceRef}
+ disabled={sameAddress}
+ placeholder='Masukkan alamat lengkap pengiriman invoice'
+ type='text'
+ value={formPengiriman.streetInvoice}
+ className='form-input'
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.streetInvoice}
+ </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='w-full'
+ ref={stateInvoiceRef}
+ aria-label='stateInvoice'
+ >
+ <Controller
+ name='stateInvoice'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={states}
+ disabled={sameAddress}
+ placeholder='Provinsi'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.stateInvoice}
+ </div>
+ )}
+ </div>
+ <div
+ className='w-full'
+ ref={cityInvoiceRef}
+ aria-label='cityInvoice'
+ >
+ <Controller
+ name='cityInvoice'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={citiesInvoice}
+ disabled={!watchStateInvoice || sameAddress}
+ placeholder='Kota'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.cityInvoice}
+ </div>
+ )}
+ </div>
+ </div>
+ <div
+ className={`flex-row flex gap-2 justify-between ${
+ isKonfirmasi ? 'w-full' : 'w-3/5'
+ }`}
+ >
+ <div className='w-full' ref={districtInvoiceRef}>
+ <Controller
+ name='districtInvoice'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={districtsInvoice}
+ disabled={
+ !watchStateInvoice ||
+ !watchCityInvoice ||
+ sameAddress
+ }
+ placeholder='Kecamatan'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.districtInvoice}
+ </div>
+ )}
+ </div>
+ <div className='w-full' ref={subDistrictInvoiceRef}>
+ <Controller
+ name='subDistrictInvoice'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={subDistrictsInvoice}
+ disabled={!watchDistrictInvoice || sameAddress}
+ placeholder='Kelurahan'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.subDistrictsInvoice}
+ </div>
+ )}
+ </div>
+ <div className='w-full' ref={zipInvoiceRef}>
+ <Controller
+ name='zipInvoice'
+ control={control}
+ render={(props) => (
+ <>
+ {zipsInvoice.length > 0 ? (
+ <HookFormSelect
+ {...props}
+ options={zipsInvoice}
+ disabled={
+ !watchsubDistrictInvoice || sameAddress
+ }
+ placeholder='Zip'
+ />
+ ) : (
+ <input
+ id='zipInvoice'
+ name='zipInvoice'
+ ref={zipInvoiceRef}
+ placeholder='Kode Pos'
+ type='number'
+ disabled={
+ !watchsubDistrictInvoice || sameAddress
+ }
+ value={formPengiriman.zipInvoice}
+ className='form-input'
+ onChange={handleInputChange}
+ />
+ )}
+ </>
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.zipInvoice}
+ </div>
+ )}
+ </div>
+ </div>
+ </div>
+ </>
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-between items-start'>
+ <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
+ </span>
+ )}
+ </div>
+ <div className='w-3/5 flex gap-3 flex-col'>
+ <textarea
+ id='tukarInvoiceInput'
+ name='tukarInvoiceInput'
+ aria-label='tukarInvoiceInput'
+ placeholder='Masukkan jadwal penukaran invoice'
+ value={formPengiriman.tukarInvoiceInput}
+ className='form-input'
+ rows={4}
+ cols={40}
+ onChange={handleInputChange}
+ />
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-between items-start'>
+ <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
+ </span>
+ )}
+ </div>
+ <div className='w-3/5 flex gap-3 flex-col'>
+ <textarea
+ id='tukarInvoiceInputPembayaran'
+ name='tukarInvoiceInputPembayaran'
+ placeholder='Masukkan jadwal pembayaran'
+ value={formPengiriman.tukarInvoiceInputPembayaran}
+ className='form-input'
+ rows={4}
+ cols={40}
+ onChange={handleInputChange}
+ />
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-wrap'>
+ Apakah ada dokumen tanda terima yang diberikan pada saat
+ pengiriman barang?{' '}
+ <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ Pilih dokumen tanda terima saat pengiriman barang jika ada
+ </span>
+ )}
+ </div>
+ <div
+ className='w-3/5 flex gap-3 flex-col'
+ ref={dokumenPengirimanRef}
+ aria-label='dokumenPengirimanInput'
+ >
+ <Checkbox
+ colorScheme='red'
+ key='0'
+ isChecked={isChecked(0)}
+ onChange={() => handleCheckboxChange(0)}
+ >
+ Surat Tanda Terima Barang (STTB)
+ </Checkbox>
+ <Checkbox
+ colorScheme='red'
+ key='1'
+ isChecked={isChecked(1)}
+ onChange={() => handleCheckboxChange(1)}
+ >
+ Good Receipt (GR)
+ </Checkbox>
+ <Checkbox
+ colorScheme='red'
+ key='2'
+ isChecked={isChecked(2)}
+ onChange={() => handleCheckboxChange(2)}
+ >
+ Surat Terima Barang (STB)
+ </Checkbox>
+ <Checkbox
+ colorScheme='red'
+ key='3'
+ isChecked={isChecked(3)}
+ onChange={() => handleCheckboxChange(3)}
+ >
+ Lembar Penerimaan Barang (LPB)
+ </Checkbox>
+
+ <div className='flex gap-3 flex-col'>
+ <Checkbox
+ colorScheme='red'
+ key='4'
+ isChecked={
+ isChecked(4) || formPengiriman.dokumenKirimInput
+ }
+ onChange={() => handleCheckboxChange(4)}
+ >
+ Lainnya
+ </Checkbox>
+ <textarea
+ id='dokumenKirimInput'
+ name='dokumenKirimInput'
+ aria-label='dokumenKirimInput'
+ placeholder='isi manual dokumen yang anda mau'
+ type='textarea'
+ ref={dokumenPengirimanInputRef}
+ value={formPengiriman.dokumenKirimInput}
+ className='form-input'
+ rows={4}
+ cols={40}
+ onChange={handleInputChange}
+ />
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.dokumenPengiriman}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-wrap'>
+ Dokumen yang dibawa saat pengiriman barang
+ <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ Dokumen lampiran yang dibawa saat pengiriman barang
+ </span>
+ )}
+ </div>
+ <div className='flex gap-3 flex-col w-3/5 '>
+ <textarea
+ id='dokumenPengirimanInput'
+ name='dokumenPengirimanInput'
+ aria-label='dokumenPengirimanInput'
+ placeholder='isi manual dokumen yang anda mau'
+ type='textarea'
+ ref={dokumenPengirimanInputRef}
+ value={formPengiriman.dokumenPengirimanInput}
+ className='form-input'
+ rows={4}
+ cols={40}
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.dokumenPengiriman}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-wrap'>
+ Dokumen yang dilampirkan saat Pengiriman Invoice{' '}
+ <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ <span className='text-xs opacity-60'>
+ Dokumen lampiran saat pengiriman invoice
+ </span>
+ </div>
+ <div className='flex gap-3 flex-col w-3/5'>
+ <textarea
+ id='dokumenPengirimanInvoice'
+ aria-label='dokumenPengirimanInvoice'
+ name='dokumenPengirimanInvoice'
+ placeholder='isi manual dokumen yang anda mau'
+ type='textarea'
+ ref={dokumenPengirimanInvoiceRef}
+ value={formPengiriman.dokumenPengirimanInvoice}
+ className='form-input'
+ rows={4}
+ cols={40}
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.dokumenPengirimanInvoice}
+ </div>
+ )}
+ </div>
+ </div>
+ </div>
+ </form>
+ </div>
+ )}
+ {isMobile && (
+ <div className='text-sm'>
+ <h1
+ className={`font-bold py-4 mt-8 ${
+ isKonfirmasi ? 'hidden' : 'text-xl'
+ }`}
+ >
+ Pengiriman
+ </h1>
+ <form className='flex flex-col w-full '>
+ <div className='w-full grid grid-row-2 gap-2'>
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label text-nowrap'>
+ Nama PIC Penerimaan Barang
+ </label>
+ <div className='flex items-center border border-gray-300 rounded-md w-full'>
+ <select
+ value={formPengiriman.PICTittle || ''}
+ id='PICTittle'
+ name='PICTittle'
+ className=' p-2 rounded-l-md'
+ onChange={handleInputChange}
+ >
+ <option value='' disabled>
+ Pilih
+ </option>
+ <option value='Bpk'>Bpk</option>
+ <option value='Ibu'>Ibu</option>
+ </select>
+ <input
+ value={formPengiriman.PICName}
+ id='PICName'
+ name='PICName'
+ aria-label='PICName'
+ placeholder='Masukkan nama pic penerima barang disini'
+ type='text'
+ className='form-input border-l border-r-0 border-t-0 border-b-0 border-gray-300 p-2 ml-2 flex-grow rounded-none'
+ aria-invalid={errorsPengiriman.PICName}
+ ref={PICNameRef}
+ onChange={handleInputChange}
+ />
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.PICName} {errorsPengiriman.PICTittle}
+ </div>
+ )}
+ </div>
+
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label text-nowrap'>
+ Alamat Pengiriman Barang
+ </label>
+ <Checkbox
+ colorScheme='red'
+ isChecked={sameAddressStreet}
+ onChange={handleChangeSameAddressStreet}
+ size='sm'
+ >
+ Alamat Pengiriman sama dengan alamat perusahaan
+ </Checkbox>
+ <div className='w-full'>
+ <input
+ id='streetPengiriman'
+ name='streetPengiriman'
+ aria-label='streetPengiriman'
+ ref={streetPengirimanRef}
+ disabled={sameAddressStreet}
+ placeholder='Masukkan alamat lengkap pengiriman barang'
+ type='text'
+ value={formPengiriman.streetPengiriman}
+ className='form-input'
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.streetPengiriman}
+ </div>
+ )}
+ </div>
+ <div className='sub-alamat flex flex-row w-full gap-2'>
+ <div
+ className='w-2/5'
+ ref={statePengirimanRef}
+ aria-label='statePengiriman'
+ >
+ <Controller
+ name='statePengiriman'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ disabled={sameAddressStreet}
+ options={states}
+ placeholder='Provinsi'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.statePengiriman}
+ </div>
+ )}
+ </div>
+ <div
+ className='w-3/5'
+ ref={cityPengirimanRef}
+ aria-label='cityPengiriman'
+ >
+ <Controller
+ name='cityPengiriman'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={cities}
+ disabled={!watchState || sameAddressStreet}
+ placeholder='Kota'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.cityPengiriman}
+ </div>
+ )}
+ </div>
+ {/* <div className='w-1/3'>
+ <input
+ id='zipPengiriman'
+ aria-label='zipPengiriman'
+ name='zipPengiriman'
+ ref={zipRef}
+ placeholder='Kode Pos'
+ disabled={sameAddressStreet}
+ type='number'
+ value={formPengiriman.zipPengiriman}
+ className='form-input'
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.zipPengiriman}
+ </div>
+ )}
+ </div> */}
+ </div>
+ <div className='flex flex-row w-full gap-2'>
+ <div className='w-1/3' ref={districtPengirimanRef}>
+ <Controller
+ name='districtPengiriman'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={districts}
+ disabled={
+ !watchState || !watchCity || sameAddressStreet
+ }
+ placeholder='Kelurahan'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.subDistrictPengiriman}
+ </div>
+ )}
+ </div>
+ <div className='w-1/3' ref={subDistrictPengirimanRef}>
+ <Controller
+ name='subDistrictPengiriman'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={subDistricts}
+ disabled={!watchDistrict || sameAddressStreet}
+ placeholder='Kelurahan'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.subDistrictPengiriman}
+ </div>
+ )}
+ </div>
+ <div className='w-1/3' ref={zipRef}>
+ <Controller
+ name='zipPengiriman'
+ control={control}
+ render={(props) => (
+ <>
+ {/* Jika zips tidak kosong, tampilkan dropdown */}
+ {zips.length > 0 ? (
+ <HookFormSelect
+ {...props}
+ options={zips}
+ disabled={!watchsubDistrict || sameAddressStreet}
+ placeholder='Zip'
+ />
+ ) : (
+ // Jika zips kosong, tampilkan input manual
+ <input
+ id='zipPengiriman'
+ name='zipPengiriman'
+ ref={zipRef}
+ placeholder='Kode Pos'
+ type='number'
+ disabled={!watchsubDistrict || sameAddressStreet}
+ value={formPengiriman.zipPengiriman}
+ className='form-input'
+ onChange={handleInputChange}
+ />
+ )}
+ </>
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.zipPengiriman}
+ </div>
+ )}
+ </div>
+ </div>
+ </div>
+
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label text-nowrap'>
+ Nama PIC Penerimaan Invoice
+ </label>
+ <div className='flex items-center border border-gray-300 rounded-md w-full'>
+ <select
+ value={formPengiriman.invoicePicTittle || ''}
+ id='invoicePicTittle'
+ name='invoicePicTittle'
+ className=' p-2 rounded-l-md'
+ onChange={handleInputChange}
+ >
+ <option value='' disabled>
+ Pilih
+ </option>
+ <option value='Bpk'>Bpk</option>
+ <option value='Ibu'>Ibu</option>
+ </select>
+ <input
+ value={formPengiriman.invoicePic}
+ id='invoicePic'
+ aria-label='invoicePic'
+ name='invoicePic'
+ placeholder='Masukkan nama pic invoice disini'
+ type='text'
+ className='form-input border-l border-r-0 border-t-0 border-b-0 border-gray-300 p-2 ml-2 flex-grow rounded-none'
+ aria-invalid={errorsPengiriman.invoicePic}
+ ref={invoicePicRef}
+ onChange={handleInputChange}
+ />
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.invoicePic}{' '}
+ {errorsPengiriman.invoicePicTittle}
+ </div>
+ )}
+ </div>
+
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label text-nowrap'>
+ Alamat Pengiriman Invoice
+ </label>
+ <div>
+ <Checkbox
+ colorScheme='red'
+ isChecked={sameAddress}
+ onChange={handleChangeSameAddress}
+ size='sm'
+ >
+ Alamat invoice sama dengan alamat pengiriman
+ </Checkbox>
+ </div>
+
+ <>
+ <div className='w-full'>
+ <input
+ id='streetInvoice'
+ aria-label='streetInvoice'
+ name='streetInvoice'
+ ref={streetInvoiceRef}
+ disabled={sameAddress}
+ placeholder='Masukkan alamat lengkap pengiriman invoice'
+ type='text'
+ value={formPengiriman.streetInvoice}
+ className='form-input'
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.streetInvoice}
+ </div>
+ )}
+ </div>
+ <div className='sub-alamat flex flex-row w-full gap-3'>
+ <div
+ className='w-3/5'
+ ref={stateInvoiceRef}
+ aria-label='stateInvoice'
+ >
+ <Controller
+ name='stateInvoice'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={states}
+ disabled={sameAddress}
+ placeholder='Provinsi'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.stateInvoice}
+ </div>
+ )}
+ </div>
+ <div
+ className='w-2/5'
+ ref={cityInvoiceRef}
+ aria-label='cityInvoice'
+ >
+ <Controller
+ name='cityInvoice'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={cities}
+ disabled={!watchState || sameAddress}
+ placeholder='Kota'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.cityInvoice}
+ </div>
+ )}
+ </div>
+ </div>
+ <div className='sub-alamat flex flex-row w-full gap-3'>
+ <div className='w-1/3' ref={districtInvoiceRef}>
+ <Controller
+ name='districtInvoice'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={districtsInvoice}
+ disabled={
+ !watchStateInvoice ||
+ !watchCityInvoice ||
+ sameAddress
+ }
+ placeholder='Kecamatan'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.districtInvoice}
+ </div>
+ )}
+ </div>
+ <div className='w-1/3' ref={subDistrictInvoiceRef}>
+ <Controller
+ name='subDistrictInvoice'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={subDistrictsInvoice}
+ disabled={!watchDistrictInvoice || sameAddress}
+ placeholder='Kelurahan'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.subDistrictsInvoice}
+ </div>
+ )}
+ </div>
+ <div className='w-1/3' ref={zipInvoiceRef}>
+ <Controller
+ name='zipInvoice'
+ control={control}
+ render={(props) => (
+ <>
+ {zipsInvoice.length > 0 ? (
+ <HookFormSelect
+ {...props}
+ options={zipsInvoice}
+ disabled={
+ !watchsubDistrictInvoice || sameAddress
+ }
+ placeholder='Zip'
+ />
+ ) : (
+ <input
+ id='zipInvoice'
+ name='zipInvoice'
+ ref={zipInvoiceRef}
+ placeholder='Kode Pos'
+ type='number'
+ disabled={
+ !watchsubDistrictInvoice || sameAddress
+ }
+ value={formPengiriman.zipInvoice}
+ className='form-input'
+ onChange={handleInputChange}
+ />
+ )}
+ </>
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.zipInvoice}
+ </div>
+ )}
+ </div>
+ </div>
+ </>
+ </div>
+
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <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
+ </span>
+ )}
+ <div className='w-full flex gap-2 flex-col'>
+ <textarea
+ id='tukarInvoiceInput'
+ name='tukarInvoiceInput'
+ placeholder='Masukkan jadwal penukaran invoice'
+ type='textarea'
+ value={formPengiriman.tukarInvoiceInput}
+ className='form-input'
+ rows={4}
+ cols={40}
+ onChange={handleInputChange}
+ />
+ </div>
+ <div className='w-2/5'></div>
+ </div>
+
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <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
+ </span>
+ )}
+ <div className='w-full flex gap-2 flex-col'>
+ <textarea
+ id='tukarInvoiceInputPembayaran'
+ name='tukarInvoiceInputPembayaran'
+ placeholder='Masukkan jadwal pembayaran'
+ type='textarea'
+ value={formPengiriman.tukarInvoiceInputPembayaran}
+ className='form-input'
+ rows={4}
+ cols={40}
+ onChange={handleInputChange}
+ />
+ </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
+ pengiriman barang?{' '}
+ <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ Pilih dokumen lampiran saat pengiriman barang
+ </span>
+ )}
+ <div
+ className='w-full flex gap-2 flex-col'
+ ref={dokumenPengirimanRef}
+ aria-label='dokumenPengirimanInput'
+ >
+ <Checkbox
+ size='sm'
+ colorScheme='red'
+ key='0'
+ isChecked={isChecked(0)}
+ onChange={() => handleCheckboxChange(0)}
+ >
+ Surat Tanda Terima Barang (STTB)
+ </Checkbox>
+ <Checkbox
+ size='sm'
+ colorScheme='red'
+ key='1'
+ isChecked={isChecked(1)}
+ onChange={() => handleCheckboxChange(1)}
+ >
+ Good Receipt (GR)
+ </Checkbox>
+ <Checkbox
+ size='sm'
+ colorScheme='red'
+ key='2'
+ isChecked={isChecked(2)}
+ onChange={() => handleCheckboxChange(2)}
+ >
+ Surat Terima Barang (STB)
+ </Checkbox>
+ <Checkbox
+ size='sm'
+ colorScheme='red'
+ key='3'
+ isChecked={isChecked(3)}
+ onChange={() => handleCheckboxChange(3)}
+ >
+ Lembar Penerimaan Barang (LPB)
+ </Checkbox>
+ <div className='flex gap-3 flex-col'>
+ <Checkbox
+ colorScheme='red'
+ key='4'
+ isChecked={
+ isChecked(4) || formPengiriman.dokumenKirimInput
+ }
+ onChange={() => handleCheckboxChange(4)}
+ >
+ Lainnya
+ </Checkbox>
+ <textarea
+ id='dokumenKirimInput'
+ name='dokumenKirimInput'
+ aria-label='dokumenKirimInput'
+ placeholder='isi manual dokumen yang anda mau'
+ type='textarea'
+ ref={dokumenPengirimanInputRef}
+ value={formPengiriman.dokumenKirimInput}
+ className='form-input'
+ rows={4}
+ cols={40}
+ onChange={handleInputChange}
+ />
+ </div>
+
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.dokumenPengiriman}
+ </div>
+ )}
+ </div>
+ </div>
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label text-wrap'>
+ Dokumen yang dibawa saat pengiriman barang
+ <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ Dokumen lampiran yang dibawa saat pengiriman barang
+ </span>
+ )}
+ <div className='flex gap-3 flex-col w-full'>
+ <textarea
+ id='dokumenPengirimanInput'
+ aria-label='dokumenPengirimanInput'
+ name='dokumenPengirimanInput'
+ placeholder='isi manual dokumen yang anda mau'
+ type='textarea'
+ ref={dokumenPengirimanInputRef}
+ value={formPengiriman.dokumenPengirimanInput}
+ className='form-input'
+ rows={4}
+ cols={40}
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.dokumenPengiriman}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label text-wrap'>
+ Dokumen yang dilampirkan saat Pengiriman Invoice
+ <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ <span className='text-xs opacity-60'>
+ Dokumen lampiran saat pengiriman invoice
+ </span>
+ <div className='flex gap-3 flex-col w-full'>
+ <textarea
+ id='dokumenPengirimanInvoice'
+ aria-label='dokumenPengirimanInvoice'
+ name='dokumenPengirimanInvoice'
+ placeholder='isi manual dokumen yang anda mau'
+ type='textarea'
+ ref={dokumenPengirimanInvoiceRef}
+ value={formPengiriman.dokumenPengirimanInvoice}
+ className='form-input'
+ rows={4}
+ cols={40}
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errorsPengiriman.dokumenInvoicePengiriman}
+ </div>
+ )}
+ </div>
+ <div className='w-2/5'></div>
+ </div>
+ </div>
+ </form>
+ </div>
+ )}
+ </>
+ );
+};
+
+export default Pengiriman;
diff --git a/src/lib/pengajuan-tempo/component/Referensi.jsx b/src/lib/pengajuan-tempo/component/Referensi.jsx
new file mode 100644
index 00000000..8db321d1
--- /dev/null
+++ b/src/lib/pengajuan-tempo/component/Referensi.jsx
@@ -0,0 +1,636 @@
+import React, { useState, useEffect, useMemo, useRef } from 'react';
+import { useForm } from 'react-hook-form';
+import {
+ usePengajuanTempoStoreSupplier,
+ usePengajuanTempoStore,
+} from '../../../../src-migrate/modules/register/stores/usePengajuanTempoStore';
+import * as Yup from 'yup';
+import { yupResolver } from '@hookform/resolvers/yup';
+import { PlusCircleIcon } from '@heroicons/react/24/outline';
+import useDevice from '@/core/hooks/useDevice';
+import { Trash2Icon } from 'lucide-react';
+import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/24/outline';
+import BottomPopup from '@/core/components/elements/Popup/BottomPopup';
+const initialData = [];
+const Referensi = ({ chekValid, buttonSubmitClick, data }) => {
+ const [changeConfirmation, setChangeConfirmation] = useState(false);
+ const [selectedIndex, setSelectedIndex] = useState(null);
+
+ const { isDesktop, isMobile } = useDevice();
+ const [openIndexes, setOpenIndexes] = useState([]);
+
+ const {
+ register,
+ formState: { errors },
+ handleSubmit,
+ watch,
+ setValue,
+ control,
+ } = useForm({
+ resolver: yupResolver(validationSchema),
+ defaultValues,
+ });
+ const { formSupplier, updateFormSupplier, updateHasSave, hasSavedata } =
+ usePengajuanTempoStoreSupplier();
+ const { form } = usePengajuanTempoStore();
+ const [formData, setFormData] = useState([
+ {
+ supplier: '',
+ pic: '',
+ telepon: '',
+ durasiTempo: '',
+ creditLimit: '',
+ },
+ ]);
+
+ const [buttonSubmit, setButtonSubmit] = useState(false);
+ const [supplierData, setSupplierData] = useState(initialData);
+ const [newSupplier, setNewSupplier] = useState({
+ supplier: '',
+ pic: '',
+ telepon: '',
+ durasiTempo: '',
+ creditLimit: '',
+ });
+ const onChangeInput = (e, index) => {
+ updateHasSave(false);
+ const { name, value } = e.target;
+
+ let formattedValue = value;
+
+ if (name === 'durasiTempo') {
+ formattedValue = value.replace(/\s*Hari\s*/g, '');
+ } else if (name === 'creditLimit') {
+ formattedValue = value.replace(/^Rp\s*/, '');
+ }
+
+ const editData = supplierData.map((item, i) =>
+ i === index ? { ...item, [name]: formattedValue } : item
+ );
+ setSupplierData(editData);
+ if (value == '' && supplierData.supplier) {
+ updateHasSave(true);
+ }
+ };
+
+ const handleNewSupplierChange = (e) => {
+ updateHasSave(false);
+ const { name, value } = e.target;
+
+ let formattedValue = value;
+
+ if (name === 'durasiTempo') {
+ formattedValue = value.replace(/\s*Hari\s*/g, '');
+ } else if (name === 'creditLimit') {
+ formattedValue = value.replace(/^Rp\s*/, '');
+ }
+
+ const updatedSupplier = { ...newSupplier, [name]: formattedValue };
+ setNewSupplier(updatedSupplier);
+ if (value == '') {
+ updateHasSave(true);
+ }
+ };
+
+ useEffect(() => {
+ const isAllFieldsEmpty = Object.values(newSupplier).every(
+ (value) => value === ''
+ );
+ if (isAllFieldsEmpty) {
+ updateHasSave(true);
+ } else {
+ updateHasSave(false);
+ }
+ }, [newSupplier]);
+
+ const handleDeleteSupplier = () => {
+ if (selectedIndex !== null) {
+ // Logika untuk menghapus supplier
+ const updatedSuppliers = supplierData.filter(
+ (_, idx) => idx !== selectedIndex
+ );
+ setSupplierData(updatedSuppliers);
+ setChangeConfirmation(false);
+ updateHasSave(false);
+ }
+ };
+
+ const handleAddNewSupplier = () => {
+ // updateHasSave(false);
+ if (Object.values(newSupplier).every((val) => val.trim() !== '')) {
+ setSupplierData((prevData) => {
+ const newData = [...prevData, newSupplier];
+ return newData;
+ });
+
+ setNewSupplier({
+ supplier: '',
+ pic: '',
+ telepon: '',
+ durasiTempo: '',
+ creditLimit: '',
+ });
+ }
+ };
+
+ useEffect(() => {
+ handleAddNewSupplier();
+ updateFormSupplier(supplierData);
+ setButtonSubmit(!buttonSubmit);
+ }, [buttonSubmitClick]);
+ const simpanData = () => {
+ setButtonSubmit(!buttonSubmit);
+ if (Object.values(newSupplier).every((val) => val.trim() !== '')) {
+ setSupplierData((prevData) => {
+ const newData = [...prevData, newSupplier];
+ return newData;
+ });
+
+ setNewSupplier({
+ supplier: '',
+ pic: '',
+ telepon: '',
+ durasiTempo: '',
+ creditLimit: '',
+ });
+ }
+ updateHasSave(true);
+ };
+ const formatRupiah = (value) => {
+ if (!value) return '';
+ const numberString = value.replace(/[^0-9]/g, '');
+ return numberString
+ ? 'Rp ' + new Intl.NumberFormat('id-ID').format(numberString)
+ : '';
+ };
+ const formatHari = (value) => {
+ if (!value) return '';
+
+ const numberString = value.replace(/[^0-9]/g, '');
+
+ return numberString ? numberString.replace(/Hari/g, '') + ' Hari' : '';
+ };
+
+ useEffect(() => {
+ updateFormSupplier(supplierData);
+ }, [buttonSubmit]);
+ const getFromLocalStorage = (key) => {
+ const itemStr = localStorage.getItem(key);
+ if (!itemStr) return null;
+
+ const item = JSON.parse(itemStr);
+ return item;
+ };
+ useEffect(() => {
+ // const cachedData = getFromLocalStorage('Referensi');
+ if (data) {
+ setSupplierData(data);
+ updateFormSupplier(data);
+ }
+ }, [buttonSubmitClick]);
+
+ useEffect(() => {
+ setOpenIndexes(supplierData.map((_, index) => index));
+ }, [supplierData]);
+
+ const toggleOpen = (index) => {
+ setOpenIndexes((prev) =>
+ prev.includes(index) ? prev.filter((i) => i !== index) : [...prev, index]
+ );
+ };
+
+ const handleOpenConfirmation = (index) => {
+ setSelectedIndex(index);
+ setChangeConfirmation(true);
+ };
+ return (
+ <>
+ <BottomPopup
+ active={changeConfirmation}
+ close={() => setChangeConfirmation(false)}
+ title='Konfirmasi Hapus Data'
+ >
+ <div className='leading-7 text-gray_r-12/80'>
+ Apakah anda yakin menghapus data?
+ </div>
+ <div className='flex mt-6 gap-x-4 md:justify-end'>
+ <button
+ className='btn-solid-red flex-1 md:flex-none'
+ type='button'
+ onClick={handleDeleteSupplier}
+ >
+ Ya, Hapus
+ </button>
+ <button
+ className='btn-light flex-1 md:flex-none'
+ type='button'
+ onClick={() => setChangeConfirmation(false)}
+ >
+ Batal
+ </button>
+ </div>
+ </BottomPopup>
+ {isDesktop && (
+ <div className='py-4'>
+ <div className='flex flex-col justify-start'>
+ <h1 className='font-bold text-2xl'>
+ Referensi Supplier / Rekanan Bisnis Perusahaan{' '}
+ <span className=' opacity-60 text-xl'>(Opsional)</span>
+ </h1>
+ <p className='opacity-60'>
+ Data yang anda berikan hanya untuk bahan referensi internal kami
+ untuk memberikan anda credit limit dan durasi tempo
+ </p>
+ </div>
+ <form className='flex mt-4 flex-col w-full '>
+ <table className='border' border='1' cellPadding='10'>
+ <thead>
+ <tr className='border '>
+ <th className='text-left px-5 py-2'>
+ Nama Supplier / Rekanan
+ </th>
+ <th className='text-left px-5 py-2'>PIC</th>
+ <th className='text-left px-5 py-2'>Telepon</th>
+ <th className='text-left px-5 py-2'>Durasi Tempo</th>
+ <th className='text-left px-5 py-2'>Credit Limit</th>
+ </tr>
+ </thead>
+ <tbody>
+ {supplierData.map((supplier, index) => (
+ <tr key={index}>
+ <td>
+ <input
+ name='supplier'
+ value={supplier.supplier}
+ type='text'
+ onChange={(e) => onChangeInput(e, index)}
+ className='form-input border px-4 py-2'
+ placeholder='Type Supplier'
+ />
+ </td>
+ <td>
+ <input
+ name='pic'
+ value={supplier.pic}
+ type='text'
+ className='form-input border px-4 py-2'
+ onChange={(e) => onChangeInput(e, index)}
+ placeholder='Type PIC'
+ />
+ </td>
+ <td>
+ <input
+ name='telepon'
+ type='text'
+ className='form-input border px-4 py-2'
+ value={supplier.telepon}
+ onChange={(e) => onChangeInput(e, index)}
+ placeholder='Type Telepon'
+ />
+ </td>
+ <td>
+ <input
+ name='durasiTempo'
+ type='text'
+ className='form-input border px-4 py-2'
+ value={formatHari(supplier.durasiTempo)}
+ onChange={(e) => onChangeInput(e, index)}
+ placeholder='Type Durasi Tempo'
+ />
+ </td>
+ <td className='flex flex-row gap-2 justify-center items-center'>
+ <input
+ name='creditLimit'
+ type='text'
+ value={formatRupiah(supplier.creditLimit)}
+ className='form-input border px-4 py-2'
+ onChange={(e) => onChangeInput(e, index)}
+ placeholder='Type Credit Limit'
+ />
+ <Trash2Icon
+ size={18}
+ onClick={() => handleOpenConfirmation(index)}
+ className='cursor-pointer'
+ />
+ </td>
+ </tr>
+ ))}
+ <tr>
+ <td>
+ <input
+ name='supplier'
+ value={newSupplier.supplier}
+ type='text'
+ className='form-input border px-4 py-2'
+ onChange={handleNewSupplierChange}
+ placeholder='Isi nama supplier anda'
+ />
+ </td>
+ <td>
+ <input
+ name='pic'
+ value={newSupplier.pic}
+ type='text'
+ className='form-input border px-4 py-2'
+ onChange={handleNewSupplierChange}
+ placeholder='Isi PIC supplier anda'
+ />
+ </td>
+ <td>
+ <input
+ name='telepon'
+ value={newSupplier.telepon}
+ type='text'
+ onChange={handleNewSupplierChange}
+ placeholder='Isi telepon supplier anda'
+ className='form-input border px-4 py-2'
+ />
+ </td>
+ <td>
+ <input
+ name='durasiTempo'
+ value={formatHari(newSupplier.durasiTempo)}
+ type='text'
+ onChange={handleNewSupplierChange}
+ className='form-input border px-4 py-2'
+ placeholder='Durasi jatuh tempo'
+ />
+ </td>
+ <td className='flex flex-row items-center gap-2 '>
+ <input
+ name='creditLimit'
+ value={formatRupiah(newSupplier.creditLimit)}
+ type='text'
+ className='form-input border px-4 py-2'
+ onChange={handleNewSupplierChange}
+ placeholder='limit kredit'
+ />
+ {/* <Trash2Icon
+ size={18}
+ onClick={() => handleDeleteSupplier(index)}
+ /> */}
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </form>
+ <div className='flex items-center gap-4 mt-8'>
+ {/* <button
+ onClick={handleAddNewSupplier}
+ className='bg-gray-200 border border-gray-500 rounded-md text-sm text-gray-500 p-2 h-11 mb-1 content-center flex flex-row justify-center items-center'
+ >
+ {<PlusCircleIcon className='w-5 mr-2' />}
+ {''} Tambah Data Baru
+ </button> */}
+ {!hasSavedata && (
+ <>
+ <button
+ onClick={simpanData}
+ className={`bg-red-500 border border-red-500 rounded-md text-sm text-white p-2 h-11 mb-1 content-center flex flex-row justify-center items-center `}
+ >
+ Simpan Data
+ </button>
+ <span className='text-sm opacity-60 text-red-500'>
+ *Klik simpan sebelum lanjut ke tahap selanjutnya
+ </span>
+ </>
+ )}
+ </div>
+ </div>
+ )}
+ {isMobile && (
+ <div className='text-sm'>
+ <div className='flex flex-col py-4 mt-8 justify-start'>
+ <h1 className='font-bold text-xl'>
+ Referensi Supplier / Rekanan Bisnis Perusahaan{' '}
+ <span className=' opacity-60 text-xl'>(Opsional)</span>
+ </h1>
+ <p className='opacity-60'>
+ Data yang anda berikan hanya untuk bahan referensi internal kami
+ untuk memberikan anda credit limit dan durasi tempo
+ </p>
+ </div>
+ <div className='flex gap-4 flex-col'>
+ <div className='h-[2px] bg-gray-300 w-[120%] inset-0 relative transform -translate-x-5'></div>
+ <h2 className='py-2 font-semibold text-base'>
+ Daftar Nama Supplier
+ </h2>
+ {/* <div className='h-[2px] bg-gray-300 w-[120%] inset-0 relative transform -translate-x-5'></div> */}
+ <div className=''>
+ {supplierData.map((supplier, index) => (
+ <div key={index}>
+ <div
+ className='flex flex-row justify-center items-center py-4'
+ onClick={() => toggleOpen(index)}
+ >
+ <p className='font-semibold text-base w-4/5'>
+ {supplier.supplier}
+ </p>
+ <div className='w-1/5 flex justify-end items-center gap-2'>
+ <Trash2Icon
+ size={16}
+ color='red'
+ onClick={() => handleOpenConfirmation(index)}
+ className='cursor-pointer'
+ />
+ {openIndexes.includes(index) ? (
+ <ChevronUpIcon className='w-4' />
+ ) : (
+ <ChevronDownIcon className='w-4' />
+ )}
+ </div>
+ </div>
+ {openIndexes.includes(index) && (
+ <form className='flex flex-col w-full'>
+ <div className='w-full grid grid-row-2 gap-4'>
+ <div className='flex flex-row justify-start items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ Nama Supplier
+ </label>
+ </div>
+ <div className='w-3/5 opacity-70'>
+ {supplier.supplier}
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-start items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ PIC
+ </label>
+ </div>
+ <div className='w-3/5 opacity-70'>{supplier.pic}</div>
+ </div>
+
+ <div className='flex flex-row justify-start items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ Telepon
+ </label>
+ </div>
+ <div className='w-3/5 opacity-70'>
+ {supplier.telepon}
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-start items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ Durasi Tempo
+ </label>
+ </div>
+ <div className='w-3/5 opacity-70'>
+ {formatHari(supplier.durasiTempo)}
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-start items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ Kredit Limit
+ </label>
+ </div>
+ <div className='w-3/5 opacity-70'>
+ {formatRupiah(supplier.creditLimit)}
+ </div>
+ </div>
+ </div>
+ </form>
+ )}
+ </div>
+ ))}
+ </div>
+ {/* <div className='h-[2px] bg-gray-300 w-[120%] inset-0 relative transform -translate-x-5'></div> */}
+ {/* <h2 className='py-2 font-semibold text-base text-red-500 flex flex-row'>
+ <PlusCircleIcon className='w-5 mr-2' />
+ Tambah Data Baru
+ </h2> */}
+ <div className='h-[2px] bg-gray-300 w-[120%] inset-0 relative transform -translate-x-5'></div>
+ <form className='flex flex-col w-full'>
+ <div className='w-full grid grid-row-2 gap-2'>
+ <div className='flex flex-row justify-start items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ Nama Supplier
+ </label>
+ </div>
+ <div className='w-3/5 opacity-70'>
+ <input
+ name='supplier'
+ value={newSupplier.supplier}
+ type='text'
+ className='form-input border px-4 py-2'
+ onChange={handleNewSupplierChange}
+ placeholder='Format: PT. ABC TESTING INDONESIA'
+ />
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-start items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>PIC</label>
+ </div>
+ <div className='w-3/5 opacity-70'>
+ <input
+ name='pic'
+ value={newSupplier.pic}
+ type='text'
+ className='form-input border px-4 py-2'
+ onChange={handleNewSupplierChange}
+ placeholder='John Doe'
+ />
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-start items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>Telepon</label>
+ </div>
+ <div className='w-3/5 opacity-70'>
+ <input
+ name='telepon'
+ value={newSupplier.telepon}
+ type='text'
+ onChange={handleNewSupplierChange}
+ placeholder='Format: 08123456789'
+ className='form-input border px-4 py-2'
+ />
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-start items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ Durasi Tempo
+ </label>
+ </div>
+ <div className='w-3/5 opacity-70'>
+ <input
+ name='durasiTempo'
+ value={formatHari(newSupplier.durasiTempo)}
+ type='text'
+ onChange={handleNewSupplierChange}
+ className='form-input border px-4 py-2'
+ placeholder='Isi durasi tempo supplier anda (hari)'
+ />
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-start items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ Kredit Limit
+ </label>
+ </div>
+ <div className='w-3/5 opacity-70'>
+ <input
+ name='creditLimit'
+ value={formatRupiah(newSupplier.creditLimit)}
+ type='text'
+ className='form-input border px-4 py-2'
+ onChange={handleNewSupplierChange}
+ placeholder='Rp 999.999.999'
+ />
+ </div>
+ </div>
+ </div>
+ </form>
+ </div>
+ <div className='flex flex-col justify-start items-start gap-4 mt-8'>
+ {!hasSavedata && (
+ <>
+ <span className='text-xs opacity-60 text-red-500'>
+ *Klik simpan sebelum lanjut ke tahap selanjutnya
+ </span>
+ <button
+ onClick={simpanData}
+ className={`bg-red-500 border border-red-500 rounded-md w-full text-sm text-white p-2 h-11 mb-1 content-center flex flex-row justify-center items-center`}
+ >
+ Simpan Data
+ </button>
+ </>
+ )}
+ </div>
+ </div>
+ )}
+ </>
+ );
+};
+
+const validationSchema = Yup.object().shape({
+ supplier: Yup.string().required('Harus di-isi'),
+ pic: Yup.string().required('Harus di-isi'),
+ telepon: Yup.string().required('Harus di-isi'),
+ durasiTempo: Yup.string().required('Harus di-isi'),
+ creditLimit: Yup.string().required('Harus di-isi'),
+});
+
+const defaultValues = {
+ supplier: '',
+ pic: '',
+ telepon: '',
+ durasiTempo: '',
+ creditLimit: '',
+};
+export default Referensi;
diff --git a/src/lib/pengajuan-tempo/component/Stepper.jsx b/src/lib/pengajuan-tempo/component/Stepper.jsx
new file mode 100644
index 00000000..c5efa5bc
--- /dev/null
+++ b/src/lib/pengajuan-tempo/component/Stepper.jsx
@@ -0,0 +1,65 @@
+import React from 'react';
+import useDevice from '@/core/hooks/useDevice';
+const Stepper = ({ currentStep, numberOfSteps }) => {
+ const { isDesktop, isMobile } = useDevice();
+ const stepLabels = [
+ 'Informasi Perusahaan',
+ 'Kontak Person',
+ 'Pengiriman',
+ 'Referensi',
+ 'Dokumen',
+ 'Konfirmasi',
+ ];
+ const activeColor = (index) =>
+ currentStep >= index ? 'bg-red-500' : 'bg-gray-300';
+ const activeColorBullet = (index) =>
+ currentStep >= index ? 'bg-red-500 ' : 'bg-white border-gray-300 border';
+ const isFinalStep = (index) => index === numberOfSteps - 1;
+ const isFirstStep = (index) => index === 0;
+ return (
+ <div className='flex items-center'>
+ {Array.from({ length: numberOfSteps }).map((_, index) => (
+ <React.Fragment key={index}>
+ {isFirstStep(index) ? null : (
+ <div
+ className={`${isMobile ? 'w-12' : 'w-48'} h-[1px] ${activeColor(
+ index
+ )}`}
+ ></div>
+ )}
+ <div
+ className={`w-6 h-6 ${
+ currentStep == index
+ ? 'border-red-500 border'
+ : `${activeColorBullet(index)} `
+ } rounded-full flex justify-center items-center text-nowrap`}
+ >
+ <div className='relative text-xs'>
+ <div
+ className={`absolute z-10 ${
+ isMobile
+ ? `w-12 h-full top-4 ${
+ isFinalStep(index) ? '-left-16' : '-left-4'
+ }`
+ : 'w-48 h-full -top-14 -left-24'
+ } `}
+ >
+ <div
+ className={`relative w-full max-w-md p-2 text-center ${
+ currentStep == index
+ ? 'text-red-500'
+ : `${isMobile ? 'hidden' : ''}`
+ } text-nowrap`}
+ >
+ {stepLabels[index]}
+ </div>
+ </div>
+ </div>
+ </div>
+ </React.Fragment>
+ ))}
+ </div>
+ );
+};
+
+export default Stepper;
diff --git a/src/lib/pengajuan-tempo/component/informasiPerusahaan.jsx b/src/lib/pengajuan-tempo/component/informasiPerusahaan.jsx
new file mode 100644
index 00000000..25a3a7ee
--- /dev/null
+++ b/src/lib/pengajuan-tempo/component/informasiPerusahaan.jsx
@@ -0,0 +1,1630 @@
+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 odooApi from '~/libs/odooApi';
+import stateApi from '@/lib/address/api/stateApi.js';
+import cityApi from '@/lib/address/api/cityApi';
+import districtApi from '@/lib/address/api/districtApi';
+import subDistrictApi from '@/lib/address/api/subDistrictApi';
+import { Radio, RadioGroup, Stack, Checkbox } from '@chakra-ui/react';
+import { usePengajuanTempoStore } from '../../../../src-migrate/modules/register/stores/usePengajuanTempoStore';
+import useDevice from '@/core/hooks/useDevice';
+import Divider from '@/core/components/elements/Divider/Divider';
+import { getAuth } from '~/libs/auth';
+import addressApi from '@/lib/address/api/addressApi';
+import { toast } from 'react-hot-toast';
+import BottomPopup from '@/core/components/elements/Popup/BottomPopup';
+import { useRouter } from 'next/router';
+const InformasiPerusahaan = ({
+ chekValid,
+ buttonSubmitClick,
+ isKonfirmasi,
+}) => {
+ const auth = getAuth();
+ const router = useRouter();
+ const { isDesktop, isMobile } = useDevice();
+ const { control, watch, setValue, getValues } = useForm();
+ const { form, errors, validate, updateForm } = usePengajuanTempoStore();
+ const [states, setState] = useState([]);
+ const [cities, setCities] = useState([]);
+ const [districts, setDistricts] = useState([]);
+ const [subDistricts, setSubDistricts] = useState([]);
+ const [zips, setZips] = useState([]);
+ const [industries, setIndustries] = useState([]);
+ const [paymentTerm, setPaymentTerm] = useState([]);
+ const [selectedCategory, setSelectedCategory] = useState('');
+ const [bersedia, setBersedia] = useState(null);
+ const [changeConfirmation, setChangeConfirmation] = useState(false);
+ const category_produk = [
+ { id: 2040, name: 'Pengaman, Kesehatan & Keamanan' },
+ { id: 2097, name: 'Perkakas Tangan, Listrik & Pneumatic' },
+ { id: 2161, name: 'Mesin Industrial' },
+ { id: 2222, name: 'Mesin Pertanian & Perkebunan' },
+ { id: 2246, name: 'Mesin Pembersih & Janitorial' },
+ { id: 2273, name: 'Cairan Berbahan Kimia' },
+ { id: 2315, name: 'Perlengkapan Pengukuran & Pengujian' },
+ { id: 2354, name: 'Peralatan Listrik & Elektronik' },
+ { id: 2394, name: 'Perlengkapan Logistik & Gudang' },
+ { id: 2420, name: 'Peralatan Kantor & Stationery' },
+ { id: 2445, name: 'Komponen & Aksesoris' },
+ { id: 2477, name: 'Peralatan Horeca & Food Service' },
+ ];
+ const radioOptions = [
+ { label: '5.000.000', value: '5000000' },
+ { label: '10.000.000', value: '10000000' },
+ { label: '15.000.000', value: '15000000' },
+ { label: '20.000.000', value: '20000000' },
+ { label: '25.000.000', value: '25000000' },
+ { label: '30.000.000', value: '30000000' },
+ { label: '35.000.000', value: '35000000' },
+ ];
+
+ useEffect(() => {
+ const loadState = async () => {
+ let dataState = await stateApi({ tempo: true });
+ dataState = dataState.map((state) => ({
+ value: state.id,
+ label: state.name,
+ }));
+ setState(dataState);
+ };
+ loadState();
+ }, []);
+
+ const watchState = watch('state');
+ useEffect(() => {
+ if (watchState) {
+ updateForm('state', `${watchState}`);
+ validate();
+ const loadCities = async () => {
+ let dataCities = await cityApi({ stateId: watchState });
+ dataCities = dataCities.map((city) => ({
+ value: city.id,
+ label: city.name,
+ }));
+ setCities(dataCities);
+ };
+ loadCities();
+ }
+ }, [watchState]);
+
+ const watchCity = watch('city');
+
+ // Untuk memperbarui form.city
+ useEffect(() => {
+ if (watchCity && form.city !== `${watchCity}`) {
+ updateForm('city', `${watchCity}`);
+ validate();
+ }
+ }, [watchCity]);
+
+ // Untuk memuat distrik
+ useEffect(() => {
+ if (watchCity) {
+ 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(() => {
+ setValue('subDistrict', '');
+ if (watchDistrict) {
+ updateForm('district', `${watchDistrict}`);
+ validate();
+ const loadSubDistricts = async () => {
+ let dataSubDistricts = await subDistrictApi({
+ districtId: watchDistrict,
+ });
+ dataSubDistricts = dataSubDistricts.map((district) => ({
+ value: district.id,
+ label: district.name,
+ }));
+ setSubDistricts(dataSubDistricts);
+ };
+ loadSubDistricts();
+ }
+ }, [watchDistrict, setValue]);
+
+ const watchsubDistrict = watch('subDistrict');
+
+ useEffect(() => {
+ let kelurahan = '';
+ let kecamatan = '';
+
+ if (watchDistrict) {
+ for (const data in districts) {
+ if (districts[data].value == watchDistrict) {
+ kecamatan = districts[data].label.toLowerCase();
+ }
+ }
+ }
+
+ if (watchsubDistrict) {
+ updateForm('subDistrict', `${watchsubDistrict}`);
+ validate();
+ 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, setValue, subDistricts]);
+
+ const watchZip = watch('zip');
+ useEffect(() => {
+ if (watchZip) {
+ updateForm('zip', `${watchZip}`);
+ validate();
+ }
+ }, [watchZip]);
+
+ useEffect(() => {
+ const loadIndustries = async () => {
+ const dataIndustries = await odooApi('GET', '/api/v1/partner/industry');
+ setIndustries(
+ dataIndustries?.map((o) => ({
+ value: o.id,
+ label: o.name,
+ category: o.category,
+ }))
+ );
+ };
+ loadIndustries();
+ }, []);
+ useEffect(() => {
+ const selectedIndustryType = industries.find(
+ (industry) => industry.value === watch('industryId')
+ );
+ if (selectedIndustryType) {
+ updateForm('industryId', `${selectedIndustryType?.value}`);
+ validate();
+ setSelectedCategory(selectedIndustryType.category);
+ }
+ }, [watch('industryId'), industries]);
+
+ useEffect(() => {
+ const loadPaymentTerm = async () => {
+ const dataPaymentTerm = [
+ { id: 29, name: 'Tempo 7 Hari' },
+ { id: 24, name: 'Tempo 14 Hari' },
+ { id: 32, name: 'Tempo 21 Hari' },
+ { id: 25, name: 'Tempo 30 Hari' },
+ ];
+ setPaymentTerm(
+ dataPaymentTerm?.map((o) => ({
+ value: o.id,
+ label: o.name,
+ }))
+ );
+ };
+ loadPaymentTerm();
+ }, []);
+ useEffect(() => {
+ const selectedPaymentTerm = paymentTerm.find(
+ (industry) => industry.value === watch('tempoDuration')
+ );
+ if (selectedPaymentTerm) {
+ updateForm('tempoDuration', `${selectedPaymentTerm?.value}`);
+ validate();
+ }
+ }, [watch('tempoDuration'), paymentTerm]);
+
+ const estimasiValue = watch('estimasi');
+ const tempoLimitValue = watch('tempoLimit');
+
+ // Memformat angka menjadi format rupiah
+ const formatRupiah = (value) => {
+ if (!value) return '';
+ const numberString = value.replace(/[^0-9]/g, ''); // Menghapus karakter non-digit
+ return numberString
+ ? 'Rp ' + new Intl.NumberFormat('id-ID').format(numberString)
+ : '';
+ };
+
+ const handleChange = (e) => {
+ const value = e.target.value;
+ const formattedValue = formatRupiah(value);
+ updateForm('estimasi', formattedValue.replace(/^Rp\s*/, ''));
+ validate();
+ };
+
+ const [isCustom, setIsCustom] = React.useState(false);
+ const [tempoLimitValueEx, setTempoLimitValueEx] = React.useState('');
+ const handleCheckboxBersediaChange = (value) => {
+ // if (value === 'bersedia') {
+ // setBersedia(true);
+ // } else if (value === 'tidakBersedia') {
+ // setBersedia(false);
+ // }
+ // updateForm('bersedia', `${value === 'bersedia'}`);
+ updateForm('bersedia', `${value}`);
+ validate();
+ };
+ const handleCheckboxPortalChange = (value) => {
+ // if (value === 'bersedia') {
+ // setBersedia(true);
+ // } else if (value === 'tidakBersedia') {
+ // setBersedia(false);
+ // }
+ // updateForm('bersedia', `${value === 'bersedia'}`);
+ updateForm('portal', `${value}`);
+ validate();
+ };
+ const [selectedIds, setSelectedIds] = useState(
+ form.categoryProduk ? form.categoryProduk.split(',').map(Number) : [] // 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
+ updateForm('categoryProduk', updatedSelected.join(','));
+ validate();
+ };
+
+ useEffect(() => {
+ if (form.categoryProduk) {
+ setSelectedIds(form.categoryProduk.split(',').map(Number)); // Parse string menjadi array angka
+ }
+ }, [form.categoryProduk]);
+ const isChecked = (id) => selectedIds.includes(id);
+
+ const handleInputChange = (event) => {
+ const { name, value } = event.target;
+ updateForm(name, value);
+ validate();
+ };
+
+ const midIndex = Math.ceil(category_produk.length / 2);
+ const firstColumn = category_produk.slice(0, midIndex);
+ const secondColumn = category_produk.slice(midIndex);
+ const isFormValid = useMemo(() => Object.keys(errors).length === 0, [errors]);
+
+ const nameRef = useRef(null);
+ const industry_idRef = useRef(null);
+ const streetRef = useRef(null);
+ const stateRef = useRef(null);
+ const cityRef = useRef(null);
+ const districtRef = useRef(null);
+ const subDistrictRef = useRef(null);
+ const zipRef = useRef(null);
+ const mobileRef = useRef(null);
+ const bankNameRef = useRef(null);
+ const accountNameRef = useRef(null);
+ const accountNumberRef = useRef(null);
+ const estimasiRef = useRef(null);
+ const tempoDurationRef = useRef(null);
+ const bersediaRef = useRef(null);
+ const portalRef = useRef(null);
+ const categoryProdukRef = useRef(null);
+ const tempoLimitRef = useRef(null);
+
+ useEffect(() => {
+ const options = {
+ behavior: 'smooth',
+ block: 'center',
+ };
+ const loadIndustries = async () => {
+ if (!isFormValid) {
+ if (errors.name && nameRef.current) {
+ nameRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errors.industryId && industry_idRef.current) {
+ industry_idRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errors.street && streetRef.current) {
+ streetRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errors.state && stateRef.current) {
+ stateRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errors.city && cityRef.current) {
+ cityRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errors.district && districtRef.current) {
+ districtRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errors.subDistrict && subDistrictRef.current) {
+ subDistrictRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errors.zip && zipRef.current) {
+ zipRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errors.mobile && mobileRef.current) {
+ mobileRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errors.bankName && bankNameRef.current) {
+ bankNameRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errors.accountName && accountNameRef.current) {
+ accountNameRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errors.accountNumber && accountNumberRef.current) {
+ accountNumberRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errors.estimasi && estimasiRef.current) {
+ estimasiRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errors.tempoDuration && tempoDurationRef.current) {
+ tempoDurationRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errors.bersedia && bersediaRef.current) {
+ bersediaRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errors.portal && portalRef.current) {
+ portalRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errors.categoryProduk && categoryProdukRef.current) {
+ categoryProdukRef.current.scrollIntoView(options);
+ return;
+ }
+ if (errors.tempoLimit && tempoLimitRef.current) {
+ tempoLimitRef.current.scrollIntoView(options);
+ return;
+ }
+ }
+ };
+ loadIndustries();
+ }, [buttonSubmitClick, chekValid]);
+ useEffect(() => {
+ if (form.industryId) {
+ setValue('industryId', parseInt(form.industryId));
+ }
+ if (form.state) {
+ setValue('state', parseInt(form.state));
+ }
+
+ if (form.district) {
+ setValue('district', parseInt(form.district));
+ }
+ if (form.subDistrict) {
+ setValue('subDistrict', parseInt(form.subDistrict));
+ }
+ if (form.zip) {
+ setValue('zip', parseInt(form.zip));
+ }
+ if (form.tempoDuration) {
+ setValue('tempoDuration', parseInt(form.tempoDuration));
+ }
+ if (form.tempoLimit) {
+ setValue('tempoLimit', form.tempoLimit);
+ }
+ if (form.tempoLimit) {
+ const isValueInOptions = radioOptions.some(
+ (option) => option.value === form.tempoLimit
+ );
+
+ if (isValueInOptions) {
+ setValue('tempoLimit', form.tempoLimit); // Set value dari radio options
+ setIsCustom(false); // Pastikan custom tidak aktif
+ } else {
+ setValue('tempoLimit', 'custom'); // Set value ke custom jika tidak termasuk dalam options
+ setIsCustom(true); // Aktifkan custom input
+ setTempoLimitValueEx(form.tempoLimit); // Set nilai input custom ke form.tempoLimit
+ }
+ }
+ }, [form]);
+ useEffect(() => {
+ if (form.city) {
+ setValue('city', parseInt(form.city));
+ }
+ }, [form.city]);
+ useEffect(() => {
+ const loadProfile = async () => {
+ try {
+ const dataProfile = await addressApi({ id: auth.parentId });
+ if (dataProfile.name) {
+ updateForm('name', dataProfile.name);
+ }
+ if (dataProfile.street) {
+ updateForm('street', dataProfile.street);
+ }
+ if (dataProfile.stateId.id) {
+ updateForm('state', `${dataProfile.stateId.id}`);
+ }
+ if (dataProfile.city.id) {
+ updateForm('city', `${dataProfile.city.id}`);
+ }
+ if (dataProfile.district.id) {
+ updateForm('district', `${dataProfile.district.id}`);
+ }
+ if (dataProfile.subDistrict.id) {
+ updateForm('subDistrict', `${dataProfile.subDistrict.id}`);
+ }
+ if (dataProfile.zip) {
+ updateForm('zip', dataProfile.zip);
+ }
+ if (dataProfile.mobile) {
+ updateForm('mobile', dataProfile.mobile.replace(/\D/g, ''));
+ } else {
+ setChangeConfirmation(true);
+ }
+ if (!dataProfile.email) {
+ setChangeConfirmation(true);
+ }
+ } catch (error) {
+ console.error('Error loading profile:', error);
+ } finally {
+ validate();
+ }
+ };
+
+ if (auth?.parentId) {
+ loadProfile();
+ }
+ }, [auth?.parentId]);
+ useEffect(() => {
+ const loadProfile = async () => {
+ try {
+ const dataProfile = await addressApi({ id: auth.parentId });
+ setValue('industryId', parseInt(dataProfile.industryId));
+ setValue('state', parseInt(dataProfile.stateId.id));
+ setValue('city', parseInt(dataProfile.city.id));
+ setValue('district', parseInt(dataProfile.district.id));
+ setValue('subDistrict', parseInt(dataProfile.subDistrict.id));
+ } catch (error) {
+ console.error('Error loading profile:', error);
+ }
+ };
+
+ if (auth?.parentId) {
+ loadProfile();
+ }
+ }, [auth?.parentId, setValue]);
+
+ const handleLengkapiData = () => {
+ router.push('/my/profile');
+ };
+ return (
+ <>
+ <BottomPopup
+ active={changeConfirmation}
+ close={() => {
+ toast.error(
+ 'Mohon lengkapi data perusahaan sebelum melakukan pengajuan tempo'
+ );
+ }}
+ title='Data Perusahaan Anda Belum Lengkap'
+ >
+ <div className='leading-7 text-gray_r-12/80'>
+ Mohon lengkapi data perusahaan sebelum melakukan pengajuan tempo
+ </div>
+ <div className='flex mt-6 gap-x-4 md:justify-end'>
+ <button
+ className='btn-solid-red flex-1 md:flex-none'
+ type='button'
+ onClick={handleLengkapiData}
+ >
+ Lengkapi Data
+ </button>
+ <button
+ className='btn-light flex-1 md:flex-none'
+ type='button'
+ onClick={() => {
+ toast.error(
+ 'Mohon lengkapi data perusahaan sebelum melakukan pengajuan tempo'
+ );
+ }}
+ >
+ Batal
+ </button>
+ </div>
+ </BottomPopup>
+ {isDesktop && (
+ <div className=''>
+ <h1 className={`font-bold ${isKonfirmasi ? 'text-xl' : ''}`}>
+ Informasi Perusahaan
+ </h1>
+ <form className='flex flex-col w-full '>
+ <div className='w-full grid grid-row-2 gap-5'>
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-nowrap'>
+ Nama Perusahaan
+ </label>
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ isi detail perusahaan sesuai dengan nama yang terdaftar{' '}
+ </span>
+ )}
+ </div>
+ <div className='w-3/5'>
+ <input
+ id='name'
+ name='name'
+ placeholder='Masukkan nama perusahaan'
+ type='text'
+ className='form-input'
+ disabled={true}
+ aria-invalid={errors.name}
+ value={form.name}
+ ref={nameRef}
+ onChange={handleInputChange}
+ />
+ {/* {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ Format: PT. INDOTEKNIK DOTCOM GEMILANG
+ </span>
+ )} */}
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.name}
+ </div>
+ )}
+ </div>
+ </div>
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5' ref={industry_idRef}>
+ <label className='form-label text-nowrap'>Industri</label>
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ Pilih jenis industri perusahaan anda
+ </span>
+ )}
+ </div>
+ <div className='w-3/5'>
+ <Controller
+ name='industryId'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={industries}
+ placeholder={'Pilih industri perusahaan anda'}
+ />
+ )}
+ />
+ {!isKonfirmasi && selectedCategory && (
+ <span className='text-gray_r-11 text-xs opacity-60'>
+ Kategori : {selectedCategory}
+ </span>
+ )}
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.industryId}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5 text-nowrap'>
+ <label className='form-label '>Alamat Perusahaan</label>
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ alamat sesuai dengan alamat kantor pusat
+ </span>
+ )}
+ </div>
+ <div className='w-3/5 flex gap-3 flex-col'>
+ <div>
+ <input
+ id='street'
+ name='street'
+ ref={streetRef}
+ placeholder='Masukkan alamat lengkap perusahaan'
+ type='text'
+ value={form.street}
+ className='form-input'
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.street}
+ </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={`${isKonfirmasi ? 'w-full' : 'w-full'}`}
+ ref={stateRef}
+ >
+ <Controller
+ name='state'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={states}
+ placeholder='Provinsi'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.state}
+ </div>
+ )}
+ </div>
+ <div
+ className={`${isKonfirmasi ? 'w-full' : 'w-full'}`}
+ ref={cityRef}
+ >
+ <Controller
+ name='city'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={cities}
+ disabled={!watchState}
+ placeholder='Kota'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.city}
+ </div>
+ )}
+ </div>
+ </div>
+ <div
+ className={`flex-row flex gap-2 justify-between ${
+ isKonfirmasi ? 'w-full' : 'w-3/5'
+ }`}
+ >
+ <div className='w-full' ref={districtRef}>
+ <Controller
+ name='district'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={districts}
+ disabled={!watchState || !watchCity}
+ placeholder='Kecamatan'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.district}
+ </div>
+ )}
+ </div>
+ <div className='w-full' ref={subDistrictRef}>
+ <Controller
+ name='subDistrict'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={subDistricts}
+ disabled={!watchDistrict}
+ placeholder='Kelurahan'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.district}
+ </div>
+ )}
+ </div>
+ <div className='w-full' ref={zipRef}>
+ <Controller
+ name='zip'
+ control={control}
+ render={(props) => (
+ <>
+ {/* Jika zips tidak kosong, tampilkan dropdown */}
+ {zips.length > 0 ? (
+ <HookFormSelect
+ {...props}
+ options={zips}
+ disabled={!watchsubDistrict}
+ placeholder='Zip'
+ />
+ ) : (
+ // Jika zips kosong, tampilkan input manual
+ <input
+ id='zip'
+ name='zip'
+ ref={zipRef}
+ placeholder='Kode Pos'
+ type='number'
+ disabled={!watchsubDistrict}
+ value={form.zip}
+ className='form-input'
+ onChange={handleInputChange}
+ />
+ )}
+ </>
+ )}
+ />
+
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.zip}
+ </div>
+ )}
+ </div>
+ </div>
+ {/* <div className='w-1/3'>
+ <input
+ id='zip'
+ name='zip'
+ ref={zipRef}
+ placeholder='Kode Pos'
+ type='number'
+ disabled={!watchsubDistrict}
+ value={form.zip}
+ className='form-input'
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.zip}
+ </div>
+ )}
+ </div> */}
+ </div>
+ </div>
+ </div>
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5 text-nowrap'>
+ <label className='form-label '>No. HP Perusahaan</label>
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ isi no telfon perusahaan yang sesuai
+ </span>
+ )}
+ </div>
+ <div className='w-3/5'>
+ <input
+ id='mobile'
+ name='mobile'
+ ref={mobileRef}
+ placeholder='Masukkan nomor telfon perusahaan'
+ type='tel'
+ value={form.mobile}
+ className='form-input'
+ aria-invalid={errors.mobile}
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.mobile}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-between items-start'>
+ <div className=' w-2/5 text-nowrap'>
+ <label className='form-label'>Data Bank</label>
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ Isi detail data bank perusahaan anda
+ </span>
+ )}
+ </div>
+ <div className='w-3/5 flex gap-3 flex-row'>
+ <div>
+ <input
+ id='bankName'
+ name='bankName'
+ ref={bankNameRef}
+ placeholder='Nama bank'
+ type='text'
+ value={form.bankName}
+ className='form-input'
+ onChange={handleInputChange}
+ />
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ Format: BCA, Mandiri, CIMB, BNI dll
+ </span>
+ )}
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.bankName}
+ </div>
+ )}
+ </div>
+ <div>
+ <input
+ id='accountName'
+ name='accountName'
+ ref={accountNameRef}
+ placeholder='Nama Rekening'
+ type='text'
+ value={form.accountName}
+ className='form-input'
+ onChange={handleInputChange}
+ />
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ Format: John Doe
+ </span>
+ )}
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.accountName}
+ </div>
+ )}
+ </div>
+ <div>
+ <input
+ id='accountNumber'
+ name='accountNumber'
+ ref={accountNumberRef}
+ placeholder='Nomor Rekening Bank'
+ type='text'
+ value={form.accountNumber}
+ className='form-input'
+ onChange={handleInputChange}
+ />
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ Format: 01234567896
+ </span>
+ )}
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.accountNumber}
+ </div>
+ )}
+ </div>
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5 text-nowrap'>
+ <label className='form-label '>
+ Website <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <input
+ id='website'
+ name='website'
+ placeholder='www.indoteknik.com'
+ type='text'
+ value={form.website}
+ className='form-input'
+ onChange={handleInputChange}
+ />
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5 text-nowrap'>
+ <label className='form-label text-wrap'>
+ Estimasi Pembelian pertahun{' '}
+ <span className=' opacity-60 '>(Opsional)</span>
+ </label>
+ </div>
+ <div className='w-3/5'>
+ <div className='relative'>
+ <input
+ id='estimasi'
+ name='estimasi'
+ ref={estimasiRef}
+ // {...register('estimasi', {
+ // setValueAs: (value) => value.replace(/^Rp\s*/, ''), // Menyimpan hanya angka
+ // })}
+ placeholder='Isi estimasi pembelian produk pertahun'
+ type='text'
+ className='form-input' // padding untuk memberi ruang untuk "RP"
+ value={formatRupiah(form.estimasi)}
+ onChange={handleChange} // Mengatur perubahan input
+ />
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.estimasi}
+ </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>
+ <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-[75%]' : 'w-[25%]'}`}
+ ref={tempoDurationRef}
+ >
+ {/* <RadioGroup
+ onChange={onChangeTempoDuration}
+ value={form.tempoDuration}
+ >
+ <Stack direction='column' className=''>
+ <Radio colorScheme='red' value='7'>
+ 7 Hari
+ </Radio>
+ <Radio colorScheme='red' value='14' className=''>
+ 14 Hari
+ </Radio>
+ <Radio colorScheme='red' value='30' className=''>
+ 30 Hari
+ </Radio>
+ </Stack>
+ </RadioGroup> */}
+ <Controller
+ name='tempoDuration'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={paymentTerm}
+ placeholder={'Pilih Durasi Tempo'}
+ />
+ )}
+ />
+
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.tempoDuration}
+ </div>
+ )}
+ </div>
+ </div>
+ <div className='text-red-500 text-xs mt-2'>
+ **Durasi tempo dapat berbeda sesuai dengan verifikasi oleh
+ tim Indoteknik.com
+ </div>
+ </div>
+ </div>
+
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-wrap '>
+ Apakah terdapat vendor portal pada perusahaan anda?
+ </label>
+ </div>
+ <div className='w-3/5 flex flex-col justify-start'>
+ <div className='flex gap-x-4' ref={portalRef}>
+ <RadioGroup
+ onChange={handleCheckboxPortalChange}
+ value={form.portal}
+ >
+ <Stack direction='row'>
+ <Radio colorScheme='red' value='ada'>
+ Ya, ada
+ </Radio>
+ <Radio colorScheme='red' value='tidak'>
+ Tidak ada
+ </Radio>
+ </Stack>
+ </RadioGroup>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.portal}
+ </div>
+ )}
+ </div>
+ </div>
+ <div className='flex flex-row justify-between items-start'>
+ <div className='w-2/5'>
+ <label className='form-label text-wrap '>
+ Apakah bersedia transaksi via website?
+ </label>
+ </div>
+ <div className='w-3/5 flex flex-col justify-start'>
+ <div className='flex gap-x-4' ref={bersediaRef}>
+ <RadioGroup
+ onChange={handleCheckboxBersediaChange}
+ value={form.bersedia}
+ >
+ <Stack direction='row'>
+ <Radio colorScheme='red' value='bersedia'>
+ Saya bersedia
+ </Radio>
+ <Radio colorScheme='red' value='tidakBersedia'>
+ Tidak bersedia
+ </Radio>
+ </Stack>
+ </RadioGroup>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.bersedia}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div
+ className={`flex flex-row justify-between ${
+ isKonfirmasi ? 'items-center' : 'items-start'
+ }`}
+ >
+ <div className='w-2/5 text-nowrap'>
+ <label className='form-label '>
+ Kategori Produk yang Digunakan
+ </label>
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ Pilih produk bisa lebih dari 1
+ </span>
+ )}
+ </div>
+ <div className='w-3/5 flex flex-col'>
+ <div className='flex flex-row justify-between'>
+ <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>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.categoryProduk}
+ </div>
+ )}
+ </div>
+ </div>
+ </div>
+ </form>
+ </div>
+ )}
+ {isMobile && (
+ <div className='text-sm'>
+ <h1
+ className={`font-bold py-4 mt-8 ${
+ isKonfirmasi ? 'hidden' : 'text-xl'
+ }`}
+ >
+ Informasi Perusahaan
+ </h1>
+ <form className='flex flex-col w-full '>
+ <div className='w-full grid grid-row-2 gap-4'>
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label text-nowrap'>
+ Nama Perusahaan
+ </label>
+ <input
+ id='name'
+ name='name'
+ placeholder='Format: PT. INDOTEKNIK DOTCOM GEMILANG'
+ type='text'
+ className='form-input'
+ aria-invalid={errors.name}
+ value={form.name}
+ ref={nameRef}
+ onChange={handleInputChange}
+ />
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ isi detail perusahaan sesuai dengan nama yang terdaftar{' '}
+ </span>
+ )}
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.name}
+ </div>
+ )}
+ </div>
+
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label text-nowrap'>Industri</label>
+ <div className='w-full' ref={industry_idRef}>
+ <Controller
+ name='industryId'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={industries}
+ placeholder={
+ 'Pilih Industri yang sesuai dengan perusahaan'
+ }
+ />
+ )}
+ />
+ </div>
+ {!isKonfirmasi && selectedCategory && (
+ <span className='text-gray_r-11 text-xs opacity-60'>
+ Kategori : {selectedCategory}
+ </span>
+ )}
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.industryId}
+ </div>
+ )}
+ </div>
+
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label '>Alamat Perusahaan</label>
+ <input
+ id='street'
+ name='street'
+ ref={streetRef}
+ placeholder='Masukkan alamat lengkap perusahaan'
+ type='text'
+ value={form.street}
+ className='form-input'
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.street}
+ </div>
+ )}
+ <div className='w-full text-warp gap-3 flex flex-col'>
+ <div className='sub-alamat flex flex-row w-full gap-3 '>
+ <div className='w-full' ref={stateRef}>
+ <Controller
+ name='state'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={states}
+ placeholder='Provinsi'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.state}
+ </div>
+ )}
+ </div>
+ <div className='w-full' ref={cityRef}>
+ <Controller
+ name='city'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={cities}
+ disabled={!watchState}
+ placeholder='Kota'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.city}
+ </div>
+ )}
+ </div>
+ </div>
+ <div className='flex flex-row gap-3'>
+ <div className='w-full' ref={districtRef}>
+ <Controller
+ name='district'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={districts}
+ disabled={!watchCity}
+ placeholder='Kecamatan'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.district}
+ </div>
+ )}
+ </div>
+ <div className='w-full' ref={subDistrictRef}>
+ <Controller
+ name='subDistrict'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={subDistricts}
+ disabled={!watchDistrict}
+ placeholder='Kelurahan'
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.district}
+ </div>
+ )}
+ </div>
+ <div className='w-full'>
+ <Controller
+ name='zip'
+ control={control}
+ render={(props) => (
+ <>
+ {/* Jika zips tidak kosong, tampilkan dropdown */}
+ {zips.length > 0 ? (
+ <HookFormSelect
+ {...props}
+ options={zips}
+ disabled={!watchsubDistrict}
+ placeholder='Zip'
+ />
+ ) : (
+ // Jika zips kosong, tampilkan input manual
+ <input
+ id='zip'
+ name='zip'
+ ref={zipRef}
+ placeholder='Kode Pos'
+ type='number'
+ disabled={!watchsubDistrict}
+ value={form.zip}
+ className='form-input'
+ onChange={handleInputChange}
+ />
+ )}
+ </>
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.zip}
+ </div>
+ )}
+ </div>
+ </div>
+ </div>
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ Isi detail alamat sesuai dengan yang terdaftar
+ </span>
+ )}
+ </div>
+
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label '>No. HP Perusahaan</label>
+ <input
+ id='mobile'
+ name='mobile'
+ ref={mobileRef}
+ placeholder='Format: 08123456789 / (021) 123 4567'
+ type='tel'
+ value={form.mobile}
+ className='form-input'
+ aria-invalid={errors.mobile}
+ onChange={handleInputChange}
+ />
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ Isi detail perusahaan sesuai dengan nama yang terdaftar
+ </span>
+ )}
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.mobile}
+ </div>
+ )}
+ </div>
+
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label'>Data Bank</label>
+ {!isKonfirmasi && (
+ <span className='text-xs opacity-60'>
+ Isi data bank perusahaan sesuai dengan yang terdaftar
+ </span>
+ )}
+ <div className='flex gap-3 flex-row'>
+ <div>
+ <input
+ id='bankName'
+ name='bankName'
+ ref={bankNameRef}
+ placeholder='Nama bank'
+ type='text'
+ value={form.bankName}
+ className='form-input'
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.bankName}
+ </div>
+ )}
+ </div>
+ <div>
+ <input
+ id='accountName'
+ name='accountName'
+ ref={accountNameRef}
+ placeholder='Nama Rekening'
+ type='text'
+ value={form.accountName}
+ className='form-input'
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.accountName}
+ </div>
+ )}
+ </div>
+ <div>
+ <input
+ id='accountNumber'
+ name='accountNumber'
+ ref={accountNumberRef}
+ placeholder='Nomor Rekening'
+ type='text'
+ value={form.accountNumber}
+ className='form-input'
+ onChange={handleInputChange}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.accountNumber}
+ </div>
+ )}
+ </div>
+ </div>
+ </div>
+
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label '>
+ Website <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ <input
+ id='website'
+ name='website'
+ placeholder='Format: www.indoteknik.com'
+ type='text'
+ value={form.website}
+ className='form-input'
+ onChange={handleInputChange}
+ />
+ </div>
+
+ <div className='flex flex-col gap-2 justify-between items-start'>
+ <label className='form-label '>
+ Estimasi Pembelian pertahun{' '}
+ <span className=' opacity-60'>(Opsional)</span>
+ </label>
+ <input
+ id='estimasi'
+ name='estimasi'
+ ref={estimasiRef}
+ // {...register('estimasi', {
+ // setValueAs: (value) => value.replace(/^Rp\s*/, ''), // Menyimpan hanya angka
+ // })}
+ placeholder='Isi estimasi pembelian produk pertahun'
+ type='text'
+ className='form-input' // padding untuk memberi ruang untuk "RP"
+ value={formatRupiah(form.estimasi)}
+ onChange={handleChange} // Mengatur perubahan input
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.estimasi}
+ </div>
+ )}
+ </div>
+ <div className='h-[2px] bg-gray-300 w-[120%] inset-0 relative transform -translate-x-5'></div>
+ <div className='flex flex-col gap-2 justify-start items-start'>
+ <label className='form-label text-nowrap'>Durasi Tempo</label>
+ <div className='w-full' ref={tempoDurationRef}>
+ <Controller
+ name='tempoDuration'
+ control={control}
+ render={(props) => (
+ <HookFormSelect
+ {...props}
+ options={paymentTerm}
+ placeholder={'Pilih Durasi Tempo'}
+ />
+ )}
+ />
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.tempoDuration}
+ </div>
+ )}
+ </div>
+ </div>
+
+ <div className='text-red-500 text-xs'>
+ **Durasi tempo dapat berbeda dengan verifikasi oleh tim
+ indoteknik.com
+ </div>
+ <div className='h-[2px] bg-gray-300 w-[120%] inset-0 relative transform -translate-x-5'></div>
+
+ <div className='flex flex-col gap justify-between items-start'>
+ <label className='form-label text-wrap '>
+ Apakah terdapat vendor portal pada perusahaan anda?
+ </label>
+ <div className='flex gap-x-4' ref={bersediaRef}>
+ <RadioGroup
+ size='sm'
+ onChange={handleCheckboxPortalChange}
+ value={form.portal}
+ >
+ <Stack direction='col'>
+ <Radio colorScheme='red' value='ada'>
+ Ya, ada
+ </Radio>
+ <Radio colorScheme='red' value='tidak'>
+ Tidak ada
+ </Radio>
+ </Stack>
+ </RadioGroup>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.portal}
+ </div>
+ )}
+ </div>
+ <div className='flex flex-col gap justify-between items-start'>
+ <label className='form-label text-wrap '>
+ Apakah bersedia transaksi via website?
+ </label>
+ <div className='flex gap-x-4' ref={bersediaRef}>
+ <RadioGroup
+ size='sm'
+ onChange={handleCheckboxBersediaChange}
+ value={form.bersedia}
+ >
+ <Stack direction='col'>
+ <Radio colorScheme='red' value='bersedia'>
+ Saya bersedia
+ </Radio>
+ <Radio colorScheme='red' value='tidakBersedia'>
+ Tidak bersedia
+ </Radio>
+ </Stack>
+ </RadioGroup>
+ </div>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.bersedia}
+ </div>
+ )}
+ </div>
+ <div className='h-[2px] bg-gray-300 w-[120%] inset-0 relative transform -translate-x-5'></div>
+
+ <div
+ className={`flex flex-col gap-2 justify-between ${
+ isKonfirmasi ? 'items-start' : 'items-start'
+ }`}
+ >
+ <label className='form-label '>
+ Kategori Produk yang Digunakan
+ </label>
+ <div className='flex flex-col justify-between gap-2 '>
+ <div className='flex flex-col gap-2' ref={categoryProdukRef}>
+ {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>
+ {chekValid && (
+ <div className='text-caption-2 text-danger-500 mt-1'>
+ {errors.categoryProduk}
+ </div>
+ )}
+ </div>
+ </div>
+ </form>
+ </div>
+ )}
+ </>
+ );
+};
+
+export default InformasiPerusahaan;
diff --git a/src/lib/quotation/components/Quotation.jsx b/src/lib/quotation/components/Quotation.jsx
index 5a2f63a5..2f4d6c46 100644
--- a/src/lib/quotation/components/Quotation.jsx
+++ b/src/lib/quotation/components/Quotation.jsx
@@ -37,6 +37,7 @@ const { checkoutApi } = require('@/lib/checkout/api/checkoutApi');
const { getProductsCheckout } = require('@/lib/checkout/api/checkoutApi');
const Quotation = () => {
+ const PPN = process.env.NEXT_PUBLIC_PPN ? parseFloat(process.env.NEXT_PUBLIC_PPN) : 0;
const router = useRouter();
const auth = useAuth();
const query = router.query.source ?? null;
@@ -307,7 +308,7 @@ const Quotation = () => {
toast.error('Gagal melakukan transaksi, terjadi kesalahan internal');
};
- const taxTotal = (totalAmount - totalDiscountAmount) * 0.11;
+ const taxTotal = (totalAmount - totalDiscountAmount) * (PPN - 1);
return (
<>
@@ -425,7 +426,7 @@ const Quotation = () => {
<div>{currencyFormat(cartCheckout?.subtotal)}</div>
</div>
<div className='flex gap-x-2 justify-between'>
- <div className='text-gray_r-11'>PPN 11%</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'>
@@ -574,7 +575,7 @@ const Quotation = () => {
<div>{currencyFormat(cartCheckout?.subtotal)}</div>
</div>
<div className='flex gap-x-2 justify-between'>
- <div className='text-gray_r-11'>PPN 11%</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'>
diff --git a/src/lib/tempo/components/Tempo.jsx b/src/lib/tempo/components/Tempo.jsx
new file mode 100644
index 00000000..c246f3d8
--- /dev/null
+++ b/src/lib/tempo/components/Tempo.jsx
@@ -0,0 +1,754 @@
+import {
+ CheckIcon,
+ ClockIcon,
+ EllipsisVerticalIcon,
+ MagnifyingGlassIcon,
+} from '@heroicons/react/24/outline';
+import { Skeleton } from '@chakra-ui/react';
+import { div, toQuery } from 'lodash-contrib';
+import _ from 'lodash';
+import { useRouter } from 'next/router';
+import { useState, useEffect } from 'react';
+import useInvoices from '../../invoice/hooks/useInvoices';
+import Spinner from '@/core/components/elements/Spinner/Spinner';
+import Alert from '@/core/components/elements/Alert/Alert';
+import Pagination from '@/core/components/elements/Pagination/Pagination';
+import Link from '@/core/components/elements/Link/Link';
+import currencyFormat from '@/core/utils/currencyFormat';
+import BottomPopup from '@/core/components/elements/Popup/BottomPopup';
+import {
+ downloadInvoice,
+ downloadTaxInvoice,
+} from '../../invoice/utils/invoices';
+import MobileView from '@/core/components/views/MobileView';
+import DesktopView from '@/core/components/views/DesktopView';
+import Menu from '@/lib/auth/components/Menu';
+import odooApi from '@/core/api/odooApi';
+import { getAuth } from '@/core/utils/auth';
+import FooterBanner from '~/modules/footer-banner';
+import Image from '~/components/ui/image';
+import useDevice from '@/core/hooks/useDevice';
+import { Tooltip } from '@chakra-ui/react';
+import { InfoIcon } from 'lucide-react';
+const Tempo = () => {
+ const auth = getAuth();
+ const router = useRouter();
+ const { q = '', page = 1, limit = 15, status = '' } = router.query;
+ const { isDesktop, isMobile } = useDevice();
+ const [pageNew, setPageNew] = useState(page);
+ const [limitNew, setLimitNew] = useState(limit);
+ const [statusNew, setStatusNew] = useState(status);
+
+ const query = {
+ name: q,
+ offset: (pageNew - 1) * limitNew,
+ limit: limitNew,
+ status: statusNew,
+ };
+ const { invoices } = useInvoices({ query });
+ const [isLoading, setIsLoading] = useState(true);
+ const [tempo, setTempo] = useState([]);
+ const [inputQuery, setInputQuery] = useState(q);
+ const [toOthers, setToOthers] = useState(null);
+
+ const pageCount = Math.ceil(invoices?.data?.invoiceTotal / limitNew);
+ let pageQuery = _.omit(query, ['limitNew', 'offset']);
+ pageQuery = _.pickBy(pageQuery, _.identity);
+ pageQuery = toQuery(pageQuery);
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ router.push(`/my/tempo?q=${inputQuery}`);
+ };
+ useEffect(() => {
+ const loadTempo = async () => {
+ setIsLoading(true);
+ const dataTempo = await odooApi(
+ 'GET',
+ `/api/v1/check/${auth.partnerId}/tempo`
+ );
+ setTempo(dataTempo);
+ setIsLoading(false);
+ };
+ loadTempo();
+ }, []);
+ const limitTempo = Math.round(
+ parseInt(tempo.amountDue + tempo.remainingLimit)
+ );
+ const amountDue = Math.round(parseInt(tempo.amountDue + tempo.amountDueSale));
+
+ const startItem = 1 + (pageNew - 1) * limitNew;
+ const endItem = Math.min(
+ limitNew * pageNew,
+ invoices?.data?.invoiceTotal +
+ (statusNew === 0 || statusNew === 2 || statusNew === 4
+ ? invoices?.data?.saleOrderTotal
+ : 0)
+ );
+ const getDueDate = (invoice) => {
+ const [day, month, year] = invoice.split('/');
+ const dueDate = new Date(year, month - 1, day); // Konversi ke objek Date
+ return dueDate;
+ };
+
+ const formatTanggal = (tanggalInput) => {
+ const [day, month, year] = tanggalInput.split('/');
+ const date = new Date(year, month - 1, day);
+ const formattedDate = new Intl.DateTimeFormat('id-ID', {
+ day: '2-digit',
+ month: 'short',
+ year: 'numeric',
+ }).format(date);
+
+ return formattedDate;
+ };
+
+ const handleClick = () => {
+ setStatusNew((prevStatus) => (prevStatus === 4 ? 0 : 4)); // Toggle antara 4 dan 0
+ };
+ const handleClickInvoice = () => {
+ setStatusNew((prevStatus) => (prevStatus === 1 ? 0 : 1)); // Toggle antara 4 dan 0
+ };
+ return (
+ <>
+ <DesktopView>
+ <div className='container mx-auto flex py-10'>
+ <div className='w-3/12 pr-4'>
+ <Menu />
+ </div>
+ <div className='w-9/12 p-4 bg-white border border-gray_r-6 rounded flex flex-col gap-y-4'>
+ <div className='flex flex-col mb-6 items-start justify-start'>
+ <h1 className='text-title-sm font-semibold'>Pembayaran Tempo</h1>
+ <div className='flex items-start mb-4'>
+ <p className='flex flex-row gap-2 w-full items-center'>
+ Kredit Limit Anda
+ <Skeleton
+ isLoaded={!isLoading}
+ h='fit'
+ w='fit'
+ className='text-3xl font-semibold text-green-600 min-w-56'
+ >
+ {limitTempo ? currencyFormat(limitTempo) : '-'}
+ </Skeleton>
+ </p>
+ <Tooltip
+ placement='top-start'
+ label='Jumlah kredit limit dapat berubah sesuai dengan ketentuan yang Indoteknik miliki.'
+ >
+ <div className='text-gray-600'>
+ <InfoIcon size={15} />
+ </div>
+ </Tooltip>
+ </div>
+ <p className='flex flex-row gap-2 w-full'>
+ Status Detail Tempo Pembayaran Anda adalah{' '}
+ <Skeleton
+ isLoaded={!isLoading}
+ h='fit'
+ w={32}
+ className={`${
+ tempo.paymentTerm ? 'badge-solid-green' : 'badge-yellow'
+ } px-1 text-sm flex items-center justify-center font-thin`}
+ >
+ {tempo.paymentTerm ? tempo.paymentTerm : 'Review'}
+ </Skeleton>
+ </p>
+ </div>
+ <div className='grid grid-flow-col gap-4'>
+ <div className='border w-full p-4'>
+ <p>Sisa Kredit Limit</p>
+ <Skeleton
+ isLoaded={!isLoading}
+ h='36px'
+ w='full'
+ className='text-3xl font-semibold text-green-600'
+ >
+ {limitTempo && amountDue
+ ? currencyFormat(
+ Math.round(parseInt(limitTempo - amountDue))
+ )
+ : '-'}
+ </Skeleton>
+ </div>
+ <div
+ className={`border w-full p-4 flex gap-4 items-center hover:cursor-pointer hover:shadow-lg ${
+ statusNew == 4 ? 'bg-red-100' : ''
+ }`}
+ onClick={handleClick}
+ >
+ <div className=''>
+ <p>Kredit Limit Terpakai</p>
+ <Skeleton
+ isLoaded={!isLoading}
+ h='36px'
+ w='full'
+ className='text-3xl font-semibold text-red-600'
+ >
+ {amountDue ? currencyFormat(amountDue) : '-'}
+ </Skeleton>
+ </div>
+ <div className='w-fit h-full flex items-center text-red-600 text-lg text-nowrap font-medium '>
+ {tempo.amountDueTotal
+ ? tempo.amountDueTotal +
+ tempo.amountDueSaleTotal +
+ ` Transaksi`
+ : ''}
+ </div>
+ </div>
+ <div
+ className={`border w-full p-4 flex gap-4 items-center hover:cursor-pointer hover:shadow-lg ${
+ statusNew == 1 ? 'bg-red-100' : ''
+ }`}
+ onClick={handleClickInvoice}
+ >
+ <div className=''>
+ <p>Jatuh Tempo</p>
+ <Skeleton
+ isLoaded={!isLoading}
+ h='36px'
+ w='full'
+ className='text-3xl font-semibold text-red-600'
+ >
+ {tempo.amountJatuhTempo
+ ? currencyFormat(tempo.amountJatuhTempo)
+ : '-'}
+ </Skeleton>
+ </div>
+ <div className='w-fit h-full flex items-center text-red-600 text-lg text-nowrap font-medium '>
+ {tempo.amountJatuhTempoTotal
+ ? tempo.amountJatuhTempoTotal + ` Invoice`
+ : ''}
+ </div>
+ </div>
+ {/* <div className='border w-full p-4'>
+ <p>Jatuh Tempo</p>
+ <Skeleton
+ isLoaded={!isLoading}
+ h='36px'
+ // w={16}
+ className='text-3xl font-semibold text-red-600'
+ >
+ {tempo.amountJatuhTempo
+ ? currencyFormat(tempo.amountJatuhTempo)
+ : '-'}
+ </Skeleton>
+ </div> */}
+ </div>
+
+ {auth && auth?.tempoProgres == 'review' && !tempo.paymentTerm ? (
+ <div className='flex justify-center'>
+ <Image
+ src='/images/ICON-DOKUMEN-VERIFIKASI.png'
+ alt='Registrasi Tempo'
+ width={isMobile ? 300 : 600}
+ height={isMobile ? 300 : 550}
+ />
+ </div>
+ ) : (
+ <div className='border p-4 rounded'>
+ <div className='flex mb-6 items-center justify-between mt-4'>
+ <form className='flex' onSubmit={handleSubmit}>
+ <button className='bg-gray_r-2 px-3' type='submit'>
+ <MagnifyingGlassIcon className='w-6' />
+ </button>
+ <input
+ type='text'
+ className='form-input bg-gray_r-2 border-none'
+ placeholder='Cari Invoice...'
+ value={inputQuery}
+ onChange={(e) => setInputQuery(e.target.value)}
+ />
+ </form>
+ {!invoices.isLoading && (
+ <div className='flex flex-row gap-4 items-center justify-center'>
+ <p>
+ Menampilkan {startItem}-{endItem} dari{' '}
+ {invoices?.data?.invoiceTotal +
+ (statusNew === 0 || statusNew === 2 || statusNew === 4
+ ? invoices?.data?.saleOrderTotal
+ : 0)}
+ </p>
+ <select
+ id='limitSelect'
+ value={limitNew}
+ onChange={(e) => {
+ setLimitNew(Number(e.target.value));
+ setPageNew(1);
+ }}
+ className='border p-2'
+ >
+ <option value={10}>10</option>
+ <option value={15}>15</option>
+ <option value={20}>20</option>
+ </select>
+ <select
+ id='statusSelect'
+ value={statusNew ?? ''} // Gunakan nullish coalescing untuk default value
+ onChange={(e) => {
+ setStatusNew(Number(e.target.value));
+ if (e.target.value == 0) {
+ if (inputQuery) {
+ router.push('/my/tempo/');
+ }
+ }
+ }}
+ className='border p-2'
+ >
+ <option value='' disabled hidden>
+ Status
+ </option>
+ <option value={0}>All</option>
+ <option value={1}>Jatuh Tempo</option>
+ <option value={2}>Belum Jatuh Tempo</option>
+ <option value={3}>Lunas</option>
+ </select>
+ </div>
+ )}
+ </div>
+
+ <table className='table-data'>
+ <thead>
+ <tr>
+ <th>No. Invoice</th>
+ <th>No. Transaksi</th>
+ <th className='!text-left'>Salesperson</th>
+ <th>Tanggal</th>
+ <th>Jatuh Tempo</th>
+ <th>Status</th>
+ <th className='!text-left'>Total</th>
+ </tr>
+ </thead>
+ <tbody>
+ {/* {invoices.isLoading && (
+ <tr>
+ <td colSpan={6}>
+ <div className='flex justify-center my-2'>
+ <Spinner className='w-6 text-gray_r-12/50 fill-gray_r-12' />
+ </div>
+ </td>
+ </tr>
+ )} */}
+ {/* {!invoices.isLoading &&
+ (!invoices?.data?.saleOrders ||
+ invoices?.data?.saleOrders?.length == 0) && (
+ <tr>
+ <td colSpan={6}>Tidak ada orders</td>
+ </tr>
+ )} */}
+ {(statusNew == 0 || statusNew == 2 || statusNew == 4) &&
+ invoices.data?.saleOrders?.map((orders) => (
+ <tr key={orders.id}>
+ <td>{orders.salesOrder || '-'}</td>
+ <td>
+ <Link href={`/my/transactions/${orders.id}`}>
+ {orders.name}
+ </Link>
+ </td>
+ <td className='!text-left'>{orders.sales}</td>
+ <td>{orders.dateOrder.split(' ')[0]}</td>
+ <td>-</td>
+ <td>
+ {
+ <div className=' h-fit mx-auto inline-flex items-center rounded-md bg-red-50 px-2 py-1 text-xs font-medium text-red-700 ring-1 ring-inset ring-red-600/20'>
+ Belum Jatuh Tempo
+ </div>
+ }
+ </td>
+ <td className='!text-left'>
+ {currencyFormat(orders.amountTotal)}
+ </td>
+ </tr>
+ ))}
+ </tbody>
+ <tbody>
+ {invoices.isLoading && (
+ <tr>
+ <td colSpan={6}>
+ <div className='flex justify-center my-2'>
+ <Spinner className='w-6 text-gray_r-12/50 fill-gray_r-12' />
+ </div>
+ </td>
+ </tr>
+ )}
+ {!invoices.isLoading &&
+ (!invoices?.data?.invoices ||
+ invoices?.data?.invoices?.length == 0) && (
+ <tr>
+ <td colSpan={6}>Tidak ada Invoice</td>
+ </tr>
+ )}
+ {invoices.data?.invoices?.map((invoice) => (
+ <tr key={invoice.id}>
+ <td>
+ <Link href={`/my/invoices/${invoice.id}`}>
+ {invoice.name}
+ </Link>
+ </td>
+ <td>{invoice.salesOrder || '-'}</td>
+ <td className='!text-left'>{invoice.sales}</td>
+ <td>{invoice.invoiceDate}</td>
+ <td>{invoice.invoiceDateDue}</td>
+ <td>
+ {invoice.amountResidual > 0 ? (
+ new Date() > getDueDate(invoice.invoiceDateDue) ? (
+ <div className='badge-solid-red h-fit mx-auto'>
+ Jatuh Tempo
+ </div>
+ ) : (
+ <div className=' h-fit mx-auto inline-flex items-center rounded-md bg-red-50 px-2 py-1 text-xs font-medium text-red-700 ring-1 ring-inset ring-red-600/20'>
+ Belum Jatuh Tempo
+ </div>
+ )
+ ) : (
+ <div className='badge-solid-green h-fit mx-auto'>
+ Lunas
+ </div>
+ )}
+ </td>
+ <td className='!text-left'>
+ {currencyFormat(invoice.amountTotal)}
+ </td>
+ </tr>
+ ))}
+ </tbody>
+ </table>
+
+ <Pagination
+ pageCount={pageCount}
+ currentPage={parseInt(pageNew)}
+ // url={`/my/tempo${pageQuery}`}
+ url={`/my/tempo?${toQuery(_.omit(query, ['page']))}`}
+ className='mt-2 mb-2'
+ />
+ </div>
+ )}
+
+ <div className=''>
+ <FooterBanner />
+ </div>
+ </div>
+ </div>
+ </DesktopView>
+ <MobileView>
+ <div className=' bg-[#FEF8E2] w-[110%] inset-0 relative transform -translate-x-5 px-8 py-4'>
+ <div className='flex flex-col gap-y-4 '>
+ <p className='flex flex-row gap-2 w-full text-sm items-center'>
+ <span className='opacity-70 text-nowrap'>
+ Kredit Limit Anda :{' '}
+ </span>
+ <Skeleton
+ isLoaded={!isLoading}
+ h='fit'
+ w='fit'
+ className='text-xl font-semibold text-green-700 min-w-56'
+ >
+ {limitTempo ? currencyFormat(limitTempo) : '-'}
+ </Skeleton>
+ </p>
+ <div className='flex flex-col items-center justify-center'>
+ <p className='flex flex-row w-full text-sm gap-2 items-center justify-start'>
+ <span className='opacity-70'>
+ Status Detail Tempo Pembayaran Anda adalah :
+ </span>
+ <Skeleton
+ isLoaded={!isLoading}
+ h='fit'
+ w={24}
+ className={`${
+ tempo.paymentTerm ? 'badge-solid-green' : 'badge-yellow'
+ } px-1 text-xs flex items-center justify-center font-extralight text-nowrap`}
+ >
+ {tempo.paymentTerm ? tempo.paymentTerm : 'Review'}
+ </Skeleton>
+ </p>
+ </div>
+ <div className='flex items-start justify-around text-sm gap-3'>
+ <div className='w-[30%] flex flex-col gap-2'>
+ <p className='opacity-70 text-nowrap'>Sisa Kredit Limit</p>
+ <Skeleton
+ isLoaded={!isLoading}
+ // h='36px'
+ // w={16}
+ className='font-semibold text-sm text-nowrap text-green-700 '
+ >
+ {limitTempo && amountDue
+ ? currencyFormat(
+ Math.round(parseInt(limitTempo - amountDue))
+ )
+ : '-'}
+ </Skeleton>
+ </div>{' '}
+ <div className='w-[40%] flex flex-col gap-2'>
+ <p className='opacity-70 text-nowrap'>Kredit Limit Terpakai</p>
+ <Skeleton
+ isLoaded={!isLoading}
+ // h='36px'
+ // w={16}
+ className='font-semibold text-sm text-nowrap text-green-700'
+ >
+ {amountDue ? currencyFormat(amountDue) : '-'}
+ </Skeleton>
+ </div>
+ <div className=' w-[30%] flex flex-col gap-2'>
+ <p className='opacity-70 text-nowrap'>Jatuh Tempo</p>
+ <Skeleton
+ isLoaded={!isLoading}
+ // h='36px'
+ // w={16}
+ className=' font-semibold text-sm text-nowrap text-red-600'
+ >
+ {tempo.amountJatuhTempo
+ ? currencyFormat(tempo.amountJatuhTempo)
+ : '-'}
+ </Skeleton>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div className='p-4 flex flex-col gap-y-4 w-full'>
+ {auth && (!auth?.tempoProgres == 'review' || tempo.paymentTerm) && (
+ <form
+ className='w-full flex gap-x-2 flex-row justify-between'
+ onSubmit={handleSubmit}
+ >
+ <div className='flex w-3/5'>
+ <input
+ type='text'
+ className='form-input'
+ placeholder='Cari Invoice...'
+ value={inputQuery}
+ onChange={(e) => setInputQuery(e.target.value)}
+ />
+ </div>
+ <button className='btn-light bg-transparent px-3' type='submit'>
+ <MagnifyingGlassIcon className='w-6' />
+ </button>
+ <div className='flex w-1/5 '>
+ <div className='flex flex-row gap-4 items-center justify-center'>
+ <select
+ id='statusSelect'
+ value={statusNew}
+ onChange={(e) => {
+ setStatusNew(Number(e.target.value));
+ if (e.target.value == 0) {
+ setInputQuery(' ');
+ }
+ }}
+ className='border p-2 w-full h-full rounded-md'
+ >
+ <option value={0}>All</option>
+ <option value={1}>Jatuh Tempo</option>
+ <option value={2}>Belum Jatuh Tempo</option>
+ <option value={3}>Lunas</option>
+ </select>
+ </div>
+ </div>
+ </form>
+ )}
+
+ {invoices.isLoading && (
+ <div className='flex justify-center my-4'>
+ <Spinner className='w-6 text-gray_r-12/50 fill-gray_r-12' />
+ </div>
+ )}
+
+ {auth && auth?.tempoProgres == 'review' && !tempo.paymentTerm ? (
+ // <Alert type='info' className='text-center'>
+ // Tidak ada invoice
+ // </Alert>
+ <div className='flex items-center justify-center'>
+ <Image
+ src='/images/ICON-DOKUMEN-VERIFIKASI.png'
+ alt='Registrasi Tempo'
+ width={isMobile ? 300 : 600}
+ height={isMobile ? 300 : 550}
+ />
+ </div>
+ ) : (
+ <div>
+ {(statusNew == 0 || statusNew == 2 || statusNew == 4) &&
+ invoices.data?.saleOrders?.map((invoice, index) => (
+ <div
+ className='p-4 shadow border border-gray_r-3 rounded-md'
+ key={index}
+ >
+ <div className='grid grid-cols-2'>
+ <Link href={`/my/quotations/${invoice.id}`}>
+ <span className='text-caption-2 text-gray_r-11'>
+ No. Transaksi
+ </span>
+ <h2 className='font-semibold text-black mt-1'>
+ {invoice.name}
+ </h2>
+ </Link>
+ <div className='flex gap-x-1 justify-end items-end '>
+ <div className='badge-solid-red h-fit ml-auto'>
+ Belum Jatuh Tempo
+ </div>
+ {/* <EllipsisVerticalIcon
+ className='w-5 h-5'
+ onClick={() => setToOthers(invoice)}
+ /> */}
+ </div>
+ </div>
+ <Link href={`/my/quotations/${invoice.id}`}>
+ <div className='grid grid-cols-2 text-caption-2 text-gray_r-11 mt-2 font-normal'>
+ <p className='opacity-70'>
+ {invoice.dateOrder.split(' ')[0]}
+ </p>
+ <p className='text-right text-xs text-red-500'>
+ Jatuh Tempo: -
+ </p>
+ </div>
+ <hr className='my-3' />
+ <div className='grid grid-cols-2'>
+ <div>
+ <span className='text-caption-2 text-gray_r-11'>
+ No. Invoice
+ </span>
+ <p className='mt-1 font-semibold text-gray_r-12'>-</p>
+ </div>
+ <div className='text-right'>
+ <span className='opacity-65 text-caption-2 text-gray_r-11'>
+ Total Belanja
+ </span>
+ <p className='mt-1 font-semibold text-red-500 '>
+ {currencyFormat(invoice.amountTotal)}
+ </p>
+ </div>
+ </div>
+ </Link>
+ {/* {invoice.efaktur ? (
+ <div className='badge-green h-fit mt-3 ml-auto flex items-center gap-x-0.5'>
+ <CheckIcon className='w-4 stroke-2' />
+ Faktur Pajak
+ </div>
+ ) : (
+ <div className='badge-red h-fit mt-3 ml-auto flex items-center gap-x-0.5'>
+ <ClockIcon className='w-4 stroke-2' />
+ Faktur Pajak
+ </div>
+ )} */}
+ </div>
+ ))}
+ {invoices.data?.invoices?.map((invoice, index) => (
+ <div
+ className='p-4 shadow border border-gray_r-3 rounded-md'
+ key={index}
+ >
+ <div className='grid grid-cols-2'>
+ <Link href={`/my/quotations/${invoice.salesOrderId}`}>
+ <span className='text-caption-2 text-gray_r-11'>
+ No. Transaksi
+ </span>
+ <h2 className='font-semibold text-black mt-1'>
+ {invoice.salesOrder}
+ </h2>
+ </Link>
+ <div className='flex gap-x-1 justify-end items-end '>
+ {invoice.amountResidual > 0 ? (
+ new Date() > getDueDate(invoice.invoiceDateDue) ? (
+ <div className='inline-flex items-end rounded-md bg-red-50 px-2 py-1 text-xs font-medium text-red-700 ring-1 ring-inset ring-red-600/20'>
+ Jatuh Tempo
+ </div>
+ ) : (
+ <div className='badge-solid-red h-fit ml-auto'>
+ Belum Jatuh Tempo
+ </div>
+ )
+ ) : (
+ <div className='badge-solid-green h-fit ml-auto'>
+ Lunas
+ </div>
+ )}
+ {/* <EllipsisVerticalIcon
+ className='w-5 h-5'
+ onClick={() => setToOthers(invoice)}
+ /> */}
+ </div>
+ </div>
+ <Link href={`/my/invoices/${invoice.id}`}>
+ <div className='grid grid-cols-2 text-caption-2 text-gray_r-11 mt-2 font-normal'>
+ <p className='opacity-70'>
+ {formatTanggal(invoice.invoiceDate)}
+ </p>
+ <p className='text-right text-xs text-red-500'>
+ Jatuh Tempo: {formatTanggal(invoice.invoiceDateDue)}
+ </p>
+ </div>
+ <hr className='my-3' />
+ <div className='grid grid-cols-2'>
+ <div>
+ <span className='text-caption-2 text-gray_r-11'>
+ No. Invoice
+ </span>
+ <p className='mt-1 font-semibold text-gray_r-12'>
+ {invoice.name || '-'}
+ </p>
+ </div>
+ <div className='text-right'>
+ <span className='opacity-65 text-caption-2 text-gray_r-11'>
+ Total Belanja
+ </span>
+ <p className='mt-1 font-semibold text-red-500 '>
+ {currencyFormat(invoice.amountTotal)}
+ </p>
+ </div>
+ </div>
+ </Link>
+ {/* {invoice.efaktur ? (
+ <div className='badge-green h-fit mt-3 ml-auto flex items-center gap-x-0.5'>
+ <CheckIcon className='w-4 stroke-2' />
+ Faktur Pajak
+ </div>
+ ) : (
+ <div className='badge-red h-fit mt-3 ml-auto flex items-center gap-x-0.5'>
+ <ClockIcon className='w-4 stroke-2' />
+ Faktur Pajak
+ </div>
+ )} */}
+ </div>
+ ))}
+ </div>
+ )}
+
+ <Pagination
+ pageCount={pageCount}
+ currentPage={parseInt(pageNew)}
+ url={`/my/tempo?${toQuery(_.omit(query, ['page']))}`}
+ className='mt-2 mb-2'
+ />
+
+ <BottomPopup
+ title='Lainnya'
+ active={toOthers}
+ close={() => setToOthers(null)}
+ >
+ <div className='flex flex-col gap-y-4 mt-2'>
+ <button
+ className='text-left disabled:opacity-60'
+ onClick={() => {
+ downloadInvoice(toOthers);
+ setToOthers(null);
+ }}
+ >
+ Download Invoice
+ </button>
+ <button
+ className='text-left disabled:opacity-60'
+ disabled={!toOthers?.efaktur}
+ onClick={() => {
+ downloadTaxInvoice(toOthers);
+ setToOthers(null);
+ }}
+ >
+ Download Faktur Pajak
+ </button>
+ </div>
+ </BottomPopup>
+ </div>
+ </MobileView>
+ </>
+ );
+};
+
+export default Tempo;
diff --git a/src/lib/transaction/components/Transaction.jsx b/src/lib/transaction/components/Transaction.jsx
index f5dc507a..b2fb2c17 100644
--- a/src/lib/transaction/components/Transaction.jsx
+++ b/src/lib/transaction/components/Transaction.jsx
@@ -42,6 +42,7 @@ import { gtagPurchase } from '@/core/utils/googleTag';
import { deleteItemCart } from '@/core/utils/cart';
import axios from 'axios';
const Transaction = ({ id }) => {
+ const PPN = process.env.NEXT_PUBLIC_PPN;
const router = useRouter();
const [isModalOpen, setIsModalOpen] = useState(false);
const [selectedProduct, setSelectedProduct] = useState(null);
@@ -207,7 +208,7 @@ const Transaction = ({ id }) => {
<p>{currencyFormat(transaction.data?.amountUntaxed)}</p>
</div>
<div className='flex justify-between mt-1'>
- <p className='text-gray_r-12/70'>PPN 11%</p>
+ <p className='text-gray_r-12/70'>PPN {((PPN - 1) * 100).toFixed(0)}%</p>
<p>{currencyFormat(transaction.data?.amountTax)}</p>
</div>
<div className='flex justify-between mt-1'>
@@ -975,7 +976,7 @@ const Transaction = ({ id }) => {
{currencyFormat(transaction.data?.amountUntaxed)}
</div>
- <div className='text-right'>PPN 11%</div>
+ <div className='text-right'>PPN {((PPN - 1) * 100).toFixed(0)}%</div>
<div className='text-right font-medium'>
{currencyFormat(transaction.data?.amountTax)}
</div>