diff options
Diffstat (limited to 'src/lib/merchant/components/InformasiVendor.jsx')
| -rw-r--r-- | src/lib/merchant/components/InformasiVendor.jsx | 748 |
1 files changed, 748 insertions, 0 deletions
diff --git a/src/lib/merchant/components/InformasiVendor.jsx b/src/lib/merchant/components/InformasiVendor.jsx new file mode 100644 index 00000000..d00f27ed --- /dev/null +++ b/src/lib/merchant/components/InformasiVendor.jsx @@ -0,0 +1,748 @@ +import HookFormSelect from '@/core/components/elements/Select/HookFormSelect'; +import cityApi from '@/lib/address/api/cityApi'; +import stateApi from '@/lib/address/api/stateApi.js'; +import districtApi from '@/lib/address/api/districtApi'; +import subDistrictApi from '@/lib/address/api/subDistrictApi'; +import { yupResolver } from '@hookform/resolvers/yup'; +import React, { + useEffect, + useRef, + useState, + forwardRef, + useImperativeHandle, +} from 'react'; +import ReCAPTCHA from 'react-google-recaptcha'; +import { Controller, useForm } from 'react-hook-form'; +import { toast } from 'react-hot-toast'; +import * as Yup from 'yup'; +import createMerchantApi from '../api/createMerchantApi'; +import getMerchantApi from '../api/getMerchantApi'; +import addressApi from '@/lib/address/api/addressApi'; +import PageContent from '@/lib/content/components/PageContent'; +import { useRouter } from 'next/router'; +import useAuth from '@/core/hooks/useAuth'; +import { Radio, RadioGroup, Stack, Checkbox, Button } from '@chakra-ui/react'; +import { EyeIcon } from '@heroicons/react/24/outline'; +import BottomPopup from '@/core/components/elements/Popup/BottomPopup'; +import Image from 'next/image'; +import ImageBanner from '~/components/ui/image'; +import { ChevronRightIcon } from '@heroicons/react/24/outline'; +import MobileView from '@/core/components/views/MobileView'; +import DesktopView from '@/core/components/views/DesktopView'; +import getFileBase64 from '@/core/utils/getFileBase64'; +import odooApi from '~/libs/odooApi'; +import { formatValue } from 'react-currency-input-field'; +const InformasiVendor = forwardRef(({ handleIsError, isKonfirmasi }, ref) => { + const isError = (value) => { + // Logika menentukan error + const result = value ? true : false; + handleIsError(result); // Panggil handleIsError dari Merchant + return result; + }; + const { + register, + handleSubmit, + formState: { errors }, + control, + reset, + watch, + setValue, + getValues, + } = useForm({ + resolver: yupResolver(validationSchema), + defaultValues, + }); + const [categoryProduk, setCategoryProduk] = useState([]); + const [isExample, setIsExample] = useState(false); + + const router = useRouter(); + + const auth = useAuth(); + if (auth == false) { + router.push(`/login?next=${encodeURIComponent('/daftar-merchant')}`); + } + const dataTerhitungSejak = [ + { value: 1, label: 'Terima PO' }, + { value: 2, label: 'Barang Dikirimkan' }, + { value: 3, label: 'Tukar Faktur' }, + ]; + + const dataTempo = [ + { value: 24, label: 'Tempo 14 Hari' }, + { value: 25, label: 'Tempo 30 Hari' }, + { value: 28, label: 'Tempo 60 Hari' }, + { value: 31, label: 'Tempo 90 Hari' }, + ]; + + const midIndex = Math.ceil(categoryProduk.length / 2); + const firstColumn = categoryProduk.slice(0, midIndex); + const secondColumn = categoryProduk.slice(midIndex); + const [kreditLimitFormat, setKreditLimitFormat] = useState(); + + useEffect(() => { + const loadData = async () => { + const data = await getMerchantApi(); + if (data) { + reset({ + hargaTayang: data.hargaTayang || '', + categoryProduk: data.categoryProduk || '', + merkDagang: data.merkDagang || '', + isPengajuanTempo: data.isPengajuanTempo || '', + tempoDuration: parseInt(data.tempoDuration) || '', + // kreditLimit: parseInt(data.kreditLimit) || '', + waktuPengiriman: data.waktuPengiriman || '', + terhitungSejak: parseInt(data.terhitungSejak) || '', + }); + handleKreditLimitChange(data.kreditLimit || ''); + setSelectedIds(watch('categoryProduk').split(',').map(Number)); + } + }; + + loadData(); + }, []); + + useImperativeHandle(ref, () => () => { + handleSubmit(onSubmitHandler)(); + }); + + const handleKreditLimitChange = (e) => { + let value = e.target?.value ? e.target.value : e; + + // Hapus semua karakter non-numeric + value = value.replace(/[^\d]/g, ''); + + // Format angka sebagai Rupiah tanpa mengubah nilai sebenarnya + const formattedValue1 = formatValue({ + value: value, + groupSeparator: '.', + decimalSeparator: ',', + prefix: 'Rp ', + }); + + setKreditLimitFormat(formattedValue1); + setValue('kreditLimit', formattedValue1); + }; + + const [selectedIds, setSelectedIds] = useState( + watch('categoryProduk') + ? watch('categoryProduk').split(',').map(Number) + : [] + // form.categoryProduk ? form.categoryProduk.split(',').map(Number) : [] // Parse string menjadi array angka + // [] // Parse string menjadi array angka + ); + const handleCheckboxChange = (id) => { + const updatedSelected = selectedIds.includes(id) + ? selectedIds.filter((selectedId) => selectedId !== id) + : [...selectedIds, id]; + + setSelectedIds(updatedSelected); + + // Mengubah array kembali menjadi string yang dipisahkan oleh koma + setValue('categoryProduk', updatedSelected.join(',')); + }; + + const isChecked = (id) => selectedIds.includes(id); + + const handleCheckboxPortalChange = (value) => { + setValue('isPengajuanTempo', `${value}`); + }; + + useEffect(() => { + if (!isKonfirmasi) { + window.scrollTo({ + top: 0, + behavior: 'smooth', + }); + } + }, []); + + useEffect(() => { + const loadCategories = async () => { + let dataCategories = await odooApi('GET', '/api/v1/category/tree'); + const formattedCategories = dataCategories.map((category) => ({ + id: category.id, + name: category.name, + })); + // Simpan hasil ke state + setCategoryProduk(formattedCategories); + }; + loadCategories(); + }, []); + + const onSubmitHandler = async (values) => { + const toastId = toast.loading('Mengirimkan formulir merchant...'); + const data = { + harga_tayang: values.hargaTayang, + categoryProduk: values.categoryProduk, + merk_dagang: values.merkDagang, + is_pengajuan_tempo: values.isPengajuanTempo, + tempo_duration: values.tempoDuration, + kredit_limit: values.kreditLimit, + waktu_pengiriman: values.waktuPengiriman, + terhitung_sejak: values.terhitungSejak, + }; + const create_leads = await createMerchantApi({ data }); + if (create_leads) { + toast.dismiss(toastId); + isError(false); + toast.success('Berhasil menambahkan data'); + reset(); + // router.push('/+'); + } else { + toast.dismiss(toastId); + toast.error('Gagal menambahkan data'); + } + }; + + // const DownLoadSurat = () => { + // download surat dari /public/file/Surat Pernyataan Nomor Rekening.docx + // }; + + if (!auth) { + return; + } + + return ( + <> + <BottomPopup + className='' + title='Contoh SPPKP' + active={isExample} + close={() => setIsExample(false)} + > + <div className='flex p-2'> + <Image + src='/images/NO-SPPKP-FORMAT-TEMPLATE.jpg' + alt='Contoh SPPKP' + className='w-full h-full ' + width={800} + height={800} + quality={85} + /> + </div> + </BottomPopup> + <DesktopView> + <div className='container flex flex-col items-star py-4 '> + <h2 className='text-xs md:text-title-sm font-semibold mb-6'> + Informasi Vendor + </h2> + + <div className='w-full mt-4'> + <form + onSubmit={handleSubmit(onSubmitHandler)} + className='flex flex-col gap-4' + > + <div className='w-full flex flex-row'> + <div className='w-2/5'> + <label className='form-label text-nowrap'> + Harga Tayang (HET){' '} + <span className=' opacity-60'>(Opsional)</span> + </label> + </div> + <div className='w-3/5'> + <textarea + {...register('hargaTayang')} + placeholder='Jelaskan detail HET yang anda miliki' + type='textarea' + className='form-input' + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.hargaTayang?.message} + </div> + </div> + </div> + + <div className={`flex flex-row justify-between items-start`}> + <div className='w-2/5 text-nowrap'> + <label + className={`form-label ${isKonfirmasi && 'text-wrap'}`} + > + Tipe Kategori Produk yang Digunakan + </label> + {!isKonfirmasi && ( + <span className='text-xs opacity-60'> + Pilih kategori produk bisa lebih dari 1 + </span> + )} + </div> + <div className='w-3/5 flex flex-col'> + <div className='flex flex-row justify-between gap-2'> + <div + className='flex flex-col gap-2' + // ref={categoryProdukRef} + > + {firstColumn.map((item) => ( + <Checkbox + colorScheme='red' + key={item.id} + onChange={() => handleCheckboxChange(item.id)} + isChecked={isChecked(item.id)} + > + {item.name} + </Checkbox> + ))} + </div> + <div className='flex flex-col gap-2 '> + {secondColumn.map((item) => ( + <Checkbox + colorScheme='red' + key={item.id} + isChecked={isChecked(item.id)} + onChange={() => handleCheckboxChange(item.id)} + > + {item.name} + </Checkbox> + ))} + </div> + </div> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.categoryProduk?.message} + </div> + </div> + </div> + + <div className='w-full flex flex-row'> + <div className='w-2/5'> + <label className='form-label text-nowrap'>Merk Dagang</label> + </div> + <div className='w-3/5'> + <input + {...register('merkDagang')} + placeholder='Merk 1, Merk 2, Merk 3' + type='text' + className='form-input' + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.merkDagang?.message} + </div> + </div> + </div> + + <div className='flex flex-row justify-between items-start'> + <div className='w-2/5'> + <label className='form-label text-wrap '> + Apakah anda memiliki Form Pengajuan Tempo? + </label> + </div> + <div className='w-3/5 flex flex-col justify-start'> + <div className='flex gap-x-4'> + <RadioGroup + onChange={handleCheckboxPortalChange} + value={watch('isPengajuanTempo')} + > + <Stack direction='row'> + <Radio colorScheme='red' value='ada'> + Ya, ada + </Radio> + <Radio colorScheme='red' value='tidak'> + Tidak ada + </Radio> + </Stack> + </RadioGroup> + </div> + + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.isPengajuanTempo?.message} + </div> + </div> + </div> + + <div className='flex flex-row justify-between items-center'> + <div className='w-2/5'> + <label className='form-label text-nowrap'>Durasi Tempo</label> + {!isKonfirmasi && ( + <span className='text-xs opacity-60'> + Pilih durasi tempo yang anda inginkan + </span> + )} + </div> + <div className='w-3/5 flex flex-col '> + <div className='flex flex-row items-center gap-3'> + <div className={`${!isKonfirmasi && 'w-[25%]'}`}> + <Controller + name='tempoDuration' + control={control} + render={(props) => ( + <HookFormSelect + {...props} + options={dataTempo} + placeholder={'Pilih Durasi Tempo'} + /> + )} + /> + + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.tempoDuration?.message} + </div> + </div> + </div> + </div> + </div> + + <div className='w-full flex flex-row'> + <div className='w-2/5'> + <label className='form-label text-nowrap'> + Jumlah Kredit Limit + </label> + {!isKonfirmasi && ( + <span className='opacity-65 text-xs'> + isi dengan kredit limit perusahaan anda + </span> + )} + </div> + <div className='w-3/5'> + <input + value={kreditLimitFormat} + onChange={handleKreditLimitChange} + placeholder='Masukkan jumlah kredit limit' + type='text' + className='form-input' + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.kreditLimit?.message} + </div> + </div> + </div> + + <div className='w-full flex flex-row'> + <div className='w-2/5'> + <label className='form-label text-nowrap'> + Waktu Pengiriman + </label> + {!isKonfirmasi && ( + <span className='opacity-65 text-xs'> + isi dengan waktu pengiriman anda + </span> + )} + </div> + <div className='w-3/5 flex flex-row gap-2'> + <div className='w-1/3'> + <input + {...register('waktuPengiriman')} + placeholder='Masukkan waktu pengiriman' + type='text' + className='form-input' + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.waktuPengiriman?.message} + </div> + </div> + <div className='w-2/3 '> + <div className='flex flex-row items-center gap-2'> + <label className=' text-nowrap text-sm opacity-70 italic'> + terhitung sejak + </label> + + <Controller + name='terhitungSejak' + control={control} + render={(props) => ( + <HookFormSelect + {...props} + options={dataTerhitungSejak} + placeholder={'waktu pengiriman'} + /> + )} + /> + </div> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.terhitungSejak?.message} + </div> + </div> + </div> + </div> + + <div className=''> + {/* <div> + <ReCAPTCHA + ref={recaptchaRef} + sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE} + /> + </div> */} + </div> + <div className='flex justify-end'> + {/* <Button + colorScheme='red' + className='w-full md:w-fit' + type='submit' + > + Daftar Merchant{' '} + {<ChevronRightIcon className='w-5' color='white' />} + </Button> */} + {!isKonfirmasi && ( + <div> + <span className='text-xs opacity-60'> + *Pastikan data yang anda masukan sudah benar dan sesuai + </span> + <button + type='submit' + className='btn-light bg-red-500 rounded-lg text-white w-fit py-2 px-4 md:w-fit mt-2 ml-0 md:ml-auto flex justify-between hover:bg-red-400' + > + <span className={` `}>Langkah Selanjutnya</span> + {<ChevronRightIcon className='w-5' />} + </button> + </div> + )} + </div> + </form> + <PageContent path='/daftar-merchant' /> + </div> + </div> + </DesktopView> + <MobileView> + <div className='container flex flex-col items-star py-4'> + {!isKonfirmasi && ( + <h2 className='font-semibold mb-6 text-xl'>Informasi Vendor</h2> + )} + + <div className='w-full mt-4'> + <form + onSubmit={handleSubmit(onSubmitHandler)} + className='flex flex-col gap-4' + > + <div className='w-full flex flex-col'> + <div className='w-full'> + <label className='form-label text-nowrap'> + Harga Tayang (HET){' '} + <span className=' opacity-60'>(Opsional)</span> + </label> + <input + {...register('hargaTayang')} + placeholder='Jelaskan detail HET yang anda miliki' + type='textarea' + className='form-input' + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.hargaTayang?.message} + </div> + </div> + </div> + <div + className={`flex flex-col gap-2 justify-between ${ + isKonfirmasi ? 'items-start' : 'items-start' + }`} + > + <label className='form-label '> + Tipe Kategori Produk yang Digunakan + </label> + <div className='flex flex-col justify-between gap-2 '> + <div className='flex flex-col gap-2'> + {firstColumn.map((item) => ( + <Checkbox + size='sm' + colorScheme='red' + key={item.id} + onChange={() => handleCheckboxChange(item.id)} + isChecked={isChecked(item.id)} + > + {item.name} + </Checkbox> + ))} + </div> + <div className='flex flex-col gap-2'> + {secondColumn.map((item) => ( + <Checkbox + size='sm' + colorScheme='red' + key={item.id} + isChecked={isChecked(item.id)} + onChange={() => handleCheckboxChange(item.id)} + > + {item.name} + </Checkbox> + ))} + </div> + </div> + + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.categoryProduk?.message} + </div> + </div> + <div className='w-full flex flex-col'> + <div className='w-full'> + <label className='form-label text-nowrap'>Merk Dagang</label> + <input + {...register('merkDagang')} + placeholder='Merk 1, Merk 2, Merk 3' + type='text' + className='form-input' + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.merkDagang?.message} + </div> + </div> + </div> + <div className='w-full flex flex-col'> + <label className='form-label text-nowrap'> + Apakah anda memiliki Form Pengajuan Tempo? + </label> + <div className='flex gap-x-4'> + <RadioGroup + onChange={handleCheckboxPortalChange} + value={watch('isPengajuanTempo')} + > + <Stack direction='row'> + <Radio colorScheme='red' value='ada'> + Ya, ada + </Radio> + <Radio colorScheme='red' value='tidak'> + Tidak ada + </Radio> + </Stack> + </RadioGroup> + </div> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.isPengajuanTempo?.message} + </div> + </div> + + <div className='w-full flex flex-col'> + <label className='form-label text-nowrap'>Durasi Tempo</label> + <Controller + name='tempoDuration' + control={control} + render={(props) => ( + <HookFormSelect + {...props} + options={dataTempo} + placeholder={'Pilih Durasi Tempo'} + /> + )} + /> + + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.tempoDuration?.message} + </div> + {!isKonfirmasi && ( + <span className='text-xs opacity-60'> + Pilih durasi tempo yang anda inginkan + </span> + )} + </div> + <div className='w-full flex flex-col'> + <label className='form-label text-nowrap'> + Jumlah Kredit Limit + </label> + <input + value={kreditLimitFormat} + onChange={handleKreditLimitChange} + placeholder='Masukkan jumlah kredit limit' + type='text' + className='form-input' + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.kreditLimit?.message} + </div> + {!isKonfirmasi && ( + <span className='opacity-65 text-xs'> + isi dengan kredit limit perusahaan anda + </span> + )} + </div> + <div className='w-full flex flex-col'> + <label className='form-label text-nowrap'> + {' '} + Waktu Pengiriman + </label> + <div className='w-full flex flex-row gap-2'> + <div className='w-1/3'> + <input + {...register('waktuPengiriman')} + placeholder='Masukkan waktu pengiriman' + type='text' + className='form-input' + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.waktuPengiriman?.message} + </div> + </div> + <div className='w-2/3 '> + <div className='flex flex-row items-center gap-2'> + <label className=' text-nowrap text-sm opacity-70 italic'> + terhitung sejak + </label> + + <Controller + name='terhitungSejak' + control={control} + render={(props) => ( + <HookFormSelect + {...props} + options={dataTerhitungSejak} + placeholder={'waktu pengiriman'} + /> + )} + /> + </div> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.terhitungSejak?.message} + </div> + </div> + </div> + {!isKonfirmasi && ( + <span className='opacity-65 text-xs'> + isi dengan waktu pengiriman anda + </span> + )} + </div> + + <div className=''> + {/* <div> + <ReCAPTCHA + ref={recaptchaRef} + sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE} + /> + </div> */} + </div> + <div className='flex justify-center w-full '> + {/* <Button + colorScheme='red' + className='w-full md:w-fit' + type='submit' + > + Daftar Merchant{' '} + {<ChevronRightIcon className='w-5' color='white' />} + </Button> */} + {!isKonfirmasi && ( + <div className='w-full'> + <span className='text-xs opacity-60'> + *Pastikan data yang anda masukan sudah benar dan sesuai + </span> + <button + type='submit' + className='btn-light bg-red-500 rounded-lg text-white w-full py-2 px-4 md:w-fit mt-2 ml-0 md:ml-auto flex justify-center hover:bg-red-400' + > + <span className={` `}>Langkah Selanjutnya</span> + {<ChevronRightIcon className='w-5' />} + </button> + </div> + )} + </div> + </form> + <PageContent path='/daftar-merchant' /> + </div> + </div> + </MobileView> + </> + ); +}); +const validationSchema = Yup.object().shape({ + categoryProduk: Yup.string().required('Harus di-pilih'), + merkDagang: Yup.string().required('Harus di-isi'), + isPengajuanTempo: Yup.string().required('Harus di-pilih'), + tempoDuration: Yup.string().required('Harus di-pilih'), + kreditLimit: Yup.string().required('Harus di-isi'), + waktuPengiriman: Yup.string().required('Harus di-isi'), + terhitungSejak: Yup.string().required('Harus di-pilih'), +}); +const defaultValues = { + categoryProduk: '', + merkDagang: '', + isPengajuanTempo: '', + tempoDuration: '', + kreditLimit: '', + waktuPengiriman: '', + terhitungSejak: '', +}; + +export default InformasiVendor; |
