diff options
19 files changed, 7696 insertions, 0 deletions
diff --git a/package.json b/package.json index 130e2f0c..3990e5cd 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@emotion/styled": "^11.11.0", "@heroicons/react": "^2.0.13", "@hookform/resolvers": "^2.9.10", + "@ramonak/react-progress-bar": "^5.3.0", "@react-email/components": "^0.0.2", "@react-email/render": "^0.0.6", "@tailwindcss/line-clamp": "^0.4.2", diff --git a/public/images/REGISTRASI-TEMPO.svg b/public/images/REGISTRASI-TEMPO.svg new file mode 100644 index 00000000..93746f25 --- /dev/null +++ b/public/images/REGISTRASI-TEMPO.svg @@ -0,0 +1,9 @@ +<svg width="494" height="498" viewBox="0 0 494 498" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<rect y="-0.00390625" width="494" height="498" fill="url(#pattern0_5944_7744)"/> +<defs> +<pattern id="pattern0_5944_7744" patternContentUnits="objectBoundingBox" width="1" height="1"> +<use xlink:href="#image0_5944_7744" transform="matrix(0.00203656 0 0 0.0020202 -0.0030303 0)"/> +</pattern> +<image id="image0_5944_7744" width="494" height="495" xlink:href="data:image/png;base64,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"/> +</defs> +</svg> diff --git a/src-migrate/modules/register/stores/usePengajuanTempoStore.ts b/src-migrate/modules/register/stores/usePengajuanTempoStore.ts new file mode 100644 index 00000000..47168a2a --- /dev/null +++ b/src-migrate/modules/register/stores/usePengajuanTempoStore.ts @@ -0,0 +1,386 @@ +import { create } from 'zustand'; +import { + TempoProps, + TempoPropsKontakPerson, + TempoPropsPengiriman, + TempoPropsSupplier, + TempoPropsDokumen, +} from '~/types/tempo'; +import { + TempoSchema, + TempoSchemaKontakPerson, + TempoSchemaPengiriman, + TempoSchemaSupplier, + TempoSchemaDokumen, +} from '~/validations/tempo'; +import { boolean, ZodError } from 'zod'; + +type State = { + form: TempoProps; + errors: { + [key in keyof TempoProps]?: string; + }; + isCheckedTNC: boolean; + isOpenTNC: boolean; + isValidCaptcha: boolean; +}; + +type Action = { + updateForm: (name: string, value: string) => void; + updateValidCaptcha: (value: boolean) => void; + toggleCheckTNC: () => void; + openTNC: () => void; + closeTNC: () => void; + validate: () => void; + resetForm: () => void; +}; + +export const usePengajuanTempoStore = create<State & Action>((set, get) => ({ + form: { + name: '', + industry_id: '', + street: '', + state: '', + city: '', + zip: '', + mobile: '', + bankName: '', + accountName: '', + accountNumber: '', + estimasi: '', + tempoDuration: '', + bersedia: '', + categoryProduk: '', + tempoLimit: '', + }, + updateForm: (name, value) => + set((state) => ({ form: { ...state.form, [name]: value } })), + + errors: {}, + validate: () => { + try { + TempoSchema.parse(get().form); + set({ errors: {} }); + } catch (error) { + if (error instanceof ZodError) { + const errors: State['errors'] = {}; + error.errors.forEach( + (e) => (errors[e.path[0] as keyof TempoProps] = 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: { + name: '', + industry_id: '', + street: '', + state: '', + city: '', + zip: '', + mobile: '', + bankName: '', + accountName: '', + accountNumber: '', + estimasi: '', + tempoDuration: '', + bersedia: '', + categoryProduk: '', + tempoLimit: '', + }, + }), +})); + +type StateKontakPerson = { + formKontakPerson: TempoPropsKontakPerson; + errorsKontakPerson: { + [key in keyof TempoPropsKontakPerson]?: string; + }; +}; +type ActionKontakPerson = { + updateFormKontakPerson: (name: string, value: string) => void; + + validateKontakPerson: () => void; + resetFormKontakPerson: () => void; +}; +export const usePengajuanTempoStoreKontakPerson = create< + StateKontakPerson & ActionKontakPerson +>((set, get) => ({ + formKontakPerson: { + direkturName: '', + direkturMobile: '', + direkturEmail: '', + purchasingName: '', + purchasingEmail: '', + financeMobile: '', + financeName: '', + financeEmail: '', + purchasingMobile: '', + }, + updateFormKontakPerson: (name, value) => + set((state) => ({ + formKontakPerson: { ...state.formKontakPerson, [name]: value }, + })), + + errorsKontakPerson: {}, + validateKontakPerson: () => { + try { + TempoSchemaKontakPerson.parse(get().formKontakPerson); + set({ errorsKontakPerson: {} }); + } catch (error) { + if (error instanceof ZodError) { + const errorsKontakPerson: StateKontakPerson['errorsKontakPerson'] = {}; + error.errors.forEach( + (e) => + (errorsKontakPerson[e.path[0] as keyof TempoPropsKontakPerson] = + e.message) + ); + set({ errorsKontakPerson }); + } + } + }, + + resetFormKontakPerson: () => + set({ + formKontakPerson: { + direkturName: '', + direkturMobile: '', + direkturEmail: '', + purchasingName: '', + purchasingEmail: '', + financeName: '', + financeMobile: '', + financeEmail: '', + purchasingMobile: '', + }, + }), +})); + +type StatePengiriman = { + formPengiriman: TempoPropsPengiriman; + errorsPengiriman: { + [key in keyof TempoPropsPengiriman]?: string; + }; +}; +type ActionPengiriman = { + updateFormPengiriman: (name: string, value: string) => void; + + validatePengiriman: () => void; + resetFormPengiriman: () => void; +}; +export const usePengajuanTempoStorePengiriman = create< + StatePengiriman & ActionPengiriman +>((set, get) => ({ + formPengiriman: { + PICName: '', + streetPengiriman: '', + statePengiriman: '', + cityPengiriman: '', + zipPengiriman: '', + invoicePic: '', + isSameAddrees: '', + streetInvoice: '', + stateInvoice: '', + cityInvoice: '', + everyWeekday: false, + everyWeekdayInput: '', + everyWeek: false, + everyWeekInput: '', + tukarInvoice: false, + tukarInvoiceInput: '', + everyWeekdayPembayaran: false, + everyWeekdayInputPembayaran: '', + everyWeekPembayaran: false, + everyWeekInputPembayaran: '', + tukarInvoicePembayaran: false, + tukarInvoiceInputPembayaran: '', + dokumenPengiriman: '', + dokumenPengirimanInput: '', + dokumenPengirimanInvoice: '', + }, + updateFormPengiriman: (name, value) => + set((state) => ({ + formPengiriman: { ...state.formPengiriman, [name]: value }, + })), + + errorsPengiriman: {}, + validatePengiriman: () => { + try { + TempoSchemaPengiriman.parse(get().formPengiriman); + set({ errorsPengiriman: {} }); + } catch (error) { + if (error instanceof ZodError) { + const errorsPengiriman: StatePengiriman['errorsPengiriman'] = {}; + error.errors.forEach( + (e) => + (errorsPengiriman[e.path[0] as keyof TempoPropsPengiriman] = + e.message) + ); + set({ errorsPengiriman }); + } + } + }, + + resetFormPengiriman: () => + set({ + formPengiriman: { + PICName: '', + streetPengiriman: '', + statePengiriman: '', + cityPengiriman: '', + zipPengiriman: '', + invoicePic: '', + streetInvoice: '', + stateInvoice: '', + cityInvoice: '', + isSameAddrees: '', + everyWeekday: false, + everyWeekdayInput: '', + everyWeek: false, + everyWeekInput: '', + tukarInvoice: false, + tukarInvoiceInput: '', + everyWeekdayPembayaran: false, + everyWeekdayInputPembayaran: '', + everyWeekPembayaran: false, + everyWeekInputPembayaran: '', + tukarInvoicePembayaran: false, + tukarInvoiceInputPembayaran: '', + dokumenPengiriman: '', + dokumenPengirimanInput: '', + dokumenPengirimanInvoice: '', + dokumenPengirimanInvoiceInput: '', + }, + }), +})); +type StateDokumen = { + formDokumen: TempoPropsDokumen; + errorsDokumen: { + [key in keyof TempoPropsDokumen]?: string; + }; +}; +type ActionDokumen = { + updateFormDokumen: ( + name: string, + fileName: string, + fileFormat: string, + value: string + ) => void; + + validateDokumen: () => void; + resetFormDokumen: () => void; + getJumlahDokumenDiisi: () => void; +}; +export const usePengajuanTempoStoreDokumen = create< + StateDokumen & ActionDokumen +>((set, get) => ({ + formDokumen: { + dokumenNib: { name: '', format: '', base64: '' }, + dokumenNpwp: { name: '', format: '', base64: '' }, + dokumenSppkp: { name: '', format: '', base64: '' }, + dokumenAktaPerubahan: { name: '', format: '', base64: '' }, + dokumenKtpDirut: { name: '', format: '', base64: '' }, + dokumenAktaPendirian: { name: '', format: '', base64: '' }, + dokumenLaporanKeuangan: { name: '', format: '', base64: '' }, + dokumenFotoKantor: { name: '', format: '', base64: '' }, + dokumenTempatBekerja: { name: '', format: '', base64: '' }, + }, + + // Memperbarui dokumen dengan name, format, dan base64 + updateFormDokumen: (name, fileName, fileFormat, value) => + set((state) => ({ + formDokumen: { + ...state.formDokumen, + [name]: { + name: fileName, + format: fileFormat, + base64: value, + }, + }, + })), + + errorsDokumen: {}, + validateDokumen: () => { + try { + TempoSchemaDokumen.parse(get().formDokumen); + set({ errorsDokumen: {} }); + } catch (error) { + if (error instanceof ZodError) { + const errorsDokumen: StateDokumen['errorsDokumen'] = {}; + error.errors.forEach( + (e) => + (errorsDokumen[e.path[0] as keyof TempoPropsDokumen] = e.message) + ); + set({ errorsDokumen }); + } + } + }, + + getJumlahDokumenDiisi: () => { + const formDokumen = get().formDokumen; + // Menghitung jumlah field yang base64 tidak kosong + const jumlahTerisi = Object.values(formDokumen).filter( + (dokumen) => dokumen.base64 !== '' + ).length; + return jumlahTerisi; + }, + + resetFormDokumen: () => + set({ + formDokumen: { + dokumenNib: { name: '', format: '', base64: '' }, + dokumenNpwp: { name: '', format: '', base64: '' }, + dokumenSppkp: { name: '', format: '', base64: '' }, + dokumenAktaPerubahan: { name: '', format: '', base64: '' }, + dokumenKtpDirut: { name: '', format: '', base64: '' }, + dokumenAktaPendirian: { name: '', format: '', base64: '' }, + dokumenLaporanKeuangan: { name: '', format: '', base64: '' }, + dokumenFotoKantor: { name: '', format: '', base64: '' }, + dokumenTempatBekerja: { name: '', format: '', base64: '' }, + }, + }), +})); + +type StateSupplier = { + hasSavedata: boolean; + formSupplier: TempoPropsSupplier[]; + errorsSupplier: { + [key in keyof TempoPropsSupplier]?: string; + }; +}; +type ActionSupplier = { + updateFormSupplier: (data: TempoPropsSupplier[]) => void; + updateHasSave: (data: boolean) => void; + validateSupplier: () => void; +}; +export const usePengajuanTempoStoreSupplier = create< + StateSupplier & ActionSupplier +>((set, get) => ({ + formSupplier: [], + hasSavedata: false, + updateFormSupplier: (data) => { + set(() => ({ + formSupplier: data, + })); + }, + updateHasSave: (data) => { + set(() => ({ + hasSavedata: data, + })); + }, + errorsSupplier: {}, + validateSupplier: () => {}, +})); diff --git a/src-migrate/types/tempo.ts b/src-migrate/types/tempo.ts new file mode 100644 index 00000000..815a7557 --- /dev/null +++ b/src-migrate/types/tempo.ts @@ -0,0 +1,127 @@ +import { + TempoSchema, + TempoSchemaKontakPerson, + TempoSchemaPengiriman, + TempoSchemaSupplier, + TempoSchemaDokumen, +} from '~/validations/tempo'; +import { OdooApiRes } from './odoo'; +import { z } from 'zod'; + +export type tempoProps = { + name: string; + industry_id: string; + street: string; + state: string; + city: string; + zip: string; + mobile: string; + bankName: string; + accountName: string; + accountNumber: string; + estimasi: string; + tempoDuration: string; + bersedia: string; +}; + +export type tempoPropsKontakPerson = { + direkturName: string; + direkturMobile: string; + direkturEmail: string; + purchasingName: string; + purchasingEmail: string; + financeMobile: string; + financeEmail: string; + financeName: string; + purchasingMobile: string; +}; +export type tempoPropsPengiriman = { + PICName: string; + streetPengiriman: string; + statePengiriman: string; + cityPengiriman: string; + streetInvoice: string; + zip: string; + invoicePic: string; + isSameAddrees: string; + stateInvoice: string; + cityInvoice: string; + everyWeekday: boolean; + everyWeekdayInput: string; + everyWeek: boolean; + everyWeekInput: string; + tukarInvoice: boolean; + tukarInvoiceInput: string; + everyWeekdayPembayaran: boolean; + everyWeekdayInputPembayaran: string; + everyWeekPembayaran: boolean; + everyWeekInputPembayaran: string; + tukarInvoicePembayaran: boolean; + tukarInvoiceInputPembayaran: string; + dokumenPengiriman: string; + dokumenPengirimanInput: string; + dokumenPengirimanInvoice: string; + dokumenPengirimanInvoiceInput: string; +}; +export type tempoPropsSupplier = { + supplier: string; + pic: string; + telepon: string; + durasiTempo: string; + creditLimit: string; +}; +export type tempoPropsDokumen = { + dokumenNib: { name: string; format: string; base64: string }; + dokumenNpwp: { name: string; format: string; base64: string }; + dokumenSppkp: { name: string; format: string; base64: string }; + dokumenAktaPerubahan: { name: string; format: string; base64: string }; + dokumenKtpDirut: { name: string; format: string; base64: string }; + dokumenAktaPendirian: { name: string; format: string; base64: string }; + dokumenLaporanKeuangan: { name: string; format: string; base64: string }; + dokumenFotoKantor: { name: string; format: string; base64: string }; + dokumenTempatBekerja: { name: string; format: string; base64: string }; +}; + +export type TempoApiProps = OdooApiRes<TempoProps>; + +export type TempoProps = z.infer<typeof TempoSchema>; +export type TempoPropsKontakPerson = z.infer<typeof TempoSchemaKontakPerson>; +export type TempoPropsPengiriman = z.infer<typeof TempoSchemaPengiriman>; +export type TempoPropsSupplier = z.infer<typeof TempoSchemaSupplier>; +export type TempoPropsDokumen = z.infer<typeof TempoSchemaDokumen>; + +export type TempoResApiProps = { + Tempo: boolean; + reason: 'EMAIL_USED' | 'NOT_ACTIVE' | null; +}; + +type ActivationResProps = { + activation: boolean; + user: TempoProps | null; +}; + +export type ActivationTokenProps = { + token: string; +}; + +export type ActivationTokenResApiProps = ActivationResProps & { + reason: 'INVALID_TOKEN' | null; +}; + +export type ActivationOtpProps = { + email: string; + otp: string; +}; + +export type ActivationOtpResApiProps = ActivationResProps & { + reason: 'INVALID_OTP' | null; +}; + +export type ActivationReqProps = { + email: string; +}; + +export type ActivationReqResApiProps = { + activation_request: boolean; + reason: 'NOT_FOUND' | 'ACTIVE' | null; +}; diff --git a/src-migrate/validations/tempo.ts b/src-migrate/validations/tempo.ts new file mode 100644 index 00000000..7f02019c --- /dev/null +++ b/src-migrate/validations/tempo.ts @@ -0,0 +1,157 @@ +import { z } from 'zod'; + +export const TempoSchema = z.object({ + name: z.string().min(1, { message: 'Nama harus diisi' }), + 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' }), + mobile: z + .string() + .min(1, { message: 'Nomor telepon harus diisi' }) + .refine((val) => /^\d{10,12}$/.test(val), { + message: 'Format nomor telepon tidak valid, contoh: 081234567890', + }), + 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' }), +}); + +export const TempoSchemaKontakPerson = z.object({ + direkturName: z.string().min(1, { message: 'Nama harus diisi' }), + financeName: 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', + }), + financeMobile: z + .string() + .min(1, { message: 'Nomor telepon harus diisi' }) + .refine((val) => /^\d{10,12}$/.test(val), { + message: 'Format nomor telepon tidak valid, contoh: 081234567890', + }), + purchasingMobile: z + .string() + .min(1, { message: 'Nomor telepon harus diisi' }) + .refine((val) => /^\d{10,12}$/.test(val), { + message: 'Format nomor telepon tidak valid, contoh: 081234567890', + }), + direkturEmail: z + .string() + .min(1, { message: 'Email harus diisi' }) + .email({ message: 'Email harus menggunakan format example@mail.com' }), + purchasingEmail: z + .string() + .min(1, { message: 'Email harus diisi' }) + .email({ message: 'Email harus menggunakan format example@mail.com' }), + financeEmail: z + .string() + .min(1, { message: 'Email harus diisi' }) + .email({ message: 'Email harus menggunakan format example@mail.com' }), + purchasingName: z.string().min(1, { message: 'Nama harus diisi' }), +}); +export const TempoSchemaPengiriman = z.object({ + PICName: z.string().min(1, { message: 'Nama harus diisi' }), + streetPengiriman: z.string().min(1, { message: 'Alamat harus diisi' }), + statePengiriman: z.string().min(1, { message: 'Provinsi harus dipilih' }), + cityPengiriman: z.string().min(1, { message: 'Kota harus dipilih' }), + zipPengiriman: z.string().min(1, { message: 'Kode pos harus diisi' }), + invoicePic: z.string().min(1, { message: 'Nama pic invoice harus diisi' }), + streetInvoice: z.string().min(1, { message: 'Alamat invoice harus diisi' }), + stateInvoice: z + .string() + .min(1, { message: 'Provinsi invoice harus dipilih' }), + isSameAddrees: z.string(), + cityInvoice: z.string().min(1, { message: 'Kota invoice harus dipilih' }), + everyWeekday: z.boolean().optional(), + everyWeekdayInput: z.string().optional(), + everyWeek: z.boolean().optional(), + everyWeekInput: z.string().optional(), + tukarInvoice: z.boolean().optional(), + tukarInvoiceInput: z.string().optional(), + everyWeekdayPembayaran: z.boolean().optional(), + everyWeekdayInputPembayaran: z.string().optional(), + everyWeekPembayaran: z.boolean().optional(), + everyWeekInputPembayaran: z.string().optional(), + tukarInvoicePembayaran: z.boolean().optional(), + tukarInvoiceInputPembayaran: z.string().optional(), + dokumenPengiriman: z.string().min(1, { + message: 'dokumen lampiran saat pengiriman barang harus dipilih', + }), + dokumenPengirimanInput: z.string().optional(), + dokumenPengirimanInvoice: z.string().min(1, { + message: 'dokumen lampiran saat pengiriman barang harus dipilih', + }), + dokumenPengirimanInvoiceInput: z.string().optional(), +}); +export const TempoSchemaSupplier = z.object({ + supplier: z.string().min(1, { message: 'Nama supplier harus diisi' }), + pic: z.string().min(1, { message: 'Nama PIC harus diisi' }), + telepon: z + .string() + .min(1, { message: 'Nomor telepon harus diisi' }) + .refine((val) => /^\d{10,12}$/.test(val), { + message: 'Format nomor telepon tidak valid, contoh: 081234567890', + }), + durasiTempo: z.string().min(1, { message: 'Durasi tempo harus diisi' }), + creditLimit: z.string().min(1, { message: 'Limit Kredit harus diisi' }), +}); +export const TempoSchemaDokumen = z.object({ + dokumenNib: z.object({ + name: z.string().min(1, { message: 'Nama file harus diisi' }), + format: z.string().min(1, { message: 'Format file harus diisi' }), + base64: z.string().min(1, { message: 'Dokumen harus diisi' }), + }), + dokumenNpwp: z.object({ + name: z.string().min(1, { message: 'Nama file harus diisi' }), + format: z.string().min(1, { message: 'Format file harus diisi' }), + base64: z.string().min(1, { message: 'Dokumen harus diisi' }), + }), + dokumenSppkp: z.object({ + name: z.string().min(1, { message: 'Nama file harus diisi' }), + format: z.string().min(1, { message: 'Format file harus diisi' }), + base64: z.string().min(1, { message: 'Dokumen harus diisi' }), + }), + dokumenAktaPerubahan: z.object({ + name: z.string().optional(), + format: z.string().optional(), + base64: z.string().optional(), + }), + dokumenKtpDirut: z.object({ + name: z.string().optional(), + format: z.string().optional(), + base64: z.string().optional(), + }), + dokumenAktaPendirian: z.object({ + name: z.string().optional(), + format: z.string().optional(), + base64: z.string().optional(), + }), + dokumenLaporanKeuangan: z.object({ + name: z.string().optional(), + format: z.string().optional(), + base64: z.string().optional(), + }), + dokumenFotoKantor: z.object({ + name: z.string().min(1, { message: 'Nama file harus diisi' }), + format: z.string().min(1, { message: 'Format file harus diisi' }), + base64: z.string().min(1, { message: 'Dokumen harus diisi' }), + }), + dokumenTempatBekerja: z.object({ + name: z.string().min(1, { message: 'Nama file harus diisi' }), + format: z.string().min(1, { message: 'Format file harus diisi' }), + base64: z.string().min(1, { message: 'Dokumen harus diisi' }), + }), +}); diff --git a/src/core/components/elements/Footer/BasicFooter.jsx b/src/core/components/elements/Footer/BasicFooter.jsx index 4688b15b..b46d25b5 100644 --- a/src/core/components/elements/Footer/BasicFooter.jsx +++ b/src/core/components/elements/Footer/BasicFooter.jsx @@ -215,6 +215,11 @@ const CustomerGuide = () => ( Tracking Order </InternalItemLink> </li> + <li> + <InternalItemLink href='/pengajuan-tempo'> + Pengajuan Tempo + </InternalItemLink> + </li> </ul> </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/component/Dokumen.jsx b/src/lib/pengajuan-tempo/component/Dokumen.jsx new file mode 100644 index 00000000..0873df66 --- /dev/null +++ b/src/lib/pengajuan-tempo/component/Dokumen.jsx @@ -0,0 +1,952 @@ +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'; +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) { + toast.error('Maksimal ukuran file adalah 2MB', { duration: 4000 }); + + event.target.value = ''; + return; + } + + // 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 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'> + <div> + <label className='form-label text-nowrap'> + NIB (SIUP/TDP/SKDP) + </label> + <span className='text-xs opacity-60'> + Pastikan dokumen yang anda upload sudah benar + </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' + > + Upload Dokumen + </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' + /> + <span className='mt-2 text-gray-600'> + {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'> + NPWP Perusahaan + </label> + <span className='text-xs opacity-60'> + Pastikan dokumen yang anda upload sudah benar + </span> + </div> + <div className=''> + <div className='flex flex-col items-start'> + <label + htmlFor='dokumenNpwp' + className='cursor-pointer bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded' + > + Upload Dokumen + </label> + <input + // value={formDokumen.dokumenNpwp} + id='dokumenNpwp' + name='dokumenNpwp' + type='file' + ref={dokumenNpwpRef} + title=' ' + className='hidden' + aria-invalid={errorsDokumen.dokumenNpwp} + onChange={handleInputChange} + accept='.pdf,.png,.jpg,.jpeg' + /> + <span className='mt-2 text-gray-600'> + {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'> + <div> + <label className='form-label text-nowrap'>SPPKP</label> + <span className='text-xs opacity-60'> + Pastikan dokumen yang anda upload sudah benar + </span> + </div> + <div className=''> + <div className='flex flex-col items-start'> + <label + htmlFor='dokumenSppkp' + className='cursor-pointer bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded' + > + Upload Dokumen + </label> + <input + // value={formDokumen.dokumenSppkp} + id='dokumenSppkp' + name='dokumenSppkp' + type='file' + ref={dokumenSppkpRef} + className='hidden' + aria-invalid={errorsDokumen.dokumenSppkp} + onChange={handleInputChange} + accept='.pdf,.png,.jpg,.jpeg' + /> + <span className='mt-2 text-gray-600'> + {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'> + <div> + <label className='form-label text-nowrap'> + Akta Perubahan{' '} + <span className=' opacity-60'>(Opsional)</span> + </label> + <span className='text-xs opacity-60'> + Pastikan dokumen yang anda upload sudah benar + </span> + </div> + <div className=''> + <div className='flex flex-col items-start'> + <label + htmlFor='dokumenAktaPerubahan' + className='cursor-pointer bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded' + > + Upload Dokumen + </label> + <input + // value={formDokumen.dokumenAktaPerubahan} + id='dokumenAktaPerubahan' + name='dokumenAktaPerubahan' + type='file' + ref={dokumenAktaPerubahanRef} + className='hidden' + aria-invalid={errorsDokumen.dokumenAktaPerubahan} + onChange={handleInputChange} + accept='.pdf,.png,.jpg,.jpeg' + /> + <span className='mt-2 text-gray-600'> + {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'> + <div> + <label className='form-label text-nowrap'> + KTP Dirut/Direktur{' '} + <span className=' opacity-60'>(Opsional)</span> + </label> + <span className='text-xs opacity-60'> + Pastikan dokumen yang anda upload sudah benar + </span> + </div> + <div className=''> + <div className='flex flex-col items-start'> + <label + htmlFor='dokumenKtpDirut' + className='cursor-pointer bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded' + > + Upload Dokumen + </label> + <input + // value={formDokumen.dokumenKtpDirut} + id='dokumenKtpDirut' + name='dokumenKtpDirut' + type='file' + ref={dokumenKtpDirutRef} + className='hidden' + aria-invalid={errorsDokumen.dokumenKtpDirut} + onChange={handleInputChange} + accept='.pdf,.png,.jpg,.jpeg' + /> + <span className='mt-2 text-gray-600'> + {formDokumen?.dokumenKtpDirut?.name} + </span> + </div> + + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errorsDokumen.dokumenKtpDirut} + </div> + )} + </div> + </div> + </div> + <div className='w-px bg-gray-300'></div> + <div className='kolom kanan w-full grid grid-rows-2 gap-10 '> + <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> + </label> + <span className='text-xs opacity-60'> + Pastikan dokumen yang anda upload sudah benar + </span> + </div> + <div className=''> + <div className='flex flex-col items-start'> + <label + htmlFor='dokumenAktaPendirian' + className='cursor-pointer bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded' + > + Upload Dokumen + </label> + <input + // value={formDokumen.dokumenAktaPendirian} + id='dokumenAktaPendirian' + name='dokumenAktaPendirian' + type='file' + ref={dokumenAktaPendirianRef} + className='hidden' + aria-invalid={errorsDokumen.dokumenAktaPendirian} + onChange={handleInputChange} + accept='.pdf,.png,.jpg,.jpeg' + /> + <span className='mt-2 text-gray-600'> + {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'> + <div> + <label className='form-label text-nowrap'> + Laporan Keuangan + <span className=' opacity-60'>(Opsional)</span> + </label> + <span className='text-xs opacity-60'> + Pastikan dokumen yang anda upload sudah benar + </span> + </div> + <div className=''> + <div className='flex flex-col items-start'> + <label + htmlFor='dokumenLaporanKeuangan' + className='cursor-pointer bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded' + > + Upload Dokumen + </label> + <input + // value={formDokumen.dokumenLaporanKeuangan} + id='dokumenLaporanKeuangan' + name='dokumenLaporanKeuangan' + type='file' + ref={dokumenLaporanKeuanganRef} + className='hidden' + aria-invalid={errorsDokumen.dokumenLaporanKeuangan} + onChange={handleInputChange} + accept='.pdf,.png,.jpg,.jpeg' + /> + <span className='mt-2 text-gray-600'> + {formDokumen?.dokumenLaporanKeuangan?.name} + </span> + </div> + + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errorsDokumen.dokumenLaporanKeuangan} + </div> + )} + </div> + </div> + <div className='w-full grid grid-cols-2 gap-5'> + <div> + <label className='form-label text-nowrap'> + Foto Kantor (Tampak Depan) + </label> + <span className='text-xs opacity-60'> + Pastikan dokumen yang anda upload sudah benar + </span> + </div> + <div className=''> + <div className='flex flex-col items-start'> + <label + htmlFor='dokumenFotoKantor' + className='cursor-pointer bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded' + > + Upload Dokumen + </label> + <input + // value={formDokumen.dokumenFotoKantor} + id='dokumenFotoKantor' + name='dokumenFotoKantor' + type='file' + ref={dokumenFotoKantorRef} + className='hidden' + aria-invalid={errorsDokumen.dokumenFotoKantor} + onChange={handleInputChange} + accept='.pdf,.png,.jpg,.jpeg' + /> + <span className='mt-2 text-gray-600'> + {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'> + <div> + <label className='form-label text-nowrap'> + Tempat Bekerja + </label> + <span className='text-xs opacity-60'> + Pastikan dokumen yang anda upload sudah benar + </span> + </div> + <div className=''> + <div className='flex flex-col items-start'> + <label + htmlFor='dokumenTempatBekerja' + className='cursor-pointer bg-gray-200 hover:bg-gray-300 text-gray-700 py-2 px-4 rounded' + > + Upload Dokumen + </label> + <input + // value={formDokumen.dokumenTempatBekerja} + id='dokumenTempatBekerja' + name='dokumenTempatBekerja' + type='file' + ref={dokumenTempatBekerjaRef} + className='hidden' + aria-invalid={errorsDokumen.dokumenTempatBekerja} + onChange={handleInputChange} + accept='.pdf,.png,.jpg,.jpeg' + /> + <span className='mt-2 text-gray-600'> + {formDokumen?.dokumenTempatBekerja?.name} + </span> + </div> + + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errorsDokumen.dokumenTempatBekerja} + </div> + )} + </div> + </div> + <div></div> + </div> + </div> + </form> + <div className='w-1/2 mt-8 flex gap-3 flex-col'> + <div className='flex justify-between'> + <p className='font-bold'>Upload Progress</p> + <p> + <span className='text-red-500 font-bold'> + {parseInt((getJumlahDokumenDiisi() / 9) * 100)} % + </span> + <span className='ml-2 opacity-60'> + {getJumlahDokumenDiisi() >= 4 + ? getJumlahDokumenDiisi() == 9 + ? '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={9} + height='14 px' + /> + <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'> + <label className='form-label text-nowrap'> + NIB (SIUP/TDP/SKDP) + </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 + ? 'Sudah Upload' + : 'Upload Dokumen'} + </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' + /> + <span className='mt-2 text-gray-600 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'> + 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 + ? 'Sudah Upload' + : 'Upload Dokumen'} + </label> + <input + // value={formDokumen.dokumenNpwp} + id='dokumenNpwp' + name='dokumenNpwp' + type='file' + ref={dokumenNpwpRef} + title=' ' + className='hidden' + aria-invalid={errorsDokumen.dokumenNpwp} + onChange={handleInputChange} + accept='.pdf,.png,.jpg,.jpeg' + /> + <span className='mt-2 text-gray-600 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'> + <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 + ? 'Sudah Upload' + : 'Upload Dokumen'} + </label> + <input + // value={formDokumen.dokumenNpwp} + id='dokumenSppkp' + name='dokumenSppkp' + type='file' + ref={dokumenSppkpRef} + title=' ' + className='hidden' + aria-invalid={errorsDokumen.dokumenSppkp} + onChange={handleInputChange} + accept='.pdf,.png,.jpg,.jpeg' + /> + <span className='mt-2 text-gray-600 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'> + <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 + ? 'Sudah Upload' + : 'Upload Dokumen'} + </label> + <input + // value={formDokumen.dokumenNpwp} + id='dokumenAktaPerubahan' + name='dokumenAktaPerubahan' + type='file' + ref={dokumenAktaPerubahanRef} + title=' ' + className='hidden' + aria-invalid={errorsDokumen.dokumenAktaPerubahan} + onChange={handleInputChange} + accept='.pdf,.png,.jpg,.jpeg' + /> + <span className='mt-2 text-gray-600 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'> + <label className='form-label text-nowrap'> + KTP Dirut/Direktur{' '} + <span className=' opacity-60'>(Opsional)</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 + ? 'Sudah Upload' + : 'Upload Dokumen'} + </label> + <input + // value={formDokumen.dokumenNpwp} + id='dokumenKtpDirut' + name='dokumenKtpDirut' + type='file' + ref={dokumenKtpDirutRef} + title=' ' + className='hidden' + aria-invalid={errorsDokumen.dokumenKtpDirut} + onChange={handleInputChange} + accept='.pdf,.png,.jpg,.jpeg' + /> + <span className='mt-2 text-gray-600 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'> + <label className='form-label text-nowrap'> + Akta Pendirian <span className=' opacity-60'>(Opsional)</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 + ? 'Sudah Upload' + : 'Upload Dokumen'} + </label> + <input + // value={formDokumen.dokumenNpwp} + id='dokumenAktaPendirian' + name='dokumenAktaPendirian' + type='file' + ref={dokumenAktaPendirianRef} + title=' ' + className='hidden' + aria-invalid={errorsDokumen.dokumenAktaPendirian} + onChange={handleInputChange} + accept='.pdf,.png,.jpg,.jpeg' + /> + <span className='mt-2 text-gray-600 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'> + <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 + ? 'Sudah Upload' + : 'Upload Dokumen'} + </label> + <input + // value={formDokumen.dokumenNpwp} + id='dokumenLaporanKeuangan' + name='dokumenLaporanKeuangan' + type='file' + ref={dokumenLaporanKeuanganRef} + 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 className='w-full flex flex-col gap-2'> + <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 + ? 'Sudah Upload' + : 'Upload Dokumen'} + </label> + <input + // value={formDokumen.dokumenNpwp} + id='dokumenFotoKantor' + name='dokumenFotoKantor' + type='file' + ref={dokumenFotoKantorRef} + title=' ' + className='hidden' + aria-invalid={errorsDokumen.dokumenFotoKantor} + onChange={handleInputChange} + accept='.pdf,.png,.jpg,.jpeg' + /> + <span className='mt-2 text-gray-600 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'> + <label className='form-label text-nowrap'> + Tempat Bekerja + </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 + ? 'Sudah Upload' + : 'Upload Dokumen'} + </label> + <input + // value={formDokumen.dokumenNpwp} + id='dokumenTempatBekerja' + name='dokumenTempatBekerja' + type='file' + ref={dokumenTempatBekerjaRef} + title=' ' + className='hidden' + aria-invalid={errorsDokumen.dokumenTempatBekerja} + onChange={handleInputChange} + accept='.pdf,.png,.jpg,.jpeg' + /> + <span className='mt-2 text-gray-600 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> + </form> + <div className='w-full mt-8 flex gap-3 flex-col'> + <div className='flex justify-between'> + <p className='font-bold'>Upload Progress</p> + <p> + <span className='text-red-500 font-bold'> + {parseInt((getJumlahDokumenDiisi() / 9) * 100)} % + </span> + <span className='ml-2 opacity-60'> + {getJumlahDokumenDiisi() >= 4 + ? getJumlahDokumenDiisi() == 9 + ? '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={9} + 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..1670314d --- /dev/null +++ b/src/lib/pengajuan-tempo/component/FinishTempo.jsx @@ -0,0 +1,78 @@ +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]); + + // Kirim email ketika komponen ini dimount atau sesuai kondisi + const sendEmail = async () => { + try { + const send = await axios.post( + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/finish-checkout?orderName=${query?.order_id}`, + {} + ); + if (send.status === 200) { + toast.success('Berhasil mengirim rincian pesanan'); + } else { + toast.error('Gagal mengirimkan rincian pesanan'); + } + } catch (error) { + console.error(error); + toast.error('Gagal mengirimkan rincian pesanan'); + } + }; + + return ( + <div className='container flex flex-col items-center gap-4'> + <div className='flex w-2/3 justify-center items-center'> + <h1 className='text-red-500 text-center font-semibold text-3xl'> + Form Pengajuan Tempo kamu Telah Berhasil Didaftarkan Mohon menunggu + hingga Proses Selesai + </h1> + </div> + <Image + src='/images/REGISTRASI-TEMPO.svg' + alt='Checkout Pesanan' + width={isMobile ? 300 : 450} + height={isMobile ? 300 : 450} + /> + + <div className='mt-2 text-center leading-6 text-base p-4 md:p-0 w-4/5'> + 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 + </div> + <Link + href={`/my/quotations/${data?.id}`} + className='btn-solid-red rounded-md text-base flex flex-row' + > + 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..80845a8f --- /dev/null +++ b/src/lib/pengajuan-tempo/component/Konfirmasi.jsx @@ -0,0 +1,263 @@ +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 [isOpen, setIsOpen] = 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]); + const handleLabelClick = () => { + setIndustriesOpen(!industriesOpen); + }; + 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'> + <p className='font-semibold text-lg'>Informasi Perusahaan</p> + <div className='p-2 bg-gray-200'> + {isOpen ? ( + <ChevronUpIcon className='w-4' /> + ) : ( + <ChevronDownIcon className='w-4' /> + )} + </div> + </div> + <InformasiPerusahaan isKonfirmasi={true} /> + </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'> + {isOpen ? ( + <ChevronUpIcon className='w-4' /> + ) : ( + <ChevronDownIcon className='w-4' /> + )} + </div> + </div> + <KontakPerusahaan isKonfirmasi={true} /> + </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'> + {isOpen ? ( + <ChevronUpIcon className='w-4' /> + ) : ( + <ChevronDownIcon className='w-4' /> + )} + </div> + </div> + <Pengiriman isKonfirmasi={true} /> + </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'> + {isOpen ? ( + <ChevronUpIcon className='w-4' /> + ) : ( + <ChevronDownIcon className='w-4' /> + )} + </div> + </div> + <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..9b0f1d8f --- /dev/null +++ b/src/lib/pengajuan-tempo/component/KonfirmasiDokumen.jsx @@ -0,0 +1,1202 @@ +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'; +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 [popUpSource, setSource] = useState(); + const { + formDokumen, + errorsDokumen, + validateDokumen, + updateFormDokumen, + getJumlahDokumenDiisi, + } = usePengajuanTempoStoreDokumen(); + + const handleConfirmSubmit = (format, base64) => { + if (format == 'pdf') { + setFormat(`application/${format}`); + } else { + setFormat(`image/${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]; + 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]); + + 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'> + <label className='form-label text-nowrap'> + NIB (SIUP/TDP/SKDP) + </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'> + {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' + > + Ubah + </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'> + 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'> + {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' + > + Ubah + </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'> + {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' + > + Ubah + </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'> + {' '} + 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'> + {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' + > + Ubah + </label> + <input + // value={formDokumen?.dokumenNib?.name} + id='dokumenAktaPerubahan' + name='dokumendokumenAktaPerubahanSppkp' + 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'> + 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'> + {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' + > + Ubah + </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'> + 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'> + {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' + > + Ubah + </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'> + 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'> + {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' + > + Ubah + </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-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'> + {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' + > + Ubah + </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'> + Tempat Bekerja + </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'> + {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' + > + Ubah + </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> + )} + {isMobile && ( + <div> + <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'> + NIB (SIUP/TDP/SKDP) + </label> + </div> + <div className='w-3/5'> + <div className='flex flex-row items-center justify-start'> + <span className='w-3/5 text-gray-600 truncate'> + {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' + > + Ubah + </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'>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'> + {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' + > + Ubah + </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-3/5 text-gray-600 truncate'> + {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' + > + Ubah + </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'> + {' '} + 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'> + {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' + > + Ubah + </label> + <input + // value={formDokumen?.dokumenNib?.name} + id='dokumenAktaPerubahan' + name='dokumendokumenAktaPerubahanSppkp' + 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-3/5 text-gray-600 truncate'> + {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' + > + Ubah + </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-3/5 text-gray-600 truncate'> + {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' + > + Ubah + </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-3/5 text-gray-600 truncate'> + {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' + > + Ubah + </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-3/5 text-gray-600 truncate'> + {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' + > + Ubah + </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'>Tempat Bekerja</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'> + {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' + > + Ubah + </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='!container' + title='Dokumen' + active={isExample} + close={() => setIsExample(false)} + > + <div className='flex justify-center items-center p-2'> + {/* <embed + src={`data:${format};base64,${base64}`} + type={format} + width='100%' + height='600px' + /> */} + <iframe + src={`data:${format};base64,${base64}`} + width='100%' + height='100%' + title='Document' + ></iframe> + </div> + </BottomPopup> + )} + {isMobile && ( + <BottomPopup + className='!container' + title='Dokumen' + active={isExample} + close={() => setIsExample(false)} + > + <div className='flex justify-center items-center p-2'> + {/* <embed + src={`data:${format};base64,${base64}`} + type={format} + width='100%' + height='100%' + /> */} + <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> + </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} +/> */ +} diff --git a/src/lib/pengajuan-tempo/component/KontakPerusahaan.jsx b/src/lib/pengajuan-tempo/component/KontakPerusahaan.jsx new file mode 100644 index 00000000..e7bfdbbe --- /dev/null +++ b/src/lib/pengajuan-tempo/component/KontakPerusahaan.jsx @@ -0,0 +1,586 @@ +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'> + <input + value={formKontakPerson.direkturName} + id='direkturName' + name='direkturName' + placeholder='Masukkan nama direktur anda' + type='text' + className='form-input' + aria-invalid={errorsKontakPerson.direkturName} + ref={direkturNameRef} + onChange={handleInputChange} + /> + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errorsKontakPerson.direkturName} + </div> + )} + </div> + </div> + + <div className='flex flex-row justify-between items-start'> + <div className='w-2/5'> + <label className='form-label text-nowrap'> + No. Telpon Direktur + </label> + {!isKonfirmasi && ( + <span className='text-xs opacity-60'> + isi nomor telpon direktur di perusahaan kamu + </span> + )} + </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> + {!isKonfirmasi && ( + <span className='text-xs opacity-60'> + isi email Direktur yang sesuai + </span> + )} + </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'> + <input + id='purchasingName' + name='purchasingName' + ref={purchasingNameRef} + placeholder='Masukkan nama purchasing anda' + value={formKontakPerson.purchasingName} + type='text' + className='form-input' + aria-invalid={errorsKontakPerson.purchasingName} + onChange={handleInputChange} + /> + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errorsKontakPerson.purchasingName} + </div> + )} + </div> + </div> + + <div className='flex flex-row justify-between items-start'> + <div className='w-2/5'> + <label className='form-label text-nowrap'> + No. Telpon Purchasing + </label> + {!isKonfirmasi && ( + <span className='text-xs opacity-60'> + isi nomor purchasing yang bertanggung jawab di perusahaan + 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 dengan benar + </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'> + <input + id='financeName' + name='financeName' + ref={financeNameRef} + placeholder='Masukkan nama finance' + value={formKontakPerson.financeName} + type='text' + className='form-input' + aria-invalid={errorsKontakPerson.financeName} + onChange={handleInputChange} + /> + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errorsKontakPerson.financeName} + </div> + )} + </div> + </div> + <div className='flex flex-row justify-between items-start'> + <div className='w-2/5'> + <label className='form-label text-nowrap'> + No. Telpon 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 dengan benar + </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> + <input + value={formKontakPerson.direkturName} + id='direkturName' + name='direkturName' + placeholder='Masukkan nama direktur anda' + type='text' + className='form-input' + aria-invalid={errorsKontakPerson.direkturName} + ref={direkturNameRef} + onChange={handleInputChange} + /> + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errorsKontakPerson.direkturName} + </div> + )} + </div> + + <div className='flex flex-col gap-2 justify-between items-start'> + <label className='form-label text-nowrap'> + No. Telpon Direktur + </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> + <input + id='purchasingName' + name='purchasingName' + ref={purchasingNameRef} + placeholder='Masukkan nama purchasing anda' + value={formKontakPerson.purchasingName} + type='text' + className='form-input' + aria-invalid={errorsKontakPerson.purchasingName} + onChange={handleInputChange} + /> + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errorsKontakPerson.purchasingName} + </div> + )} + </div> + + <div className='flex flex-col gap-2 justify-between items-start'> + <label className='form-label text-nowrap'> + No. Telpon 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> + <input + id='financeName' + name='financeName' + ref={financeNameRef} + placeholder='Masukkan nama finance' + value={formKontakPerson.financeName} + type='text' + className='form-input' + aria-invalid={errorsKontakPerson.financeName} + onChange={handleInputChange} + /> + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errorsKontakPerson.financeName} + </div> + )} + </div> + <div className='flex flex-col gap-2 justify-between items-start'> + <label className='form-label text-nowrap'> + No. Telpon 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..d0e1fcc6 --- /dev/null +++ b/src/lib/pengajuan-tempo/component/PengajuanTempo.jsx @@ -0,0 +1,352 @@ +import React from 'react'; +import { useMemo, useState, useEffect, useRef } from 'react'; +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 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'; +const PengajuanTempo = () => { + const { isDesktop, isMobile } = useDevice(); + const [currentStep, setCurrentStep] = React.useState(0); + const NUMBER_OF_STEPS = 6; + const [isLoading, setIsLoading] = useState(false); + const { form, errors, validate, updateForm } = usePengajuanTempoStore(); + const { control, watch, setValue } = useForm(); + const auth = useAuth(); + const router = useRouter(); + 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} + />, + <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(false); + + window.scrollTo({ + top: 0, + behavior: 'smooth', + }); + }, [currentStep, buttonSubmitClick]); + + useEffect(() => { + const cachedData = getFromLocalStorage(stepLabels[currentStep]); + if (cachedData) { + if (currentStep == 3) { + stepDivsUpdateForm[currentStep](cachedData); + } else if (currentStep == 4) { + Object.keys(cachedData).forEach((key) => { + const { name, format, base64 } = cachedData[key]; + stepDivsUpdateForm[currentStep](key, name, format, base64); + }); + } else { + Object.keys(cachedData).forEach((key) => { + stepDivsUpdateForm[currentStep](key, cachedData[key]); + }); + } + } + validate(); + validateKontakPerson(); + validatePengiriman(); + validateDokumen(); + updateHasSave(false); + }, [currentStep]); + const goToNextStep = () => { + if (!isFormValid) { + setNotValid(true); + setButtonSubmitClick(!buttonSubmitClick); + return; + } else { + saveToLocalStorage(stepLabels[currentStep], stepDivsForm[currentStep]); + setButtonSubmitClick(!buttonSubmitClick); + setNotValid(false); + } + setCurrentStep((prev) => (prev === NUMBER_OF_STEPS - 1 ? prev : prev + 1)); + }; + + const handleDaftarTempo = 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, + })); + + const formattedDokumen = Object.entries(formDokumen).map(([key, doc]) => ({ + documentName: key, + details: { + name: doc.name, + format: doc.format, + base64: doc.base64, + }, + })); + + const data2 = { + user_id: auth.id, + ...form, + ...formKontakPerson, + ...formPengiriman, + formDocs: JSON.stringify(formattedDokumen), + formSupplier: JSON.stringify(productOrder), + }; + const toastId = toast.loading('Mengirimkan formulir pengajuan tempo...'); + setIsLoading(true); + try { + let address5; + const address = await createPengajuanTempoApi({ + id: '0', + user_id: auth.id, + ...form, + }); + if (address.id) { + const address2 = await createPengajuanTempoApi({ + id: address.id, + user_id: address.userId, + ...formKontakPerson, + }); + if (address2.id) { + const address3 = await createPengajuanTempoApi({ + id: address2.id, + user_id: address2.userId, + ...formPengiriman, + }); + if (address3.id) { + const address4 = await createPengajuanTempoApi({ + id: address3.id, + user_id: address3.userId, + formDocs: JSON.stringify(formattedDokumen), + }); + if (address4.id) { + address5 = await createPengajuanTempoApi({ + id: address4.id, + user_id: address4.userId, + tempo_request: true, + formSupplier: JSON.stringify(productOrder), + }); + } + } + } + } + toast.dismiss(toastId); + setIsLoading(false); + + if (address5.id) { + toast.success('Pengajuan tempo berhasil dilakukan'); + // removeFromLocalStorage(); + router.push('/pengajuan-tempo/finish?tempo_id=SO-2023-06480'); + } + } catch (error) { + toast.dismiss(toastId); + setIsLoading(false); + + toast.error('Terjadi kesalahan dalam pengiriman formulir'); + console.error(error); + } + }; + + const goToPreviousStep = () => { + setCurrentStep((prev) => (prev <= 0 ? prev : prev - 1)); + }; + + const saveToLocalStorage = (key, form) => { + localStorage.setItem(key, JSON.stringify(form)); + }; + + const getFromLocalStorage = (key) => { + const itemStr = localStorage.getItem(key); + if (!itemStr) return null; + + const item = JSON.parse(itemStr); + return item; + }; + const removeFromLocalStorage = () => { + for (const key of stepLabels) { + localStorage.removeItem(key); + } + }; + + return ( + <> + <div className='container flex flex-col items-center '> + <h1 className='text-h-sm md:text-title-sm font-semibold text-center mb-6'> + Form Pengajuan Tempo + </h1> + <p className='text-center mb-4'> + Lorem ipsum dolor sit amet consectetur. Commodo suspendisse at enim + magnis ut quisque rhoncus. Felis volutpat fringilla sollicitudin + ultricies. Enim non eget in lorem netus. Nisl pharetra accumsan diam + suspendisse. + </p> + </div> + <div className='h-[2px] w-full mb-20 bg-gray_r-3' /> + + <div className='container 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-row justify-end items-center' + } gap-4 mb-8`} + > + <span className='text-xs opacity-60'> + *Pastikan data yang anda masukan sudah benar dan sesuai + </span> + {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} + > + Langkah Selanjutnya {<ChevronRightIcon className='w-5' />} + </Button> + </Tooltip> + )} + {currentStep == 5 && ( + <Button + colorScheme='red' + w={`${isMobile ? 'full' : 'fit'}`} + onClick={handleDaftarTempo} + > + Daftar Tempo {<ChevronRightIcon className='w-5' />} + </Button> + )} + </div> + </div> + </> + ); +}; + +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..842e43ef --- /dev/null +++ b/src/lib/pengajuan-tempo/component/Pengiriman.jsx @@ -0,0 +1,1489 @@ +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 { usePengajuanTempoStorePengiriman } from '../../../../src-migrate/modules/register/stores/usePengajuanTempoStore'; +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 [states, setState] = useState([]); + const [cities, setCities] = useState([]); + const [sameAddress, setSameAddress] = useState(false); + const [everyWeekday, setEveryWeekday] = useState(false); + const [everyWeek, setEveryWeek] = useState(false); + const [tukarInvoice, setTukarInvoice] = useState(false); + const [everyWeekdayPembayaran, setEveryWeekdayPembayaran] = useState(false); + const [everyWeekPembayaran, setEveryWeekPembayaran] = useState(false); + const [tukarInvoicePembayaran, setTukarInvoicePembayaran] = 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(); + dataState = dataState.map((state) => ({ + value: state.id, + label: state.name, + })); + setState(dataState); + }; + loadState(); + }, []); + + const watchState = watch('statePengiriman'); + useEffect(() => { + updateFormPengiriman('cityPengiriman', ''); + 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(); + } + }, [watchCity]); + const handleInputChange = (event) => { + const { name, value } = event.target; + updateFormPengiriman(name, value); + validatePengiriman(); + }; + + const handleEveryWeekday = () => { + setEveryWeekday(!everyWeekday); + }; + const handleEveryWeek = () => { + setEveryWeek(!everyWeek); + }; + const handleTukarInvoice = () => { + setTukarInvoice(!tukarInvoice); + }; + const handleEveryWeekdayPembayaran = () => { + setEveryWeekdayPembayaran(!everyWeekdayPembayaran); + }; + const handleEveryWeekPembayaran = () => { + setEveryWeekPembayaran(!everyWeekPembayaran); + }; + const handleTukarInvoicePembayaran = () => { + setTukarInvoicePembayaran(!tukarInvoicePembayaran); + }; + + useEffect(() => { + updateFormPengiriman('everyWeekday', everyWeekday); + updateFormPengiriman('everyWeek', everyWeek); + updateFormPengiriman('tukarInvoice', tukarInvoice); + updateFormPengiriman('everyWeekdayPembayaran', everyWeekdayPembayaran); + updateFormPengiriman('everyWeekPembayaran', everyWeekPembayaran); + updateFormPengiriman('tukarInvoicePembayaran', tukarInvoicePembayaran); + validatePengiriman(); + }, [ + everyWeekday, + everyWeek, + tukarInvoice, + everyWeekdayPembayaran, + everyWeekPembayaran, + tukarInvoicePembayaran, + ]); + + 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 zipRef = useRef(null); + const invoicePicRef = useRef(null); + const streetInvoiceRef = useRef(null); + const stateInvoiceRef = useRef(null); + const cityInvoiceRef = 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.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.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(() => { + validatePengiriman(); + if (formPengiriman.isSameAddrees) { + const isSame = formPengiriman.isSameAddrees; + if (isSame == 'true') { + setSameAddress(true); + } else { + setSameAddress(false); + } + } + }, [buttonSubmitClick]); + useEffect(() => { + if (formPengiriman.isSameAddrees) { + const isSame = formPengiriman.isSameAddrees; + if (isSame == 'true') { + setSameAddress(true); + } else { + setSameAddress(false); + } + } + }, [formPengiriman.isSameAddrees]); + useEffect(() => { + if (sameAddress) { + updateFormPengiriman('streetInvoice', formPengiriman.streetPengiriman); + updateFormPengiriman('stateInvoice', formPengiriman.statePengiriman); + updateFormPengiriman('cityInvoice', formPengiriman.cityPengiriman); + } else { + updateFormPengiriman('streetInvoice', ''); + updateFormPengiriman('stateInvoice', ''); + updateFormPengiriman('cityInvoice', ''); + setValue('streetInvoice', ''); + setValue('stateInvoice', ''); + setValue('cityInvoice', ''); + } + updateFormPengiriman('isSameAddrees', `${sameAddress}`); + validatePengiriman(); + }, [sameAddress]); + + 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); + }; + 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'> + <input + value={formPengiriman.PICName} + id='PICName' + name='PICName' + placeholder='Masukkan nama pic pengiriman disini' + type='text' + className='form-input' + aria-invalid={errorsPengiriman.PICName} + ref={PICNameRef} + onChange={handleInputChange} + /> + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errorsPengiriman.PICName} + </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'> + <div> + <input + id='streetPengiriman' + name='streetPengiriman' + ref={streetPengirimanRef} + 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-3'> + <div className='w-2/5' ref={statePengirimanRef}> + <Controller + name='statePengiriman' + control={control} + render={(props) => ( + <HookFormSelect + {...props} + options={states} + placeholder='Provinsi' + /> + )} + /> + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errorsPengiriman.statePengiriman} + </div> + )} + </div> + <div className='w-1/3' ref={cityPengirimanRef}> + <Controller + name='cityPengiriman' + control={control} + render={(props) => ( + <HookFormSelect + {...props} + options={cities} + disabled={!watchState} + 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' + name='zipPengiriman' + ref={zipRef} + placeholder='Kode Pos' + 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> + </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'> + <input + value={formPengiriman.invoicePic} + id='invoicePic' + name='invoicePic' + placeholder='Masukkan nama pic invoice disini' + type='text' + className='form-input' + aria-invalid={errorsPengiriman.invoicePic} + ref={invoicePicRef} + onChange={handleInputChange} + /> + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errorsPengiriman.invoicePic} + </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 pengiriman + </Checkbox> + </div> + {!sameAddress && ( + <> + <div> + <input + id='streetInvoice' + name='streetInvoice' + ref={streetInvoiceRef} + 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}> + <Controller + name='stateInvoice' + control={control} + render={(props) => ( + <HookFormSelect + {...props} + options={states} + placeholder='Provinsi' + /> + )} + /> + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errorsPengiriman.stateInvoice} + </div> + )} + </div> + <div className='w-2/5' ref={cityInvoiceRef}> + <Controller + name='cityInvoice' + control={control} + render={(props) => ( + <HookFormSelect + {...props} + options={cities} + disabled={!watchState} + placeholder='Kota' + /> + )} + /> + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errorsPengiriman.cityInvoice} + </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'> + Pilih jika bisnis anda memiliki jadwal penukaran invoice + </span> + )} + </div> + <div className='w-3/5 flex gap-3 flex-col'> + <div className='flex gap-3 flex-col'> + <Checkbox + colorScheme='red' + isChecked={everyWeekday} + onChange={handleEveryWeekday} + > + Setiap Minggu dihari + </Checkbox> + <input + id='everyWeekdayInput' + name='everyWeekdayInput' + ref={everyWeekdayInputRef} + placeholder='Format: Senin, Selasa, Rabu, Kamis, Jumat, Sabtu, Minggu' + type='text' + value={formPengiriman.everyWeekdayInput} + className='form-input' + onChange={handleInputChange} + /> + </div> + <div className='flex gap-3 flex-col'> + <Checkbox + colorScheme='red' + isChecked={everyWeek} + onChange={handleEveryWeek} + > + Setiap Bulan di minggu ke + </Checkbox> + <input + id='everyWeekInput' + name='everyWeekInput' + ref={everyWeekInputRef} + placeholder='Format: Minggu 1 & Minggu 2' + type='text' + value={formPengiriman.everyWeekInput} + className='form-input' + onChange={handleInputChange} + /> + </div> + <div className='flex gap-3 flex-col'> + <Checkbox + colorScheme='red' + isChecked={tukarInvoice} + onChange={handleTukarInvoice} + > + Lainnya + </Checkbox> + <textarea + id='tukarInvoiceInput' + name='tukarInvoiceInput' + placeholder='isi manual dokumen yang anda mau' + type='textarea' + value={formPengiriman.tukarInvoiceInput} + className='form-input' + rows={4} + cols={40} + onChange={handleInputChange} + /> + </div> + </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'> + Pilih jika bisnis anda memiliki jadwal pembayaran + </span> + )} + </div> + <div className='w-3/5 flex gap-3 flex-col'> + <div className='flex gap-3 flex-col'> + <Checkbox + colorScheme='red' + isChecked={everyWeekdayPembayaran} + onChange={handleEveryWeekdayPembayaran} + > + Setiap Minggu dihari + </Checkbox> + <input + id='everyWeekdayInputPembayaran' + name='everyWeekdayInputPembayaran' + placeholder='Format: Senin, Selasa, Rabu, Kamis, Jumat, Sabtu, Minggu' + type='text' + value={formPengiriman.everyWeekdayInputPembayaran} + className='form-input' + onChange={handleInputChange} + /> + </div> + <div className='flex gap-3 flex-col'> + <Checkbox + colorScheme='red' + isChecked={everyWeekPembayaran} + onChange={handleEveryWeekPembayaran} + > + Setiap Bulan di minggu ke + </Checkbox> + <input + id='everyWeekInputPembayaran' + name='everyWeekInputPembayaran' + placeholder='Format: Minggu 1 & Minggu 2' + type='text' + value={formPengiriman.everyWeekInputPembayaran} + className='form-input' + onChange={handleInputChange} + /> + </div> + <div className='flex gap-3 flex-col'> + <Checkbox + colorScheme='red' + isChecked={tukarInvoicePembayaran} + onChange={handleTukarInvoicePembayaran} + > + Lainnya + </Checkbox> + <textarea + id='tukarInvoiceInputPembayaran' + name='tukarInvoiceInputPembayaran' + placeholder='isi manual dokumen yang anda mau' + type='textarea' + value={formPengiriman.tukarInvoiceInputPembayaran} + className='form-input' + rows={4} + cols={40} + onChange={handleInputChange} + /> + </div> + </div> + </div> + + <div className='flex flex-row justify-between items-start'> + <div className='w-2/5'> + <label className='form-label text-wrap'> + Dokumen saat Pengiriman Barang + </label> + {!isKonfirmasi && ( + <span className='text-xs opacity-60'> + Pilih dokumen lampiran saat pengiriman barang + </span> + )} + </div> + <div + className='w-3/5 flex gap-3 flex-col' + ref={dokumenPengirimanRef} + > + <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)} + onChange={() => handleCheckboxChange(4)} + > + Lainnya + </Checkbox> + <textarea + id='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} + /> + </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 dilampirkan saat Pengiriman Invoice + </label> + <span className='text-xs opacity-60'> + Dokumen akan dikirimkan sesuai dengan yang anda pilih + </span> + </div> + <div + className='w-3/5 flex gap-3 flex-col' + ref={dokumenPengirimanInvoiceRef} + > + <Checkbox + colorScheme='red' + key='0' + isChecked={isCheckedInvoice(0)} + onChange={() => + handleCheckboxChangeDokumenPengirimanInvoice(0) + } + > + Invoice Pembelian + </Checkbox> + <Checkbox + colorScheme='red' + key='1' + isChecked={isCheckedInvoice(1)} + onChange={() => + handleCheckboxChangeDokumenPengirimanInvoice(1) + } + > + Surat Jalan + </Checkbox> + <Checkbox + colorScheme='red' + key='2' + isChecked={isCheckedInvoice(2)} + onChange={() => + handleCheckboxChangeDokumenPengirimanInvoice(2) + } + > + Berita Acara Serah Terima (BAST) + </Checkbox> + <Checkbox + colorScheme='red' + key='3' + isChecked={isCheckedInvoice(3)} + onChange={() => + handleCheckboxChangeDokumenPengirimanInvoice(3) + } + > + Faktur Pajak + </Checkbox> + <Checkbox + colorScheme='red' + key='4' + isChecked={isCheckedInvoice(4)} + onChange={() => + handleCheckboxChangeDokumenPengirimanInvoice(4) + } + > + Good Receipt (GR) + </Checkbox> + + <div className='flex gap-3 flex-col'> + <Checkbox + colorScheme='red' + key='5' + isChecked={isCheckedInvoice(5)} + onChange={() => + handleCheckboxChangeDokumenPengirimanInvoice(5) + } + > + Lainnya + </Checkbox> + <textarea + id='dokumenPengirimanInvoiceInput' + name='dokumenPengirimanInvoiceInput' + placeholder='isi manual dokumen yang anda mau' + type='textarea' + ref={dokumenPengirimanInvoiceInputRef} + value={formPengiriman.dokumenPengirimanInvoiceInput} + className='form-input' + rows={4} + cols={40} + onChange={handleInputChange} + /> + </div> + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errorsPengiriman.dokumenInvoicePengiriman} + </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> + <input + value={formPengiriman.PICName} + id='PICName' + name='PICName' + placeholder='Masukkan nama pic pengiriman disini' + type='text' + className='form-input' + aria-invalid={errorsPengiriman.PICName} + ref={PICNameRef} + onChange={handleInputChange} + /> + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errorsPengiriman.PICName} + </div> + )} + </div> + + <div className='flex flex-col gap-2 justify-between items-start'> + <label className='form-label text-nowrap'> + Alamat Pengiriman Barang + </label> + <div className='w-full'> + <input + id='streetPengiriman' + name='streetPengiriman' + ref={streetPengirimanRef} + 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}> + <Controller + name='statePengiriman' + control={control} + render={(props) => ( + <HookFormSelect + {...props} + options={states} + placeholder='Provinsi' + /> + )} + /> + </div> + <div className='w-1/3' ref={cityPengirimanRef}> + <Controller + name='cityPengiriman' + control={control} + render={(props) => ( + <HookFormSelect + {...props} + options={cities} + disabled={!watchState} + placeholder='Kota' + /> + )} + /> + </div> + <div className='w-1/3'> + <input + id='zipPengiriman' + name='zipPengiriman' + ref={zipRef} + placeholder='Kode Pos' + type='number' + value={formPengiriman.zipPengiriman} + className='form-input' + onChange={handleInputChange} + /> + </div> + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errorsPengiriman.statePengiriman} + </div> + )} + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errorsPengiriman.cityPengiriman} + </div> + )} + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errorsPengiriman.zipPengiriman} + </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> + <input + value={formPengiriman.invoicePic} + id='invoicePic' + name='invoicePic' + placeholder='Masukkan nama pic invoice disini' + type='text' + className='form-input' + aria-invalid={errorsPengiriman.invoicePic} + ref={invoicePicRef} + onChange={handleInputChange} + /> + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errorsPengiriman.invoicePic} + </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> + {!sameAddress && ( + <> + <div className='w-full'> + <input + id='streetInvoice' + name='streetInvoice' + ref={streetInvoiceRef} + 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}> + <Controller + name='stateInvoice' + control={control} + render={(props) => ( + <HookFormSelect + {...props} + options={states} + placeholder='Provinsi' + /> + )} + /> + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errorsPengiriman.stateInvoice} + </div> + )} + </div> + <div className='w-2/5' ref={cityInvoiceRef}> + <Controller + name='cityInvoice' + control={control} + render={(props) => ( + <HookFormSelect + {...props} + options={cities} + disabled={!watchState} + placeholder='Kota' + /> + )} + /> + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errorsPengiriman.cityInvoice} + </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'> + Pilih jika bisnis anda memiliki jadwal penukaran invoice + </span> + )} + <div className='w-full flex gap-2 flex-col'> + <div className='flex gap-2 flex-col'> + <Checkbox + size='sm' + colorScheme='red' + isChecked={everyWeekday} + onChange={handleEveryWeekday} + > + Setiap Minggu dihari + </Checkbox> + <input + id='everyWeekdayInput' + name='everyWeekdayInput' + ref={everyWeekdayInputRef} + placeholder='Format: Senin, Selasa, Rabu, Kamis, Jumat, Sabtu, Minggu' + type='text' + value={formPengiriman.everyWeekdayInput} + className='form-input' + onChange={handleInputChange} + /> + </div> + <div className='flex gap-2 w-full flex-col'> + <Checkbox + size='sm' + colorScheme='red' + isChecked={everyWeek} + onChange={handleEveryWeek} + > + Setiap Bulan di minggu ke + </Checkbox> + <input + id='everyWeekInput' + name='everyWeekInput' + ref={everyWeekInputRef} + placeholder='Format: Minggu 1 & Minggu 2' + type='text' + value={formPengiriman.everyWeekInput} + className='form-input' + onChange={handleInputChange} + /> + </div> + <div className='flex gap-2 flex-col'> + <Checkbox + size='sm' + colorScheme='red' + isChecked={tukarInvoice} + onChange={handleTukarInvoice} + > + Lainnya + </Checkbox> + <textarea + id='tukarInvoiceInput' + name='tukarInvoiceInput' + placeholder='isi manual dokumen yang anda mau' + type='textarea' + value={formPengiriman.tukarInvoiceInput} + className='form-input' + rows={4} + cols={40} + onChange={handleInputChange} + /> + </div> + </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'> + Pilih jika bisnis anda memiliki jadwal pembayaran + </span> + )} + <div className='w-full flex gap-2 flex-col'> + <div className='flex gap-2 flex-col'> + <Checkbox + size='sm' + colorScheme='red' + isChecked={everyWeekdayPembayaran} + onChange={handleEveryWeekdayPembayaran} + > + Setiap Minggu dihari + </Checkbox> + <input + id='everyWeekdayInputPembayaran' + name='everyWeekdayInputPembayaran' + placeholder='Format: Senin, Selasa, Rabu, Kamis, Jumat, Sabtu, Minggu' + type='text' + value={formPengiriman.everyWeekdayInputPembayaran} + className='form-input' + onChange={handleInputChange} + /> + </div> + <div className='flex gap-2 flex-col'> + <Checkbox + size='sm' + colorScheme='red' + isChecked={everyWeekPembayaran} + onChange={handleEveryWeekPembayaran} + > + Setiap Bulan di minggu ke + </Checkbox> + <input + id='everyWeekInputPembayaran' + name='everyWeekInputPembayaran' + placeholder='Format: Minggu 1 & Minggu 2' + type='text' + value={formPengiriman.everyWeekInputPembayaran} + className='form-input' + onChange={handleInputChange} + /> + </div> + <div className='flex gap-2 flex-col'> + <Checkbox + size='sm' + colorScheme='red' + isChecked={tukarInvoicePembayaran} + onChange={handleTukarInvoicePembayaran} + > + Lainnya + </Checkbox> + <textarea + id='tukarInvoiceInputPembayaran' + name='tukarInvoiceInputPembayaran' + placeholder='isi manual dokumen yang anda mau' + type='textarea' + value={formPengiriman.tukarInvoiceInputPembayaran} + className='form-input' + rows={4} + cols={40} + onChange={handleInputChange} + /> + </div> + </div> + </div> + + <div className='flex flex-col gap-2 justify-between items-start'> + <label className='form-label text-wrap'> + Dokumen saat Pengiriman Barang + </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} + > + <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 + size='sm' + colorScheme='red' + key='4' + isChecked={isChecked(4)} + onChange={() => handleCheckboxChange(4)} + > + Lainnya + </Checkbox> + <textarea + id='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} + /> + </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 dilampirkan saat Pengiriman Invoice + </label> + <span className='text-xs opacity-60'> + Pilih dokumen lampiran saat pengiriman invoice + </span> + <div + className='w-full flex gap-2 flex-col' + ref={dokumenPengirimanInvoiceRef} + > + <Checkbox + size='sm' + colorScheme='red' + key='0' + isChecked={isCheckedInvoice(0)} + onChange={() => + handleCheckboxChangeDokumenPengirimanInvoice(0) + } + > + Invoice Pembelian + </Checkbox> + <Checkbox + size='sm' + colorScheme='red' + key='1' + isChecked={isCheckedInvoice(1)} + onChange={() => + handleCheckboxChangeDokumenPengirimanInvoice(1) + } + > + Surat Jalan + </Checkbox> + <Checkbox + size='sm' + colorScheme='red' + key='2' + isChecked={isCheckedInvoice(2)} + onChange={() => + handleCheckboxChangeDokumenPengirimanInvoice(2) + } + > + Berita Acara Serah Terima (BAST) + </Checkbox> + <Checkbox + size='sm' + colorScheme='red' + key='3' + isChecked={isCheckedInvoice(3)} + onChange={() => + handleCheckboxChangeDokumenPengirimanInvoice(3) + } + > + Faktur Pajak + </Checkbox> + <Checkbox + size='sm' + colorScheme='red' + key='4' + isChecked={isCheckedInvoice(4)} + onChange={() => + handleCheckboxChangeDokumenPengirimanInvoice(4) + } + > + Good Receipt (GR) + </Checkbox> + + <div className='flex gap-3 flex-col'> + <Checkbox + size='sm' + colorScheme='red' + key='5' + isChecked={isCheckedInvoice(5)} + onChange={() => + handleCheckboxChangeDokumenPengirimanInvoice(5) + } + > + Lainnya + </Checkbox> + <textarea + id='dokumenPengirimanInvoiceInput' + name='dokumenPengirimanInvoiceInput' + placeholder='isi manual dokumen yang anda mau' + type='textarea' + ref={dokumenPengirimanInvoiceInputRef} + value={formPengiriman.dokumenPengirimanInvoiceInput} + className='form-input' + rows={4} + cols={40} + onChange={handleInputChange} + /> + </div> + {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..8cb3b0c3 --- /dev/null +++ b/src/lib/pengajuan-tempo/component/Referensi.jsx @@ -0,0 +1,548 @@ +import React, { useState, useEffect, useMemo, useRef } from 'react'; +import { useForm } from 'react-hook-form'; +import { usePengajuanTempoStoreSupplier } 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'; +const initialData = []; +const Referensi = ({ chekValid, buttonSubmitClick }) => { + 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 } = + usePengajuanTempoStoreSupplier(); + 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) => { + 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); + updateHasSave(false); + }; + + const handleNewSupplierChange = (e) => { + 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); + updateHasSave(false); + }; + + const handleAddNewSupplier = () => { + if (Object.values(newSupplier).every((val) => val.trim() !== '')) { + setSupplierData((prevData) => { + const newData = [...prevData, newSupplier]; + return newData; + }); + + setNewSupplier({ + supplier: '', + pic: '', + telepon: '', + durasiTempo: '', + creditLimit: '', + }); + } + updateHasSave(false); + }; + + 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 (cachedData) { + setSupplierData(cachedData); + updateFormSupplier(cachedData); + } + }, [buttonSubmitClick]); + + useEffect(() => { + setOpenIndexes(supplierData.map((_, index) => index)); + }, [supplierData]); + + const toggleOpen = (index) => { + setOpenIndexes((prev) => + prev.includes(index) ? prev.filter((i) => i !== index) : [...prev, index] + ); + }; + return ( + <> + {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> + <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' + /> + </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> + <input + name='creditLimit' + value={formatRupiah(newSupplier.creditLimit)} + type='text' + className='form-input border px-4 py-2' + onChange={handleNewSupplierChange} + placeholder='limit kredit' + /> + </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> + <button + onClick={simpanData} + 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' + > + 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' /> + {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'> + <span className='text-xs opacity-60 text-red-500'> + *Klik simpan sebelum lanjut ke tahap selanjutnya + </span> + <button + onClick={simpanData} + className='bg-gray-200 border border-gray-500 rounded-md w-full text-sm text-gray-500 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..539fe3f3 --- /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-[0.8px] ${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..8a1b3508 --- /dev/null +++ b/src/lib/pengajuan-tempo/component/informasiPerusahaan.jsx @@ -0,0 +1,1401 @@ +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'; +import useDevice from '@/core/hooks/useDevice'; +import Divider from '@/core/components/elements/Divider/Divider'; +const InformasiPerusahaan = ({ + chekValid, + buttonSubmitClick, + isKonfirmasi, +}) => { + const { isDesktop, isMobile } = useDevice(); + const { control, watch, setValue, getValues } = 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: 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(); + 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); + updateForm('estimasi', formattedValue.replace(/^Rp\s*/, '')); + validate(); + }; + const onChangeTempoDuration = (e) => { + updateForm('tempoDuration', `${e}`); + validate(); + }; + + const onChangeTempoLimit = (e) => { + updateForm('tempoLimit', `${e}`); + 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 [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 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]); + useEffect(() => { + if (form.industry_id) { + setValue('industry_id', parseInt(form.industry_id)); + } + if (form.state) { + setValue('state', parseInt(form.state)); + } + if (form.city) { + setValue('city', parseInt(form.city)); + } + if (form.tempoDuration) { + setValue('tempoDuration', 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]); + return ( + <> + {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' + 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'> + isi detail perusahaan sesuai dengan nama yang terdaftar + </span> + )} + </div> + <div className='w-3/5'> + <Controller + name='industry_id' + control={control} + render={(props) => ( + <HookFormSelect + {...props} + options={industries} + placeholder={'Pilih industri bisnis 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.industry_id} + </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 flex-row w-full gap-3'> + <div className='w-2/5' 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-1/3' 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 className='w-1/3'> + <input + id='zip' + name='zip' + ref={zipRef} + placeholder='Kode Pos' + type='number' + 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. Telfon 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 '> + Estimasi Pembelian pertahun + </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-row items-center'> + <div className='w-1/5' 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> + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.tempoDuration} + </div> + )} + </div> + {!isKonfirmasi && ( + <div className='w-4/5 flex flex-row justify-between items-center'> + <div className='w-2/5 text-nowrap'> + <label className='form-label '>Limit Tempo</label> + <span className='text-xs opacity-60'> + Ajukan nilai limit yang anda mau + </span> + </div> + <div + className='flex flex-col justify-start items-start' + ref={tempoLimitRef} + > + <RadioGroup + onChange={(value) => { + if (value === 'custom') { + setIsCustom(true); + updateForm('tempoLimit', tempoLimitValue); // Update dengan nilai input custom jika dipilih + } else { + setIsCustom(false); + onChangeTempoLimit(value); // Update dengan nilai radio button yang dipilih + } + }} + className='flex items-center justify-between' + value={isCustom ? 'custom' : form.tempoLimit} + > + <Stack direction='row'> + {/* Kolom 1 */} + <Stack + direction='column' + spacing={2} + className='mr-4' + > + {radioOptions.slice(0, 4).map((option) => ( + <Radio + key={option.value} + colorScheme='red' + value={option.value} + > + {option.label} + </Radio> + ))} + </Stack> + + {/* Kolom 2 */} + <Stack + direction='column' + className='ml-8' + spacing={2} + > + {radioOptions.slice(4).map((option) => ( + <Radio + key={option.value} + colorScheme='red' + value={option.value} + > + {option.label} + </Radio> + ))} + <div className='flex flex-row items-center'> + <Radio colorScheme='red' value='custom'></Radio> + + <input + placeholder='Isi limit yang anda inginkan' + type='text' + className='border ml-2 p-1' // padding untuk memberi ruang untuk "RP" + value={formatRupiah(tempoLimitValueEx)} // Menampilkan nilai terformat + onChange={(e) => { + const value = e.target.value; + const formattedValue = formatRupiah(value); + setTempoLimitValueEx(formattedValue); + updateForm( + 'tempoLimit', + formattedValue.replace(/^Rp\s*/, '') + ); // Mengupdate nilai di react-hook-form + }} + /> + </div> + </Stack> + </Stack> + </RadioGroup> + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.tempoLimit} + </div> + )} + </div> + </div> + )} + </div> + </div> + {isKonfirmasi && ( + <div className='flex flex-row justify-between items-center'> + <div className='w-2/5'> + <label className='form-label w-2/5 text-nowrap'> + Limit Tempo + </label> + <span className='text-xs opacity-60'> + Ajukan nilai limit yang anda mau + </span> + </div> + <div + className='w-3/5 flex flex-col justify-start items-start' + ref={tempoLimitRef} + > + <RadioGroup + onChange={(value) => { + if (value === 'custom') { + setIsCustom(true); + updateForm('tempoLimit', tempoLimitValue); // Update dengan nilai input custom jika dipilih + } else { + setIsCustom(false); + onChangeTempoLimit(value); // Update dengan nilai radio button yang dipilih + } + }} + className='flex items-center justify-between' + value={isCustom ? 'custom' : form.tempoLimit} + > + <Stack direction='row'> + {/* Kolom 1 */} + <Stack direction='column' spacing={2} className='mr-4'> + {radioOptions.slice(0, 4).map((option) => ( + <Radio + key={option.value} + colorScheme='red' + value={option.value} + > + {option.label} + </Radio> + ))} + </Stack> + + {/* Kolom 2 */} + <Stack direction='column' className='ml-8' spacing={2}> + {radioOptions.slice(4).map((option) => ( + <Radio + key={option.value} + colorScheme='red' + value={option.value} + > + {option.label} + </Radio> + ))} + <div className='flex flex-row items-center'> + <Radio colorScheme='red' value='custom'></Radio> + + <input + placeholder='Isi limit yang anda inginkan' + type='text' + className='border ml-2 p-1' // padding untuk memberi ruang untuk "RP" + value={formatRupiah(tempoLimitValueEx)} // Menampilkan nilai terformat + onChange={(e) => { + const value = e.target.value; + const formattedValue = formatRupiah(value); + setTempoLimitValueEx(formattedValue); + updateForm( + 'tempoLimit', + formattedValue.replace(/^Rp\s*/, '') + ); // Mengupdate nilai di react-hook-form + }} + /> + </div> + </Stack> + </Stack> + </RadioGroup> + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.tempoLimit} + </div> + )} + </div> + </div> + )} + + <div className='text-red-500'> + *Durasi dan limit dapat berbeda sesuai dengan verifikasi oleh + tim Indoteknik.com + </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> + {!isKonfirmasi && ( + <span className='text-xs opacity-60'> + Pilih produk bisa lebih dari 1 + </span> + )} + </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> + {/* <Checkbox + name='bersedia' + borderColor='gray.600' + colorScheme='red' + isChecked={bersedia === true} // Checked when bersedia is true + onChange={() => handleCheckboxBersediaChange('bersedia')} + value={true} + size='md' + > + Saya bersedia + </Checkbox> + <Checkbox + name='bersedia' + borderColor='gray.600' + colorScheme='red' + isChecked={bersedia === false} // Checked when bersedia is false + onChange={() => handleCheckboxBersediaChange('tidakBersedia')} + value={false} + size='md' + > + Tidak bersedia + </Checkbox> */} + </div> + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.estimasi} + </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='industry_id' + 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.industry_id} + </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} + /> + <div className='w-full text-nowrap'> + <div className='sub-alamat flex flex-row w-full gap-3'> + <div className='w-2/5' 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-1/3' 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 className='w-1/3'> + <input + id='zip' + name='zip' + ref={zipRef} + placeholder='Kode Pos' + type='number' + 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> + )} + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.street} + </div> + )} + </div> + + <div className='flex flex-col gap-2 justify-between items-start'> + <label className='form-label '>No. Telfon 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> + <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} + /> + </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> + {!isKonfirmasi && ( + <span className='text-xs opacity-60'> + Isi data bank perusahaan sesuai dengan yang terdaftar + </span> + )} + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.bankName} + </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 + </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='' ref={tempoDurationRef}> + <RadioGroup + size='sm' + onChange={onChangeTempoDuration} + value={form.tempoDuration} + > + <Stack direction='row' 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> + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.tempoDuration} + </div> + )} + </div> + </div> + <div className='flex flex-col gap-2 justify-start items-start'> + <label className='form-label '>Limit Tempo</label> + <div + className='flex justify-between items-center ' + ref={tempoLimitRef} + > + <RadioGroup + size='sm' + onChange={(value) => { + if (value === 'custom') { + setIsCustom(true); + updateForm('tempoLimit', tempoLimitValue); // Update dengan nilai input custom jika dipilih + } else { + setIsCustom(false); + onChangeTempoLimit(value); // Update dengan nilai radio button yang dipilih + } + }} + className='flex items-center justify-between' + value={isCustom ? 'custom' : form.tempoLimit} + > + <Stack direction='row'> + {/* Kolom 1 */} + <Stack direction='row' spacing={2} className='mr-4'> + {radioOptions.slice(0, 3).map((option) => ( + <Radio + key={option.value} + colorScheme='red' + value={option.value} + > + {option.label} + </Radio> + ))} + <Radio colorScheme='red' value='custom'></Radio> + + <input + placeholder='Isi limit' + type='text' + className='border ml-1 p-1 w-full ' // padding untuk memberi ruang untuk "RP" + value={formatRupiah(tempoLimitValueEx)} // Menampilkan nilai terformat + onChange={(e) => { + const value = e.target.value; + const formattedValue = formatRupiah(value); + setTempoLimitValueEx(formattedValue); + updateForm( + 'tempoLimit', + formattedValue.replace(/^Rp\s*/, '') + ); // Mengupdate nilai di react-hook-form + }} + /> + </Stack> + + {/* Kolom 2 */} + {/* <Stack direction='column' className='ml-8' spacing={2}> + {radioOptions.slice(4).map((option) => ( + <Radio + key={option.value} + colorScheme='red' + value={option.value} + > + {option.label} + </Radio> + ))} + <div className='flex flex-row items-center'></div> + </Stack> */} + </Stack> + </RadioGroup> + {chekValid && ( + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.tempoLimit} + </div> + )} + </div> + </div> + <div className='text-red-500 text-xs'> + **Durasi & Limit 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 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.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-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/pages/pengajuan-tempo/[status].jsx b/src/pages/pengajuan-tempo/[status].jsx new file mode 100644 index 00000000..1ebf167b --- /dev/null +++ b/src/pages/pengajuan-tempo/[status].jsx @@ -0,0 +1,32 @@ +import BasicLayout from '@/core/components/layouts/BasicLayout'; +import IsAuth from '@/lib/auth/components/IsAuth'; +import FinishTempoComponent from '@/lib/pengajuan-tempo/component/FinishTempo'; +import { useRouter } from 'next/router'; +import axios from 'axios'; +import Seo from '@/core/components/Seo'; + +export async function getServerSideProps(context) { + const { tempo_id } = context.query; + // await axios.post( + // `${process.env.NEXT_PUBLIC_SELF_HOST}/api/pengajuan-tempo?formId=${tempo_id}`, + // {}, + // { headers: context.req.headers } + // ); + return { props: {} }; +} + +export default function Finish() { + const router = useRouter(); + + return ( + <> + <Seo title='Pengajuan Tempo Indoteknik.com' /> + + <IsAuth> + <BasicLayout> + <FinishTempoComponent query={router.query || {}} /> + </BasicLayout> + </IsAuth> + </> + ); +} diff --git a/src/pages/pengajuan-tempo/index.jsx b/src/pages/pengajuan-tempo/index.jsx new file mode 100644 index 00000000..23d7aaba --- /dev/null +++ b/src/pages/pengajuan-tempo/index.jsx @@ -0,0 +1,29 @@ +import Seo from '@/core/components/Seo'; +import dynamic from 'next/dynamic'; +import SimpleFooter from '@/core/components/elements/Footer/SimpleFooter'; +import BasicLayout from '@/core/components/layouts/BasicLayout'; +import DesktopView from '@/core/components/views/DesktopView'; +import MobileView from '@/core/components/views/MobileView'; +const PagePengajuanTempo = dynamic(() => + import('@/lib/pengajuan-tempo/component/PengajuanTempo') +); + +export default function TrackingOrder() { + return ( + <> + <Seo title='Pengajuan Tempo - Indoteknik.com' /> + + <DesktopView> + <BasicLayout> + <PagePengajuanTempo /> + </BasicLayout> + </DesktopView> + + <MobileView> + <BasicLayout> + <PagePengajuanTempo /> + </BasicLayout> + </MobileView> + </> + ); +} |
