diff options
Diffstat (limited to 'src/lib/pengajuan-tempo/component/PengajuanTempo.jsx')
| -rw-r--r-- | src/lib/pengajuan-tempo/component/PengajuanTempo.jsx | 716 |
1 files changed, 716 insertions, 0 deletions
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; |
