From ead46a6d760850530946926b390a8954ca64e1c2 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 17 Oct 2024 17:06:58 +0700 Subject: update pengajuan tempo --- .../register/stores/usePengajuanTempoStore.ts | 86 ++- src-migrate/types/tempo.ts | 20 +- src-migrate/validations/tempo.ts | 30 + .../pengajuan-tempo/component/KontakPerusahaan.jsx | 725 +++++++++++++++++++++ .../pengajuan-tempo/component/PengajuanTempo.jsx | 5 + .../component/informasiPerusahaan.jsx | 2 +- 6 files changed, 864 insertions(+), 4 deletions(-) diff --git a/src-migrate/modules/register/stores/usePengajuanTempoStore.ts b/src-migrate/modules/register/stores/usePengajuanTempoStore.ts index 6f3bc13d..8891e6ea 100644 --- a/src-migrate/modules/register/stores/usePengajuanTempoStore.ts +++ b/src-migrate/modules/register/stores/usePengajuanTempoStore.ts @@ -1,6 +1,6 @@ import { create } from 'zustand'; -import { TempoProps } from '~/types/tempo'; -import { TempoSchema } from '~/validations/tempo'; +import { TempoProps, TempoPropsKontakPerson } from '~/types/tempo'; +import { TempoSchema, TempoSchemaKontakPerson } from '~/validations/tempo'; import { boolean, ZodError } from 'zod'; type State = { @@ -91,3 +91,85 @@ export const usePengajuanTempoStore = create((set, get) => ({ }, }), })); + +type StateKontakPerson = { + form: TempoPropsKontakPerson; + errors: { + [key in keyof TempoPropsKontakPerson]?: string; + }; + isCheckedTNC: boolean; + isOpenTNC: boolean; + isValidCaptcha: boolean; +}; +export const usePengajuanTempoStoreKontakPerson = create< + StateKontakPerson & Action +>((set, get) => ({ + form: { + direkturName: '', + direkturMobile: '', + direkturEmail: '', + industry_id: '', + street: '', + state: '', + city: '', + zip: '', + bankName: '', + accountName: '', + accountNumber: '', + estimasi: '', + tempoDuration: '', + bersedia: '', + categoryProduk: '', + tempoLimit: '', + }, + updateForm: (name, value) => + set((state) => ({ form: { ...state.form, [name]: value } })), + + errors: {}, + validate: () => { + try { + TempoSchemaKontakPerson.parse(get().form); + set({ errors: {} }); + } catch (error) { + if (error instanceof ZodError) { + const errors: StateKontakPerson['errors'] = {}; + error.errors.forEach( + (e) => (errors[e.path[0] as keyof TempoPropsKontakPerson] = e.message) + ); + set({ errors }); + } + } + }, + + isCheckedTNC: false, + toggleCheckTNC: () => set((state) => ({ isCheckedTNC: !state.isCheckedTNC })), + + isOpenTNC: false, + openTNC: () => set(() => ({ isOpenTNC: true })), + closeTNC: () => set(() => ({ isOpenTNC: false })), + + isValidCaptcha: false, + updateValidCaptcha: (value) => set(() => ({ isValidCaptcha: value })), + + resetForm: () => + set({ + form: { + direkturName: '', + direkturMobile: '', + direkturEmail: '', + industry_id: '', + street: '', + state: '', + city: '', + zip: '', + bankName: '', + accountName: '', + accountNumber: '', + estimasi: '', + tempoDuration: '', + bersedia: '', + categoryProduk: '', + tempoLimit: '', + }, + }), +})); diff --git a/src-migrate/types/tempo.ts b/src-migrate/types/tempo.ts index f8a3c5b8..a4bd3d0a 100644 --- a/src-migrate/types/tempo.ts +++ b/src-migrate/types/tempo.ts @@ -1,4 +1,4 @@ -import { TempoSchema } from '~/validations/tempo'; +import { TempoSchema, TempoSchemaKontakPerson } from '~/validations/tempo'; import { OdooApiRes } from './odoo'; import { z } from 'zod'; @@ -18,9 +18,27 @@ export type tempoProps = { bersedia: string; }; +export type tempoPropsKontakPerson = { + direkturName: string; + direkturMobile: string; + direkturEmail: string; + industry_id: string; + street: string; + state: string; + city: string; + zip: string; + bankName: string; + accountName: string; + accountNumber: string; + estimasi: string; + tempoDuration: string; + bersedia: string; +}; + export type TempoApiProps = OdooApiRes; export type TempoProps = z.infer; +export type TempoPropsKontakPerson = z.infer; export type TempoResApiProps = { Tempo: boolean; diff --git a/src-migrate/validations/tempo.ts b/src-migrate/validations/tempo.ts index 6999c1c6..dca60869 100644 --- a/src-migrate/validations/tempo.ts +++ b/src-migrate/validations/tempo.ts @@ -26,3 +26,33 @@ export const TempoSchema = z.object({ .string() .min(1, { message: 'Category produk harus dipilih' }), }); +export const TempoSchemaKontakPerson = z.object({ + direkturName: z.string().min(1, { message: 'Nama harus diisi' }), + direkturMobile: z + .string() + .min(1, { message: 'Nomor telepon harus diisi' }) + .refine((val) => /^\d{10,12}$/.test(val), { + message: 'Format nomor telepon tidak valid, contoh: 081234567890', + }), + direkturEmail: z + .string() + .min(1, { message: 'Email harus diisi' }) + .email({ message: 'Email harus menggunakan format example@mail.com' }), + street: z.string().min(1, { message: 'Alamat harus diisi' }), + industry_id: z.string().min(1, { message: 'Jenis usaha harus dipilih' }), + zip: z.string().min(1, { message: 'Kode pos harus diisi' }), + state: z.string().min(1, { message: 'Provinsi harus dipilih' }), + city: z.string().min(1, { message: 'Kota harus dipilih' }), + bankName: z.string().min(1, { message: 'Nama bank harus diisi' }), + accountName: z.string().min(1, { message: 'Nama rekening harus diisi' }), + accountNumber: z.string().min(1, { message: 'Nomor rekening harus diisi' }), + estimasi: z + .string() + .min(1, { message: 'Estimasi pemmbelian pertahun harus diisi' }), + tempoDuration: z.string().min(1, { message: 'Durasi tempo harus dipilih' }), + tempoLimit: z.string().min(1, { message: 'Limit tempo harus dipilih' }), + bersedia: z.string().min(1, { message: 'Harus dipilih' }), + categoryProduk: z + .string() + .min(1, { message: 'Category produk harus dipilih' }), +}); diff --git a/src/lib/pengajuan-tempo/component/KontakPerusahaan.jsx b/src/lib/pengajuan-tempo/component/KontakPerusahaan.jsx index e69de29b..d292449b 100644 --- a/src/lib/pengajuan-tempo/component/KontakPerusahaan.jsx +++ b/src/lib/pengajuan-tempo/component/KontakPerusahaan.jsx @@ -0,0 +1,725 @@ +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 { Radio, RadioGroup, Stack, Checkbox } from '@chakra-ui/react'; +import { usePengajuanTempoStore } from '../../../../src-migrate/modules/register/stores/usePengajuanTempoStore'; +const KontakPerusahaan = ({ chekValid, buttonSubmitClick }) => { + const { control, watch } = useForm(); + const { form, errors, validate, updateForm } = usePengajuanTempoStore(); + const [industries, setIndustries] = useState([]); + const [selectedCategory, setSelectedCategory] = useState(''); + const [states, setState] = useState([]); + const [cities, setCities] = useState([]); + const [bersedia, setBersedia] = useState(null); + const category_produk = [ + { id: 1, name: 'Pengaman, Kesehatan & Keamanan' }, + { id: 2, name: 'Perkakas Tangan, Listrik & Pneumatic' }, + { id: 3, name: 'Mesin Industrial' }, + { id: 4, name: 'Mesin Pertanian & Perkebunan' }, + { id: 5, name: 'Mesin Pembersih & Janitorial' }, + { id: 6, name: 'Cairan Berbahan Kimia' }, + { id: 7, name: 'Perlengkapan Pengukuran & Pengujian' }, + { id: 8, name: 'Peralatan Listrik & Elektronik' }, + { id: 9, name: 'Perlengkapan Logistik & Gudang' }, + { id: 10, name: 'Peralatan Kantor & Stationery' }, + { id: 11, name: 'Komponen & Aksesoris' }, + { id: 12, name: 'Peralatan Horeca & Food Service' }, + ]; + + useEffect(() => { + const loadState = async () => { + let dataState = await stateApi(); + dataState = dataState.map((state) => ({ + value: state.id, + label: state.name, + })); + setState(dataState); + }; + loadState(); + }, []); + + const watchState = watch('state'); + useEffect(() => { + updateForm('city', ''); + 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'); + useEffect(() => { + if (watchCity) { + updateForm('city', `${watchCity}`); + validate(); + } + }, [watchCity]); + + 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(); + setSelectedCategory(selectedIndustryType.category); + } + }, [watch('industry_id'), industries]); + + 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); + console.log('formattedValue', formattedValue); + updateForm('estimasi', formattedValue.replace(/^Rp\s*/, '')); + validate(); + }; + const onChangeTempoDuration = (e) => { + updateForm('tempoDuration', `${e}`); + validate(); + }; + + const onChangeTempoLimit = (e) => { + updateForm('tempoLimit', `${e}`); + validate(); + }; + const handleCheckboxBersediaChange = (value) => { + if (value === 'bersedia') { + setBersedia(true); + } else if (value === 'tidakBersedia') { + setBersedia(false); + } + updateForm('bersedia', `${value === 'bersedia'}`); + validate(); + }; + const [selectedIds, setSelectedIds] = useState([]); + + const handleCheckboxChange = (id) => { + setSelectedIds((prevSelected) => + prevSelected.includes(id) + ? prevSelected.filter((selectedId) => selectedId !== id) + : [...prevSelected, id] + ); + updateForm('categoryProduk', `${selectedIds}`); + validate(); + }; + + 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 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 categoryProdukRef = useRef(null); + const tempoLimitRef = useRef(null); + useEffect(() => { + const loadIndustries = async () => { + if (!isFormValid) { + const options = { + behavior: 'smooth', + block: 'center', + }; + if (errors.name && nameRef.current) { + nameRef.current.scrollIntoView(options); + return; + } + if (errors.industry_id && 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.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.categoryProduk && categoryProdukRef.current) { + categoryProdukRef.current.scrollIntoView(options); + return; + } + if (errors.tempoLimit && tempoLimitRef.current) { + tempoLimitRef.current.scrollIntoView(options); + return; + } + } + }; + loadIndustries(); + }, [buttonSubmitClick, chekValid]); + return ( + <> +
+

Kontak Person

+
+
+
+
+
+ +
+
+ + + Format: PT. INDOTEKNIK DOTCOM GEMILANG + + {chekValid && ( +
+ {errors.name} +
+ )} +
+
+
+
+ + + isi detail perusahaan sesuai dengan nama yang terdaftar + +
+
+ ( + + )} + /> + {selectedCategory && ( + + Kategori : {selectedCategory} + + )} + {chekValid && ( +
+ {errors.industry_id} +
+ )} +
+
+ +
+
+ + + alamat sesuai dengan alamat kantor pusat + +
+
+
+ + {chekValid && ( +
+ {errors.street} +
+ )} +
+
+
+ ( + + )} + /> + {chekValid && ( +
+ {errors.state} +
+ )} +
+
+ ( + + )} + /> + {chekValid && ( +
+ {errors.city} +
+ )} +
+
+ + {chekValid && ( +
+ {errors.zip} +
+ )} +
+
+
+
+
+
+ + + isi no telfon perusahaan yang sesuai + +
+
+ + {chekValid && ( +
+ {errors.mobile} +
+ )} +
+
+ +
+
+ + + Isi detail data bank perusahaan anda + +
+
+
+ + + Format: BCA, Mandiri, CIMB, BNI dll + + {chekValid && ( +
+ {errors.bankName} +
+ )} +
+
+ + Format: John Doe + {chekValid && ( +
+ {errors.accountName} +
+ )} +
+
+ + Format: 01234567896 + {chekValid && ( +
+ {errors.accountNumber} +
+ )} +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ 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 && ( +
+ {errors.estimasi} +
+ )} +
+
+ +
+
+ + + Pilih durasi tempo yang anda inginkan + +
+
+
+ + + + 7 Hari + + + 14 Hari + + + 30 Hari + + + + {chekValid && ( +
+ {errors.tempoDuration} +
+ )} +
+
+
+ + + Ajukan nilai limit yang anda mau + +
+
+ + + {/* Kolom 1 */} + + + 5.000.000 + + + 10.000.000 + + + 15.000.000 + + + 20.000.000 + + + + {/* Kolom 2 */} + + + 25.000.000 + + + 30.000.000 + + + 35.000.000 + +
+ + { + const value = e.target.value; + const formattedValue = formatRupiah(value); + updateForm('tempoLimit', formattedValue); // Mengupdate nilai di react-hook-form + }} + /> +
+
+
+
+ {chekValid && ( +
+ {errors.tempoLimit} +
+ )} +
+
+
+
+ +
+ *Durasi dan limit dapat berbeda sesuaui dengan verifikasi oleh tim + Indoteknik.com +
+ +
+
+ + + Pilih produk bisa lebih dari 1 + +
+
+
+ handleCheckboxBersediaChange('bersedia')} + value={true} + size='md' + > + Saya bersedia + + handleCheckboxBersediaChange('tidakBersedia')} + value={false} + size='md' + > + Tidak bersedia + +
+ {chekValid && ( +
+ {errors.estimasi} +
+ )} +
+
+ +
+
+ + + Pilih produk bisa lebih dari 1 + +
+
+
+
+ {firstColumn.map((item) => ( + handleCheckboxChange(item.id)} + > + {item.name} + + ))} +
+
+ {secondColumn.map((item) => ( + handleCheckboxChange(item.id)} + > + {item.name} + + ))} +
+
+ {chekValid && ( +
+ {errors.categoryProduk} +
+ )} +
+
+
+
+ + ); +}; + +export default KontakPerusahaan; diff --git a/src/lib/pengajuan-tempo/component/PengajuanTempo.jsx b/src/lib/pengajuan-tempo/component/PengajuanTempo.jsx index 5f250438..c15189d1 100644 --- a/src/lib/pengajuan-tempo/component/PengajuanTempo.jsx +++ b/src/lib/pengajuan-tempo/component/PengajuanTempo.jsx @@ -2,6 +2,7 @@ import React from 'react'; import { useMemo, useState, useEffect, useRef } from 'react'; import Stepper from './Stepper'; import InformasiPerusahaan from './informasiPerusahaan'; +import KontakPerusahaan from './KontakPerusahaan'; import { Controller, useForm } from 'react-hook-form'; import { usePengajuanTempoStore } from '../../../../src-migrate/modules/register/stores/usePengajuanTempoStore'; import { ChevronRightIcon } from '@heroicons/react/24/outline'; @@ -13,6 +14,10 @@ const PengajuanTempo = () => { const isFormValid = useMemo(() => Object.keys(errors).length === 0, [errors]); const [buttonSubmitClick, setButtonSubmitClick] = useState(false); const stepDivs = [ + , { return ( <>
-

Informasi Perusahaan

+

Informasi Perusahaan

-- cgit v1.2.3