Initial commit, repo migration
This commit is contained in:
@@ -0,0 +1,359 @@
|
||||
import { CardCustom } from '@/components/CardCustom'
|
||||
import { Belakang } from '@/components/Layouts'
|
||||
import { Judul, SubJudul } from '@/components/TextCustom'
|
||||
import { TagihanGetOne, TagihanSubmit } from '@/services/pengajuan-tagihan/tagihan-service'
|
||||
import { Formik } from 'formik'
|
||||
import Head from 'next/head'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Accordion, AccordionTab } from 'primereact/accordion'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Card } from 'primereact/card'
|
||||
import { FileUpload } from 'primereact/fileupload'
|
||||
import { InputTextarea } from 'primereact/inputtextarea'
|
||||
import { ProgressBar } from 'primereact/progressbar'
|
||||
import { TabPanel, TabView } from 'primereact/tabview'
|
||||
import { Toast } from 'primereact/toast'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import Swal from 'sweetalert2'
|
||||
import axios from '../../../lib/axios'
|
||||
|
||||
export default function Ditolak() {
|
||||
const router = useRouter()
|
||||
const [data, setData] = useState([])
|
||||
const toast = useRef(null)
|
||||
const [progressUpload, setProgressUpload] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
TagihanGetOne({ ref_id: router.query.ditolak }).then((res) => {
|
||||
if (res.status === 'ok') {
|
||||
setData(res.data)
|
||||
} else {
|
||||
console.log(res.message)
|
||||
}
|
||||
})
|
||||
}, [router])
|
||||
|
||||
const formikInitialValues = () => {
|
||||
let groupTagihan = []
|
||||
let detailIDTolak = []
|
||||
data.length > 0 &&
|
||||
data[1].map((valData) => {
|
||||
valData.syarat.map((valSyarat) => {
|
||||
if (valSyarat.ms === 'tms') {
|
||||
let cek_detail_id = detailIDTolak.find((e) => e === valData.detail_id)
|
||||
!cek_detail_id && detailIDTolak.push(valData.detail_id)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
detailIDTolak.map((valDetailIDTolak, iDetailIDTolak) => {
|
||||
data.length > 0 &&
|
||||
data[1]
|
||||
.filter((valTagihan) => valTagihan.detail_id === detailIDTolak[iDetailIDTolak])
|
||||
.map((valData) => {
|
||||
groupTagihan.push({
|
||||
detail_id: valData.detail_id,
|
||||
jenis_belanja: valData.jenis_belanja,
|
||||
jenis_kegiatan: valData.jenis_kegiatan,
|
||||
syarat: valData.syarat.map((valSyarat) => {
|
||||
if (valSyarat.ms === 'tms') {
|
||||
return {
|
||||
syarat_id: valSyarat.syarat_id,
|
||||
syarat_dokumen: valSyarat.syarat_dokumen,
|
||||
keterangan: valSyarat.keterangan,
|
||||
file: '',
|
||||
file_name: '',
|
||||
ms: valSyarat.ms,
|
||||
verif_note: valSyarat.verif_note,
|
||||
kasub_note: valSyarat.kasub_note,
|
||||
kabag_note: valSyarat.kabag_note,
|
||||
bendahara_note: valSyarat.bendahara_note,
|
||||
spm_note: valSyarat.spm_note,
|
||||
}
|
||||
}
|
||||
}),
|
||||
akun_id: valData.akun_id,
|
||||
filepath_old: valData.filepath,
|
||||
keperluan: valData.keperluan,
|
||||
draft: false,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return {
|
||||
tagihan_id: data[0].tagihan_id,
|
||||
detail_tagihan: groupTagihan,
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Toast ref={toast} />
|
||||
<Head>
|
||||
<title>Pengajuan Tagihan Ulang</title>
|
||||
</Head>
|
||||
<Belakang>
|
||||
{data.length > 0 && (
|
||||
<React.Fragment>
|
||||
<div className='mb-5'></div>
|
||||
<Judul>Form Pengajuan Tagihan Ulang</Judul>
|
||||
<Card>
|
||||
<div className='grid grid-cols-2 gap-3'>
|
||||
<CardCustom header={'No SPP'} content={data[0].no_spp} />
|
||||
<CardCustom header={'Unit PPK'} content={data[0].unit} />
|
||||
<CardCustom header={'Jenis Tagihan'} content={data[0].tagihan} />
|
||||
</div>
|
||||
</Card>
|
||||
<Formik
|
||||
initialValues={formikInitialValues()}
|
||||
onSubmit={(values, { setSubmitting }) => {
|
||||
setSubmitting(true)
|
||||
TagihanSubmit({ ref_id: data[0].tagihan_id })
|
||||
.then((res) => {
|
||||
if (res.status === 'ok') {
|
||||
Swal.fire({
|
||||
title: 'Success',
|
||||
text: 'Tagihan berhasil dikirim',
|
||||
icon: 'success',
|
||||
timer: 2000,
|
||||
timerProgressBar: true,
|
||||
}).then(() => {
|
||||
setSubmitting(false)
|
||||
router.push('/pengajuan-tagihan')
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
setSubmitting(false)
|
||||
console.log(err)
|
||||
})
|
||||
}}
|
||||
validate={(data) => {
|
||||
let errors = {}
|
||||
data.detail_tagihan.map((val) => {
|
||||
if (!val.file) {
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: 'file belum diupload',
|
||||
closable: false,
|
||||
})
|
||||
errors.filepath = val
|
||||
}
|
||||
})
|
||||
|
||||
return errors
|
||||
}}
|
||||
validateOnChange={false}
|
||||
>
|
||||
{({ values, setFieldValue, handleSubmit, isSubmitting }) => {
|
||||
const handleRevisi = () => {
|
||||
handleSubmit()
|
||||
}
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Accordion className='mt-3 shadow'>
|
||||
{values.detail_tagihan.map((valDetailTagihan, indexDetailTagihan) => {
|
||||
const customBase64Uploader = async (event) => {
|
||||
// convert file to base64 encoded
|
||||
const file = event.files[0]
|
||||
if (file) {
|
||||
if (file.type === 'application/pdf') {
|
||||
const reader = new FileReader()
|
||||
reader.readAsDataURL(file)
|
||||
|
||||
reader.onloadend = async function () {
|
||||
const base64data = reader.result
|
||||
setFieldValue(`detail_tagihan[${indexDetailTagihan}].file`, base64data)
|
||||
setFieldValue(`detail_tagihan[${indexDetailTagihan}].file_name`, file.name)
|
||||
|
||||
const kirimSatuan = {
|
||||
detail_id: valDetailTagihan.detail_id,
|
||||
file: base64data,
|
||||
file_name: file.name,
|
||||
}
|
||||
|
||||
await axios
|
||||
.post('tagihan/revisi_detail', kirimSatuan, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
onUploadProgress: (progresEvent) => {
|
||||
const percentedCompleted = Math.round(
|
||||
(progresEvent.loaded * 100) / progresEvent.total
|
||||
)
|
||||
setProgressUpload(percentedCompleted)
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: 'File berhasil diupload',
|
||||
closable: false,
|
||||
})
|
||||
})
|
||||
.catch((err) => console.log(err))
|
||||
}
|
||||
} else {
|
||||
event.options.clear()
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: 'Invalid format file',
|
||||
closable: false,
|
||||
})
|
||||
setFieldValue(`detail_tagihan[${indexDetailTagihan}].file`, null)
|
||||
setFieldValue(`detail_tagihan[${indexDetailTagihan}].file_name`, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
return (
|
||||
<AccordionTab
|
||||
header={`Tagihan ` + (parseInt(indexDetailTagihan) + 1)}
|
||||
key={indexDetailTagihan}
|
||||
>
|
||||
<div className='grid grid-cols-2 gap-3'>
|
||||
<CardCustom
|
||||
header={'Jenis Belanja'}
|
||||
content={valDetailTagihan.jenis_belanja}
|
||||
type='cream'
|
||||
/>
|
||||
<CardCustom
|
||||
header={'Jenis Kegiatan'}
|
||||
content={valDetailTagihan.jenis_kegiatan}
|
||||
type='cream'
|
||||
/>
|
||||
<CardCustom header={'Akun'} content={valDetailTagihan.akun_id} type='cream' />
|
||||
<CardCustom
|
||||
header={'Keperluan Untuk'}
|
||||
content={valDetailTagihan.keperluan}
|
||||
type='cream'
|
||||
/>
|
||||
</div>
|
||||
<div className='my-3'>
|
||||
<SubJudul className='text-lg mb-1'>Syarat</SubJudul>
|
||||
<Accordion className='accordion-syarat'>
|
||||
{valDetailTagihan.syarat.map((valSyarat, indexSyarat) => {
|
||||
if (valSyarat) {
|
||||
return (
|
||||
<AccordionTab
|
||||
header={valSyarat.syarat_dokumen}
|
||||
key={indexSyarat}
|
||||
headerClassName='accordiontab-revisi'
|
||||
>
|
||||
<table className='custom-table' key={indexSyarat}>
|
||||
<tbody>
|
||||
<tr className=''>
|
||||
<td colSpan={4}>
|
||||
<p className='text-xl font-semibold mb-1'>Catatan</p>
|
||||
<TabView key={indexSyarat}>
|
||||
<TabPanel header='Catatan Verif'>
|
||||
<InputTextarea
|
||||
rows={5}
|
||||
cols={30}
|
||||
value={valSyarat.verif_note ? valSyarat.verif_note : ''}
|
||||
className='w-full'
|
||||
readOnly
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel header='Catatan Bendahara'>
|
||||
<InputTextarea
|
||||
rows={5}
|
||||
cols={30}
|
||||
value={valSyarat.bendahara_note ? valSyarat.bendahara_note : ''}
|
||||
className='w-full'
|
||||
readOnly
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel header='Catatan SPM'>
|
||||
<InputTextarea
|
||||
rows={5}
|
||||
cols={30}
|
||||
value={valSyarat.spm_note ? valSyarat.spm_note : ''}
|
||||
className='w-full'
|
||||
readOnly
|
||||
/>
|
||||
</TabPanel>
|
||||
</TabView>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</AccordionTab>
|
||||
)
|
||||
}
|
||||
})}
|
||||
</Accordion>
|
||||
</div>
|
||||
<div className='my-3'>
|
||||
<table className='custom-table'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>File Sebelum</th>
|
||||
<th>Upload File Revisi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className='text-center'>
|
||||
<Link href={process.env.NEXT_PUBLIC_BACKEND_API + valDetailTagihan.filepath_old}>
|
||||
<a target={'_blank'} className='flex items-center justify-center gap-x-1'>
|
||||
<i className='pi pi-file-pdf' style={{ fontSize: '2em' }} />
|
||||
<p className='text-sm hover:underline'>Klik disini untuk lihat file</p>
|
||||
</a>
|
||||
</Link>
|
||||
</td>
|
||||
<td>
|
||||
<FileUpload
|
||||
name='upload-syarat'
|
||||
url={'#'}
|
||||
accept='.pdf'
|
||||
emptyTemplate={<p className='m-0'>Drag and drop files to here to upload.</p>}
|
||||
customUpload
|
||||
uploadHandler={(e) => customBase64Uploader(e)}
|
||||
onClear={() =>
|
||||
setFieldValue(`detail_tagihan[${indexDetailTagihan}].file`, false)
|
||||
}
|
||||
onRemove={() =>
|
||||
setFieldValue(`detail_tagihan[${indexDetailTagihan}].file`, false)
|
||||
}
|
||||
uploadOptions={{
|
||||
className: values.detail_tagihan[indexDetailTagihan].file ? 'p-disabled' : '',
|
||||
}}
|
||||
cancelOptions={{
|
||||
className: values.detail_tagihan[indexDetailTagihan].file ? 'p-disabled' : '',
|
||||
}}
|
||||
progressBarTemplate={
|
||||
<ProgressBar
|
||||
value={progressUpload}
|
||||
style={{ height: '1rem', fontSize: '13px' }}
|
||||
></ProgressBar>
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</AccordionTab>
|
||||
)
|
||||
})}
|
||||
</Accordion>
|
||||
<div className='mt-3 flex justify-center gap-x-3'>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Kirim'
|
||||
onClick={() => handleRevisi()}
|
||||
className='p-button-sm'
|
||||
loading={isSubmitting}
|
||||
/>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)
|
||||
}}
|
||||
</Formik>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</Belakang>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,651 @@
|
||||
import { CardCustom } from '@/components/CardCustom'
|
||||
import { Belakang } from '@/components/Layouts'
|
||||
import { Judul, LabelInput } from '@/components/TextCustom'
|
||||
import {
|
||||
TagihanAddDetail,
|
||||
TagihanGetOne,
|
||||
TagihanResubmit,
|
||||
TagihanSubmit,
|
||||
} from '@/services/pengajuan-tagihan/tagihan-service'
|
||||
import { AkunList } from '@/services/referensi/akun-service'
|
||||
import { JenisKegiatanList } from '@/services/referensi/jenisKegiatan-service'
|
||||
import { PersyaratanSearch } from '@/services/referensi/persyaratan-service'
|
||||
import { FieldArray, Formik } from 'formik'
|
||||
import { fetchAPI } from 'lib/fetch'
|
||||
import Head from 'next/head'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Accordion, AccordionTab } from 'primereact/accordion'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Card } from 'primereact/card'
|
||||
import { Dropdown } from 'primereact/dropdown'
|
||||
import { FileUpload } from 'primereact/fileupload'
|
||||
import { InputTextarea } from 'primereact/inputtextarea'
|
||||
import { Toast } from 'primereact/toast'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import Swal from 'sweetalert2'
|
||||
|
||||
export default function Draft({ optionJenisBelanja = null }) {
|
||||
const router = useRouter()
|
||||
const [data, setData] = useState([])
|
||||
const toast = useRef(null)
|
||||
const [optionJenisKegiatan, setOptionJenisKegiatan] = useState([])
|
||||
const [optionAkun, setOptionAkun] = useState([])
|
||||
const [countJenisTagihan, setCountJenisTagihan] = useState(0)
|
||||
const [kirimLoading, setKirimLoading] = useState(false)
|
||||
const [tagihanID, setTagihanID] = useState('')
|
||||
const [statusDraft, setStatusDraft] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
TagihanGetOne({ ref_id: router.query.draft }).then((res) => {
|
||||
if (res.status === 'ok') {
|
||||
setData(res.data)
|
||||
setTagihanID(res.data[0].tagihan_id)
|
||||
} else {
|
||||
console.log(res.message)
|
||||
}
|
||||
})
|
||||
|
||||
JenisKegiatanList()
|
||||
.then((res) => setOptionJenisKegiatan(res.data))
|
||||
.catch((err) => console.log(err))
|
||||
}, [router])
|
||||
|
||||
useEffect(() => {
|
||||
let params = {}
|
||||
params.draw = 0
|
||||
AkunList(params).then((res) => setOptionAkun(res.data))
|
||||
}, [])
|
||||
|
||||
const formikInitialValues = () => {
|
||||
let lastIndex = 0
|
||||
if (data.length > 0) {
|
||||
lastIndex = data[1].length - 1
|
||||
}
|
||||
const init = data.length > 0 && {
|
||||
jenis_tagihan_id: data[0].jenis_tagihan_id,
|
||||
tagihan_id: data[0].tagihan_id,
|
||||
detail_tagihan: [
|
||||
{
|
||||
detail_id: data[1][lastIndex].detail_id,
|
||||
jenis_belanja: data[1][lastIndex].jenis_belanja_id,
|
||||
jenis_kegiatan: data[1][lastIndex].jenis_kegiatan_id,
|
||||
filepath: data[1][lastIndex].filepath,
|
||||
syarat: data[1][lastIndex].syarat.map((valSyarat) => {
|
||||
return {
|
||||
syarat_id: valSyarat.syarat_id,
|
||||
syarat_dokumen: valSyarat.syarat_dokumen,
|
||||
keterangan: valSyarat.keterangan,
|
||||
}
|
||||
}),
|
||||
akun_id: data[1][lastIndex].akun_id,
|
||||
keperluan: data[1][lastIndex].keperluan,
|
||||
draft: false,
|
||||
status: false,
|
||||
},
|
||||
],
|
||||
dataIndex: 0, //* For select data index
|
||||
}
|
||||
|
||||
return init
|
||||
}
|
||||
|
||||
const handleBtnKirim = () => {
|
||||
Swal.fire({
|
||||
title: 'Apakah data diatas sudah benar semua?',
|
||||
text: 'Tagihan yang sudah dikirim tidak bisa diubah kembali',
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#3085d6',
|
||||
cancelButtonColor: '#d33',
|
||||
confirmButtonText: 'Ya, kirim!',
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
setKirimLoading(true)
|
||||
TagihanSubmit({ ref_id: tagihanID })
|
||||
.then((res) => {
|
||||
if (res.status == 'ok') {
|
||||
setKirimLoading(false)
|
||||
Swal.fire({
|
||||
title: 'Success',
|
||||
text: 'Tagihan berhasil dikirim',
|
||||
icon: 'success',
|
||||
timer: 2000,
|
||||
timerProgressBar: true,
|
||||
}).then(() => {
|
||||
router.push('/pengajuan-tagihan')
|
||||
})
|
||||
} else {
|
||||
setKirimLoading(false)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
setKirimLoading(false)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Toast ref={toast} />
|
||||
<Head>
|
||||
<title>Pengajuan Tagihan Draft</title>
|
||||
</Head>
|
||||
<Belakang>
|
||||
{data.length > 0 && (
|
||||
<React.Fragment>
|
||||
<div className='mb-5'></div>
|
||||
<Judul>Form Persetujuan Draft</Judul>
|
||||
<Card>
|
||||
<div className='grid grid-cols-2 gap-3'>
|
||||
<CardCustom header={'No SPP'} content={data[0].no_spp} />
|
||||
<CardCustom header={'Unit PPK'} content={data[0].unit} />
|
||||
<CardCustom header={'Jenis Tagihan'} content={data[0].tagihan} />
|
||||
</div>
|
||||
</Card>
|
||||
<Formik
|
||||
initialValues={formikInitialValues()}
|
||||
onSubmit={(data, { setSubmitting, setFieldValue }) => {
|
||||
setFieldValue('detail_tagihan[' + data.dataIndex + '].loading', true)
|
||||
/**
|
||||
* *Filter data detail tagihan follow the button kirim
|
||||
*/
|
||||
const filterData = data.detail_tagihan.find((item, i) => i === data.dataIndex)
|
||||
|
||||
/**
|
||||
* *Create new format data detail tagihan reference to API
|
||||
*/
|
||||
const dataKirim = {
|
||||
tagihan_id: data.tagihan_id,
|
||||
detail_tagihan: [filterData],
|
||||
}
|
||||
|
||||
if (data.dataIndex) {
|
||||
TagihanAddDetail(dataKirim)
|
||||
.then((res) => {
|
||||
if (res.status === 'ok') {
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
if (statusDraft) {
|
||||
router.push('/pengajuan-tagihan')
|
||||
}
|
||||
/**
|
||||
* *Add status to detail tagihan object for check if it is sended or not
|
||||
*/
|
||||
setFieldValue('detail_tagihan[' + data.dataIndex + '].status', true)
|
||||
setFieldValue('detail_tagihan[' + data.dataIndex + '].loading', false)
|
||||
setSubmitting(false)
|
||||
} else {
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
/**
|
||||
* *Add status to detail tagihan object for check if it is sended or not
|
||||
*/
|
||||
setFieldValue('detail_tagihan[' + data.dataIndex + '].status', false)
|
||||
setFieldValue('detail_tagihan[' + data.dataIndex + '].loading', false)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: 'Error saat pembuatan data/file belum diupload',
|
||||
closable: false,
|
||||
})
|
||||
})
|
||||
} else {
|
||||
TagihanResubmit(dataKirim)
|
||||
.then((res) => {
|
||||
if (res.status === 'ok') {
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
if (statusDraft) {
|
||||
router.push('/pengajuan-tagihan')
|
||||
}
|
||||
/**
|
||||
* *Add status to detail tagihan object for check if it is sended or not
|
||||
*/
|
||||
setFieldValue('detail_tagihan[' + data.dataIndex + '].status', true)
|
||||
setFieldValue('detail_tagihan[' + data.dataIndex + '].loading', false)
|
||||
setSubmitting(false)
|
||||
} else {
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
/**
|
||||
* *Add status to detail tagihan object for check if it is sended or not
|
||||
*/
|
||||
setFieldValue('detail_tagihan[' + data.dataIndex + '].status', false)
|
||||
setFieldValue('detail_tagihan[' + data.dataIndex + '].loading', false)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: 'Error saat pembuatan data/file belum diupload',
|
||||
closable: false,
|
||||
})
|
||||
})
|
||||
}
|
||||
}}
|
||||
validate={(data) => {
|
||||
let errors = {}
|
||||
data.detail_tagihan.map((val) => {
|
||||
val.syarat.map((valSyarat, indexSyarat) => {
|
||||
if (valSyarat) {
|
||||
if (!valSyarat.file && valSyarat.ms === 'tms') {
|
||||
errors.file = { ...errors.file, [indexSyarat]: 'File Harus Diisi ' }
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return errors
|
||||
}}
|
||||
validateOnChange={false}
|
||||
>
|
||||
{({ values, setFieldValue, handleSubmit, isSubmitting, handleChange }) => {
|
||||
/**
|
||||
* *Form Detail Tagihan
|
||||
* *Button Submit and draft
|
||||
*/
|
||||
const handleSubmitDetailTagihan = (e, { draft, index }) => {
|
||||
e.preventDefault()
|
||||
setFieldValue(`detail_tagihan[${index}].status`, draft)
|
||||
setStatusDraft(draft)
|
||||
handleSubmit()
|
||||
}
|
||||
|
||||
/**
|
||||
* *Change Jenis Belanja
|
||||
* *Reset form detail tagihan
|
||||
*/
|
||||
const changeJenisBelanja = (event, index) => {
|
||||
setFieldValue(`detail_tagihan[${index}].syarat`, [])
|
||||
setFieldValue(`detail_tagihan[${index}].jenis_belanja`, event.value)
|
||||
|
||||
/**
|
||||
* SHOW LIST JENIS KEGIATAN WHERE JENIS_BELANJA_ID
|
||||
*/
|
||||
JenisKegiatanList({ jenis_belanja_id: event.value, draw: 0 })
|
||||
.then((res) => setOptionJenisKegiatan(res.data))
|
||||
.catch((err) => console.log(err))
|
||||
}
|
||||
|
||||
/**
|
||||
* *Dropdown list jenis kegiatan
|
||||
* *When jenis kegiatan change hit API to get list syarat
|
||||
*/
|
||||
const changeJenisKegiatan = (event, index) => {
|
||||
setFieldValue(`detail_tagihan[${index}].syarat`, [])
|
||||
setFieldValue(`detail_tagihan[${index}].jenis_kegiatan`, event.value)
|
||||
PersyaratanSearch({
|
||||
jenis_tagihan_id: values.jenis_tagihan_id,
|
||||
jenis_belanja_id: values.detail_tagihan[index].jenis_belanja,
|
||||
jenis_kegiatan_id: event.value,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.data) {
|
||||
res.data.map((item, index2) => {
|
||||
setFieldValue(`detail_tagihan[${index}].syarat[${index2}].syarat_id`, item.syarat_id)
|
||||
setFieldValue(
|
||||
`detail_tagihan[${index}].syarat[${index2}].syarat_dokumen`,
|
||||
item.syarat_dokumen
|
||||
)
|
||||
setFieldValue(`detail_tagihan[${index}].syarat[${index2}].keterangan`, item.keterangan)
|
||||
})
|
||||
} else {
|
||||
setFieldValue(`detail_tagihan[${index}].syarat`, [])
|
||||
toast.current.show({
|
||||
severity: 'info',
|
||||
detail: 'Data syarat kosong',
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: 'Error while get data',
|
||||
closable: false,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<FieldArray name='detail_tagihan'>
|
||||
{({ push }) => (
|
||||
<React.Fragment>
|
||||
<Accordion className='mt-3 shadow'>
|
||||
{values.detail_tagihan.length > 0 &&
|
||||
values.detail_tagihan.map((valDetailTagihan, indexDetailTagihan) => {
|
||||
const customBase64Uploader = async (event) => {
|
||||
// convert file to base64 encoded
|
||||
const file = event.files[0]
|
||||
if (file) {
|
||||
if (file.type === 'application/pdf') {
|
||||
const reader = new FileReader()
|
||||
reader.readAsDataURL(file)
|
||||
|
||||
reader.onloadend = function () {
|
||||
const base64data = reader.result
|
||||
setFieldValue(`detail_tagihan[${indexDetailTagihan}].file`, base64data)
|
||||
setFieldValue(`detail_tagihan[${indexDetailTagihan}].file_name`, file.name)
|
||||
}
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: 'File berhasil diupload',
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
event.options.clear()
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: 'Invalid format file',
|
||||
closable: false,
|
||||
})
|
||||
setFieldValue(`detail_tagihan[${indexDetailTagihan}].file`, null)
|
||||
setFieldValue(`detail_tagihan[${indexDetailTagihan}].file_name`, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
return (
|
||||
<AccordionTab header={`Tagihan ${indexDetailTagihan + 1}`} key={indexDetailTagihan}>
|
||||
<div className='p-fluid grid grid-cols-2 gap-3'>
|
||||
{/* JENIS BELANJA */}
|
||||
<div className='field col-12 md:col-4'>
|
||||
<LabelInput>Jenis Belanja</LabelInput>
|
||||
<Dropdown
|
||||
panelClassName='p-dropdown-form'
|
||||
name={`detail_tagihan[${indexDetailTagihan}].jenis_belanja`}
|
||||
options={optionJenisBelanja}
|
||||
optionLabel='nama'
|
||||
optionValue='jenis_id'
|
||||
value={values.detail_tagihan[indexDetailTagihan].jenis_belanja}
|
||||
onChange={(e) => changeJenisBelanja(e, indexDetailTagihan)}
|
||||
showClear={
|
||||
values.detail_tagihan[indexDetailTagihan] !== undefined ? true : false
|
||||
}
|
||||
scrollHeight='200px'
|
||||
placeholder='Select Jenis Belanja'
|
||||
/>
|
||||
{/* {errorFieldMessageDetail(indexDetailTagihan, 'jenis_belanja')} */}
|
||||
</div>
|
||||
{/* JENIS KEGIATAN */}
|
||||
<div className='field col-12 md:col-4'>
|
||||
<LabelInput>Jenis Kegiatan</LabelInput>
|
||||
<Dropdown
|
||||
panelClassName='p-dropdown-form'
|
||||
name={`detail_tagihan[${indexDetailTagihan}].jenis_kegiatan`}
|
||||
options={optionJenisKegiatan}
|
||||
optionLabel='nama'
|
||||
optionValue='jenis_id'
|
||||
value={values.detail_tagihan[indexDetailTagihan].jenis_kegiatan}
|
||||
onChange={(e) => changeJenisKegiatan(e, indexDetailTagihan)}
|
||||
showClear={
|
||||
values.detail_tagihan[indexDetailTagihan] !== undefined ? true : false
|
||||
}
|
||||
scrollHeight='200px'
|
||||
placeholder='Select Jenis Kegiatan'
|
||||
filter
|
||||
/>
|
||||
{/* {errorFieldMessageDetail(indexDetailTagihan, 'jenis_kegiatan')} */}
|
||||
</div>
|
||||
</div>
|
||||
{/* SYARAT */}
|
||||
<div className='p-fluid grid grid-cols-2 gap-3'>
|
||||
<div className='md:col-4 my-3'>
|
||||
<LabelInput>Syarat</LabelInput>
|
||||
<div className='relative mt-2 flex overflow-auto h-96'>
|
||||
<table className='w-full text-sm text-left '>
|
||||
<thead className='text-xs uppercase bg-gray-100 sticky top-0'>
|
||||
<tr>
|
||||
<th scope='col' className='py-3 px-6'>
|
||||
Persyaratan
|
||||
</th>
|
||||
<th scope='col' className='py-3 px-6'>
|
||||
Keterangan
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{valDetailTagihan.syarat.map((item, index2) => (
|
||||
<tr className='bg-white border-b' key={index2}>
|
||||
<td scope='row' className='py-4 px-6'>
|
||||
{item.syarat_dokumen}
|
||||
</td>
|
||||
<td scope='row' className='py-4 px-6'>
|
||||
{item.keterangan}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div className='upload-all my-3'>
|
||||
<LabelInput>Upload Syarat</LabelInput>
|
||||
<div className='py-4 px-6'>
|
||||
{valDetailTagihan.file || valDetailTagihan.filepath ? (
|
||||
<div className=''>
|
||||
<div className='border mb-3'>
|
||||
<div className='bg-abu3 px-2 py-1'>
|
||||
<p>File Sebelum</p>
|
||||
</div>
|
||||
<div className='flex items-center justify-center h-[40px] py-8'>
|
||||
<Link
|
||||
href={
|
||||
valDetailTagihan.file
|
||||
? process.env.NEXT_PUBLIC_API_FILE + valDetailTagihan.file
|
||||
: process.env.NEXT_PUBLIC_API_FILE + valDetailTagihan.filepath
|
||||
}
|
||||
>
|
||||
<a
|
||||
target={'_blank'}
|
||||
className='flex items-center justify-center gap-x-1'
|
||||
>
|
||||
<i className='pi pi-file-pdf' style={{ fontSize: '2em' }} />
|
||||
<p className='text-sm hover:underline'>
|
||||
Klik disini untuk lihat file
|
||||
</p>
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className=''>
|
||||
<FileUpload
|
||||
name='upload-syarat'
|
||||
url={'#'}
|
||||
accept='.pdf'
|
||||
// maxFileSize={1000000}
|
||||
emptyTemplate={
|
||||
<p className='m-0'>Drag and drop files to here to upload.</p>
|
||||
}
|
||||
customUpload
|
||||
uploadHandler={(e) => customBase64Uploader(e)}
|
||||
onClear={() =>
|
||||
setFieldValue(`detail_tagihan[${indexDetailTagihan}].file`, false)
|
||||
}
|
||||
onRemove={() =>
|
||||
setFieldValue(`detail_tagihan[${indexDetailTagihan}].file`, false)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<FileUpload
|
||||
name='upload-syarat'
|
||||
url={'#'}
|
||||
accept='.pdf'
|
||||
// maxFileSize={1000000}
|
||||
emptyTemplate={
|
||||
<p className='m-0'>Drag and drop files to here to upload.</p>
|
||||
}
|
||||
customUpload
|
||||
uploadHandler={(e) => customBase64Uploader(e)}
|
||||
onClear={() =>
|
||||
setFieldValue(`detail_tagihan[${indexDetailTagihan}].file`, false)
|
||||
}
|
||||
onRemove={() =>
|
||||
setFieldValue(`detail_tagihan[${indexDetailTagihan}].file`, false)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* AKUN */}
|
||||
<div className='p-fluid'>
|
||||
<div className='field col-6 md:col-4 my-3 w-1/2'>
|
||||
<LabelInput>Akun</LabelInput>
|
||||
<Dropdown
|
||||
panelClassName='p-dropdown-form dd-panel-custom'
|
||||
name={`detail_tagihan[${indexDetailTagihan}].akun_id`}
|
||||
options={optionAkun}
|
||||
optionLabel={(item) => `${item.akun_id} - ${item.nama}`}
|
||||
optionValue='akun_id'
|
||||
value={parseInt(values.detail_tagihan[indexDetailTagihan].akun_id)}
|
||||
onChange={handleChange}
|
||||
showClear={
|
||||
values.detail_tagihan[indexDetailTagihan] !== undefined ? true : false
|
||||
}
|
||||
scrollHeight='300px'
|
||||
placeholder='Select Akun'
|
||||
filter
|
||||
maxLength={100}
|
||||
/>
|
||||
{/* {errorFieldMessageDetail(indexDetailTagihan, 'akun')} */}
|
||||
</div>
|
||||
<div className='field col-12 md:col-4 my-3'>
|
||||
<LabelInput>Keperluan</LabelInput>
|
||||
<InputTextarea
|
||||
name={`detail_tagihan[${indexDetailTagihan}].keperluan`}
|
||||
rows={5}
|
||||
cols={30}
|
||||
value={
|
||||
values.detail_tagihan[indexDetailTagihan].keperluan
|
||||
? values.detail_tagihan[indexDetailTagihan].keperluan
|
||||
: ''
|
||||
}
|
||||
onChange={handleChange}
|
||||
placeholder='Masukkan detail keperluan'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-3 flex justify-center gap-x-3'>
|
||||
{values.detail_tagihan[indexDetailTagihan].status === true ? (
|
||||
<div className='flex flex-col'>
|
||||
<label className='p-2 bg-green-300 rounded shadow text-center'>
|
||||
<i className='pi pi-check'></i> Tagihan Berhasil dikirim/draft
|
||||
</label>
|
||||
<small className='text-red-400'>
|
||||
Tagihan ini hanya bisa didelete, tidak bisa diubah kembali!
|
||||
</small>
|
||||
</div>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Draft'
|
||||
icon='pi pi-save'
|
||||
onClick={(e) =>
|
||||
handleSubmitDetailTagihan(e, { draft: true, index: indexDetailTagihan })
|
||||
}
|
||||
className='p-button-sm p-button-info'
|
||||
loading={isSubmitting}
|
||||
/>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Simpan'
|
||||
icon='pi pi-send'
|
||||
onClick={(e) => {
|
||||
handleSubmitDetailTagihan(e, { draft: false, index: indexDetailTagihan })
|
||||
}}
|
||||
className='p-button-sm p-button-success'
|
||||
loading={isSubmitting}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</div>
|
||||
</AccordionTab>
|
||||
)
|
||||
})}
|
||||
</Accordion>
|
||||
<div className='flex justify-end gap-2 mt-3'>
|
||||
<Button
|
||||
label='Kirim'
|
||||
icon={`pi ${kirimLoading ? 'pi-spin pi-spinner' : 'pi-send'}`}
|
||||
className='p-button-sm p-button-info'
|
||||
onClick={handleBtnKirim}
|
||||
disabled={
|
||||
values.detail_tagihan[countJenisTagihan] !== undefined &&
|
||||
values.detail_tagihan[countJenisTagihan].status === true
|
||||
? false
|
||||
: true
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Tambah Tagihan'
|
||||
icon='pi pi-plus'
|
||||
className='p-button-sm p-button-primary'
|
||||
onClick={() => {
|
||||
setCountJenisTagihan(countJenisTagihan + 1)
|
||||
setFieldValue('dataIndex', countJenisTagihan + 1)
|
||||
push({
|
||||
detail_id: values.detail_tagihan[0].detail_id,
|
||||
jenis_belanja: '',
|
||||
jenis_kegiatan: '',
|
||||
syarat: [],
|
||||
})
|
||||
}}
|
||||
disabled={
|
||||
values.detail_tagihan[countJenisTagihan] !== undefined &&
|
||||
values.detail_tagihan[countJenisTagihan].status === true
|
||||
? false
|
||||
: true
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</FieldArray>
|
||||
</React.Fragment>
|
||||
)
|
||||
}}
|
||||
</Formik>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</Belakang>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
export const getServerSideProps = async (ctx) => {
|
||||
let token = ctx.req.cookies.token
|
||||
|
||||
// const optionJenisTagihan = await fetchAPI('jenis-tagihan/list', token)
|
||||
const optionJenisBelanja = await fetchAPI('jenis-belanja/list', token)
|
||||
|
||||
return {
|
||||
props: {
|
||||
// optionJenisTagihan,
|
||||
optionJenisBelanja,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,710 @@
|
||||
import { Belakang } from '@/components/Layouts'
|
||||
import { Judul, LabelInput } from '@/components/TextCustom'
|
||||
import { DetailSPPGet, NoSPPListGet } from '@/services/pengajuan-tagihan/integrasi-service'
|
||||
import { TagihanAddDetail, TagihanGetOne, TagihanSubmit } from '@/services/pengajuan-tagihan/tagihan-service'
|
||||
import { AkunList } from '@/services/referensi/akun-service'
|
||||
import { JenisKegiatanList } from '@/services/referensi/jenisKegiatan-service'
|
||||
import { PersyaratanSearch } from '@/services/referensi/persyaratan-service'
|
||||
import { Formik } from 'formik'
|
||||
import { fetchAPI } from 'lib/fetch'
|
||||
import Head from 'next/head'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Accordion, AccordionTab } from 'primereact/accordion'
|
||||
import { BlockUI } from 'primereact/blockui'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Card } from 'primereact/card'
|
||||
import { Dropdown } from 'primereact/dropdown'
|
||||
import { FileUpload } from 'primereact/fileupload'
|
||||
import { InputText } from 'primereact/inputtext'
|
||||
import { InputTextarea } from 'primereact/inputtextarea'
|
||||
import { Toast } from 'primereact/toast'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import Swal from 'sweetalert2'
|
||||
|
||||
export default function ForceClose({ optionJenisTagihan = null, optionJenisBelanja = null }) {
|
||||
const router = useRouter()
|
||||
const [data, setData] = useState([{ no_spp: '', tgl_spp: '', jenis_tagihan: '' }])
|
||||
const toast = useRef(null)
|
||||
const [countJenisTagihan, setCountJenisTagihan] = useState(0)
|
||||
const [optionJenisKegiatan, setOptionJenisKegiatan] = useState([])
|
||||
const [optionNoSpp, setOptionNoSpp] = useState([])
|
||||
const [optionAkun, setOptionAkun] = useState([])
|
||||
const [statusDraft, setStatusDraft] = useState(false)
|
||||
const [kirimLoading, setKirimLoading] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
TagihanGetOne({ ref_id: router.query.force }).then((res) => {
|
||||
if (res.status === 'ok') {
|
||||
setData(res.data)
|
||||
DetailSPPGet({ no_spp: res.data[0].no_spp }).then((res) => {
|
||||
if (res.data.akun.length > 0) {
|
||||
setOptionAkun(res.data.akun)
|
||||
} else {
|
||||
let params = {}
|
||||
params.draw = 0
|
||||
AkunList(params).then((res) => {
|
||||
setOptionAkun(res.data)
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
console.log(res.message)
|
||||
}
|
||||
})
|
||||
|
||||
NoSPPListGet()
|
||||
.then((res) => setOptionNoSpp(res.data))
|
||||
.catch((err) => console.log(err))
|
||||
}, [router])
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Toast ref={toast} />
|
||||
<Head>
|
||||
<title>Pengajuan Tagihan Ulang</title>
|
||||
</Head>
|
||||
<Belakang>
|
||||
<Judul>Form Pengajuan Tagihan</Judul>
|
||||
<Card>
|
||||
<Formik
|
||||
initialValues={{}}
|
||||
onSubmit={() => {
|
||||
setCountJenisTagihan(countJenisTagihan + 1)
|
||||
}}
|
||||
>
|
||||
{({ handleSubmit, setFieldValue }) => {
|
||||
const handleBtnKirim = () => {
|
||||
Swal.fire({
|
||||
title: 'Apakah data diatas sudah benar semua?',
|
||||
text: 'Tagihan yang sudah dikirim tidak bisa diubah kembali',
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#3085d6',
|
||||
cancelButtonColor: '#d33',
|
||||
confirmButtonText: 'Ya, kirim!',
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
setKirimLoading(true)
|
||||
TagihanSubmit({ ref_id: data[0].tagihan_id })
|
||||
.then((res) => {
|
||||
if (res.status == 'ok') {
|
||||
setKirimLoading(false)
|
||||
Swal.fire({
|
||||
title: 'Success',
|
||||
text: 'Tagihan berhasil dikirim',
|
||||
icon: 'success',
|
||||
timer: 2000,
|
||||
timerProgressBar: true,
|
||||
}).then(() => {
|
||||
router.push('/pengajuan-tagihan')
|
||||
})
|
||||
} else {
|
||||
setKirimLoading(false)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
setKirimLoading(false)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleTambahTagihan = (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
Swal.fire({
|
||||
title: 'Apakah data yang diinputkan sudah benar benar?',
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#3085d6',
|
||||
cancelButtonColor: '#d33',
|
||||
confirmButtonText: 'Ya, benar!',
|
||||
cancelButtonText: 'Tidak',
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
Swal.fire({
|
||||
title: 'Pastikan kembali!',
|
||||
text: 'Kamu tidak bisa pakai data ini kembali!',
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#3085d6',
|
||||
cancelButtonColor: '#d33',
|
||||
confirmButtonText: 'Ya, benar!',
|
||||
cancelButtonText: 'Tidak',
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
setFieldValue('dataIndex', countJenisTagihan)
|
||||
handleSubmit()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className='p-fluid grid grid-cols-2 gap-3'>
|
||||
<div className='field col-12 md:col-4'>
|
||||
<LabelInput>No SPP</LabelInput>
|
||||
<InputText disabled value={data[0].no_spp} />
|
||||
</div>
|
||||
<div className='field col-12 md:col-4'>
|
||||
<LabelInput>Tanggal SPP</LabelInput>
|
||||
<InputText disabled value={data[0].tgl_spp} />
|
||||
</div>
|
||||
<div className='field col-12 md:col-4'>
|
||||
<LabelInput>Jenis Tagihan</LabelInput>
|
||||
<Dropdown
|
||||
panelClassName='p-dropdown-form'
|
||||
name='jenis_tagihan'
|
||||
options={optionJenisTagihan}
|
||||
optionLabel='nama'
|
||||
optionValue='jenis_id'
|
||||
value={data[0].jenis_tagihan_id}
|
||||
placeholder='Select Jenis Tagihan'
|
||||
disabled={true}
|
||||
/>
|
||||
</div>
|
||||
<div className='field col-12 md:col-4'>
|
||||
<LabelInput>Jenis SPP</LabelInput>
|
||||
<InputText disabled value='' />
|
||||
</div>
|
||||
<div className='field col-12 md:col-4'>
|
||||
<LabelInput>Uraian</LabelInput>
|
||||
<InputTextarea rows={5} cols={30} value='' disabled />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* DETAIL TAGIHAN */}
|
||||
<Formik
|
||||
initialValues={{
|
||||
tagihan_id: '',
|
||||
detail_tagihan: [],
|
||||
dataIndex: 0, //* For select data index
|
||||
}}
|
||||
onSubmit={(data_detail_tagihan) => {
|
||||
/**
|
||||
* *Filter data detail tagihan follow the button kirim
|
||||
*/
|
||||
const filterData = data_detail_tagihan.detail_tagihan.find(
|
||||
(item, i) => i === data_detail_tagihan.dataIndex
|
||||
)
|
||||
|
||||
/**
|
||||
* *Create new format data detail tagihan reference to API
|
||||
*/
|
||||
const dataKirim = {
|
||||
tagihan_id: data[0].tagihan_id,
|
||||
detail_tagihan: [filterData],
|
||||
}
|
||||
|
||||
TagihanAddDetail(dataKirim)
|
||||
.then((res) => {
|
||||
if (res.status === 'ok') {
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
if (statusDraft) {
|
||||
router.push('/pengajuan-tagihan')
|
||||
}
|
||||
/**
|
||||
* *Add status to detail tagihan object for check if it is sended or not
|
||||
*/
|
||||
setFieldValue('detail_tagihan[' + data.dataIndex + '].status', true)
|
||||
} else {
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
/**
|
||||
* *Add status to detail tagihan object for check if it is sended or not
|
||||
*/
|
||||
setFieldValue('detail_tagihan[' + data.dataIndex + '].status', false)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: 'Error saat pembuatan data/file belum diupload',
|
||||
closable: false,
|
||||
})
|
||||
/**
|
||||
* *Add status to detail tagihan object for check if it is sended or not
|
||||
*/
|
||||
setFieldValue('detail_tagihan[' + data.dataIndex + '].status', false)
|
||||
})
|
||||
}}
|
||||
validate={(data) => {
|
||||
let errorsArray = []
|
||||
let errors = {}
|
||||
|
||||
if (!data.detail_tagihan[data.dataIndex]) {
|
||||
errors.jenis_belanja = 'Jenis Belanja is required.'
|
||||
errors.jenis_kegiatan = 'Jenis Kegiatan is required.'
|
||||
errors.akun = 'Akun is required.'
|
||||
}
|
||||
if (
|
||||
data.detail_tagihan[data.dataIndex] &&
|
||||
data.detail_tagihan[data.dataIndex].jenis_belanja === ''
|
||||
) {
|
||||
errors.jenis_belanja = 'Jenis Belanja is required dong.'
|
||||
}
|
||||
if (data.detail_tagihan[data.dataIndex] && !data.detail_tagihan[data.dataIndex].jenis_kegiatan) {
|
||||
errors.jenis_kegiatan = 'Jenis Kegiatan is required.'
|
||||
}
|
||||
if (data.detail_tagihan[data.dataIndex] && !data.detail_tagihan[data.dataIndex].akun_id) {
|
||||
errors.akun = 'Akun is required.'
|
||||
}
|
||||
|
||||
if (Object.keys(errors).length > 0) {
|
||||
errorsArray.push({ dataIndex: countJenisTagihan - 1, errors })
|
||||
}
|
||||
|
||||
return errorsArray
|
||||
}}
|
||||
validateOnChange={false}
|
||||
>
|
||||
{({ values, handleChange, handleSubmit, setFieldValue, setValues, errors }) => {
|
||||
/**
|
||||
* *Message error for formik detail tagihan
|
||||
*/
|
||||
const isFieldValidDetail = (index, field) =>
|
||||
errors[0] && errors[0].dataIndex === index && errors[0].errors[field]
|
||||
const errorFieldMessageDetail = (index, field) => {
|
||||
return (
|
||||
isFieldValidDetail(index, field) && (
|
||||
<small className='p-error' style={{ fontSize: '11px' }}>
|
||||
{errors[0].errors[field]}
|
||||
</small>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* *Dropdown list jenis kegiatan
|
||||
* *When jenis kegiatan change hit API to get list syarat
|
||||
*/
|
||||
const changeJenisKegiatan = (event, index) => {
|
||||
setFieldValue(`detail_tagihan[${index}].syarat`, [])
|
||||
setFieldValue(`detail_tagihan[${index}].jenis_kegiatan`, event.value)
|
||||
PersyaratanSearch({
|
||||
jenis_tagihan_id: data[0].jenis_tagihan_id,
|
||||
jenis_belanja_id: values.detail_tagihan[index].jenis_belanja,
|
||||
jenis_kegiatan_id: event.value,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.data) {
|
||||
res.data.map((item, index2) => {
|
||||
setFieldValue(`detail_tagihan[${index}].syarat[${index2}].syarat_id`, item.syarat_id)
|
||||
setFieldValue(
|
||||
`detail_tagihan[${index}].syarat[${index2}].syarat_dokumen`,
|
||||
item.syarat_dokumen
|
||||
)
|
||||
setFieldValue(`detail_tagihan[${index}].syarat[${index2}].keterangan`, item.keterangan)
|
||||
})
|
||||
} else {
|
||||
setFieldValue(`detail_tagihan[${index}].syarat`, [])
|
||||
toast.current.show({
|
||||
severity: 'info',
|
||||
detail: 'Data syarat kosong',
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: 'Error while get data',
|
||||
closable: false,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* *Change Jenis Belanja
|
||||
* *Reset form detail tagihan
|
||||
*/
|
||||
const changeJenisBelanja = (event, index) => {
|
||||
setFieldValue(`detail_tagihan[${index}].syarat`, [])
|
||||
setFieldValue(`detail_tagihan[${index}].jenis_belanja`, event.value)
|
||||
|
||||
/**
|
||||
* SHOW LIST JENIS KEGIATAN WHERE JENIS_BELANJA_ID
|
||||
*/
|
||||
JenisKegiatanList({ jenis_belanja_id: event.value, draw: 0 })
|
||||
.then((res) => setOptionJenisKegiatan(res.data))
|
||||
.catch((err) => console.log(err))
|
||||
}
|
||||
|
||||
const headerTemplate = (index) => {
|
||||
const handleDelete = (index) => {
|
||||
setValues({
|
||||
values,
|
||||
detail_tagihan: values.detail_tagihan.filter((item, i) => i !== index),
|
||||
})
|
||||
setCountJenisTagihan(countJenisTagihan - 1)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='flex justify-between items-center w-full'>
|
||||
<p>Tagihan {index + 1}</p>
|
||||
<Button
|
||||
type='button'
|
||||
icon='pi pi-times'
|
||||
className='p-button-rounded p-button-danger p-button-text button-close-tagihan'
|
||||
aria-label='Cancel'
|
||||
onClick={() => handleDelete(index)}
|
||||
title='Delete tagihan'
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* *Form Detail Tagihan
|
||||
* *Button Submit and draft
|
||||
*/
|
||||
const handleSubmitDetailTagihan = (e, { draft, index }) => {
|
||||
e.preventDefault()
|
||||
setStatusDraft(draft)
|
||||
setFieldValue(`detail_tagihan[${index}].draft`, draft)
|
||||
setFieldValue(`detail_tagihan[${index}].status`, true)
|
||||
handleSubmit()
|
||||
}
|
||||
|
||||
const tagihanList = []
|
||||
const tagihan = (index) => {
|
||||
const customBase64Uploader = async (event) => {
|
||||
// convert file to base64 encoded
|
||||
const file = event.files[0]
|
||||
if (file) {
|
||||
if (file.type === 'application/pdf') {
|
||||
const reader = new FileReader()
|
||||
reader.readAsDataURL(file)
|
||||
|
||||
reader.onloadend = function () {
|
||||
const base64data = reader.result
|
||||
setFieldValue(`detail_tagihan[${index}].file`, base64data)
|
||||
setFieldValue(`detail_tagihan[${index}].file_name`, file.name)
|
||||
}
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: 'File berhasil diupload',
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
event.options.clear()
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: 'Invalid format file',
|
||||
closable: false,
|
||||
})
|
||||
setFieldValue(`detail_tagihan[${index}].file`, null)
|
||||
setFieldValue(`detail_tagihan[${index}].file_name`, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<AccordionTab
|
||||
key={index}
|
||||
headerTemplate={headerTemplate(index)}
|
||||
className={`accordion-tab-${index}`}
|
||||
>
|
||||
<BlockUI
|
||||
blocked={
|
||||
values.detail_tagihan[index] !== undefined && values.detail_tagihan[index].status
|
||||
? true
|
||||
: false
|
||||
}
|
||||
className='block-ui-custom cursor-not-allowed'
|
||||
>
|
||||
<div className='p-fluid grid grid-cols-2 gap-3'>
|
||||
{/* JENIS BELANJA */}
|
||||
<div className='field col-12 md:col-4'>
|
||||
<LabelInput>Jenis Belanja</LabelInput>
|
||||
<Dropdown
|
||||
panelClassName='p-dropdown-form'
|
||||
name={`detail_tagihan[${index}].jenis_belanja`}
|
||||
options={optionJenisBelanja}
|
||||
optionLabel='nama'
|
||||
optionValue='jenis_id'
|
||||
value={
|
||||
values.detail_tagihan[index] !== undefined
|
||||
? values.detail_tagihan[index].jenis_belanja
|
||||
: setFieldValue(`detail_tagihan[${index}].jenis_belanja`, '')
|
||||
}
|
||||
onChange={(e) => changeJenisBelanja(e, index)}
|
||||
showClear={values.detail_tagihan[index] !== undefined ? true : false}
|
||||
scrollHeight='200px'
|
||||
placeholder='Select Jenis Belanja'
|
||||
className={`${errorFieldMessageDetail(index, 'jenis_belanja') && 'p-invalid'}`}
|
||||
/>
|
||||
{errorFieldMessageDetail(index, 'jenis_belanja')}
|
||||
</div>
|
||||
{/* JENIS KEGIATAN */}
|
||||
<div className='field col-12 md:col-4'>
|
||||
<LabelInput>Jenis Kegiatan</LabelInput>
|
||||
<Dropdown
|
||||
panelClassName='p-dropdown-form'
|
||||
name={`detail_tagihan[${index}].jenis_kegiatan`}
|
||||
options={optionJenisKegiatan}
|
||||
optionLabel='nama'
|
||||
optionValue='jenis_id'
|
||||
value={
|
||||
values.detail_tagihan[index] !== undefined
|
||||
? values.detail_tagihan[index].jenis_kegiatan
|
||||
: setFieldValue(`detail_tagihan[${index}].jenis_kegiatan`, '')
|
||||
}
|
||||
onChange={(e) => changeJenisKegiatan(e, index)}
|
||||
showClear={values.detail_tagihan[index] !== undefined ? true : false}
|
||||
scrollHeight='200px'
|
||||
placeholder='Select Jenis Kegiatan'
|
||||
filter
|
||||
className={`${errorFieldMessageDetail(index, 'jenis_kegiatan') && 'p-invalid'}`}
|
||||
/>
|
||||
{errorFieldMessageDetail(index, 'jenis_kegiatan')}
|
||||
</div>
|
||||
</div>
|
||||
{/* SYARAT */}
|
||||
<div className='p-fluid grid grid-cols-2 gap-3'>
|
||||
<div className='md:col-4 my-3'>
|
||||
<LabelInput>Syarat</LabelInput>
|
||||
<div className='relative mt-2 flex overflow-auto h-96'>
|
||||
<table className='w-full text-sm text-left '>
|
||||
<thead className='text-xs uppercase bg-gray-100 sticky top-0'>
|
||||
<tr>
|
||||
<th scope='col' className='py-3 px-6'>
|
||||
Persyaratan
|
||||
</th>
|
||||
<th scope='col' className='py-3 px-6'>
|
||||
Keterangan
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{values.detail_tagihan[index] &&
|
||||
values.detail_tagihan[index].syarat &&
|
||||
values.detail_tagihan[index].syarat.map((item, index2) => (
|
||||
<tr className='bg-white border-b' key={index2}>
|
||||
<td scope='row' className='py-4 px-6'>
|
||||
{item.syarat_dokumen}
|
||||
</td>
|
||||
<td scope='row' className='py-4 px-6'>
|
||||
{item.keterangan}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div className='upload-all my-3'>
|
||||
<LabelInput>Upload Syarat</LabelInput>
|
||||
<div className='py-4 px-6'>
|
||||
<FileUpload
|
||||
name='upload-syarat'
|
||||
url={'#'}
|
||||
accept='.pdf'
|
||||
// maxFileSize={1000000}
|
||||
emptyTemplate={<p className='m-0'>Drag and drop files to here to upload.</p>}
|
||||
customUpload
|
||||
uploadHandler={(e) => customBase64Uploader(e)}
|
||||
onClear={() => setFieldValue(`detail_tagihan[${index}].file`, false)}
|
||||
onRemove={() => setFieldValue(`detail_tagihan[${index}].file`, false)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* AKUN */}
|
||||
<div className='p-fluid'>
|
||||
{optionNoSpp.length > 0 ? (
|
||||
<div className='field col-6 md:col-4 my-3 w-1/2'>
|
||||
<LabelInput>Akun</LabelInput>
|
||||
<Dropdown
|
||||
panelClassName='p-dropdown-form dd-panel-custom'
|
||||
name={`detail_tagihan[${index}].akun_id`}
|
||||
options={optionAkun}
|
||||
value={
|
||||
values.detail_tagihan[index] !== undefined
|
||||
? values.detail_tagihan[index].akun_id
|
||||
: setFieldValue(`detail_tagihan[${index}].akun_id`, '')
|
||||
}
|
||||
onChange={handleChange}
|
||||
showClear={values.detail_tagihan[index] !== undefined ? true : false}
|
||||
scrollHeight='300px'
|
||||
placeholder='Select Akun'
|
||||
filter
|
||||
maxLength={100}
|
||||
className={`${errorFieldMessageDetail(index, 'akun') && 'p-invalid'}`}
|
||||
/>
|
||||
{errorFieldMessageDetail(index, 'akun')}
|
||||
</div>
|
||||
) : (
|
||||
<div className='field col-6 md:col-4 my-3 w-1/2'>
|
||||
<LabelInput>Akun</LabelInput>
|
||||
<Dropdown
|
||||
panelClassName='p-dropdown-form dd-panel-custom'
|
||||
name={`detail_tagihan[${index}].akun_id`}
|
||||
options={optionAkun}
|
||||
optionLabel={(item) => `${item.akun_id} - ${item.nama}`}
|
||||
optionValue='akun_id'
|
||||
value={
|
||||
values.detail_tagihan[index] !== undefined
|
||||
? values.detail_tagihan[index].akun_id
|
||||
: setFieldValue(`detail_tagihan[${index}].akun_id`, '')
|
||||
}
|
||||
onChange={handleChange}
|
||||
showClear={values.detail_tagihan[index] !== undefined ? true : false}
|
||||
scrollHeight='300px'
|
||||
placeholder='Select Akun'
|
||||
filter
|
||||
maxLength={100}
|
||||
className={`${errorFieldMessageDetail(index, 'akun') && 'p-invalid'}`}
|
||||
/>
|
||||
{errorFieldMessageDetail(index, 'akun')}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className='field col-12 md:col-4 my-3'>
|
||||
<LabelInput>Keperluan</LabelInput>
|
||||
<InputTextarea
|
||||
name={`detail_tagihan[${index}].keperluan`}
|
||||
rows={5}
|
||||
cols={30}
|
||||
value={
|
||||
values.detail_tagihan[index] !== undefined
|
||||
? values.detail_tagihan[index].keperluan
|
||||
: ''
|
||||
}
|
||||
onChange={handleChange}
|
||||
placeholder='Masukkan detail keperluan'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</BlockUI>
|
||||
|
||||
{/* BUTTON DRAFT, SIMPAN AND MESSAGE */}
|
||||
<div className='flex justify-center gap-2 mt-3'>
|
||||
{values.detail_tagihan[index] !== undefined &&
|
||||
values.detail_tagihan[index].status === true ? (
|
||||
<div className='flex flex-col'>
|
||||
<label className='p-2 bg-green-300 rounded shadow text-center'>
|
||||
<i className='pi pi-check'></i> Tagihan Berhasil dikirim/draft
|
||||
</label>
|
||||
<small className='text-red-400'>
|
||||
Tagihan ini hanya bisa didelete, tidak bisa diubah kembali!
|
||||
</small>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Draft'
|
||||
icon='pi pi-save'
|
||||
className='p-button-sm p-button-info'
|
||||
onClick={(e) => handleSubmitDetailTagihan(e, { draft: true, index })}
|
||||
/>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Simpan'
|
||||
icon='pi pi-send'
|
||||
className='p-button-sm p-button-success'
|
||||
onClick={(e) => handleSubmitDetailTagihan(e, { draft: false, index })}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</AccordionTab>
|
||||
)
|
||||
}
|
||||
for (let index = 0; index < countJenisTagihan; index++) {
|
||||
tagihanList.push(tagihan(index))
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Accordion activeIndex={0} className='mt-5 accordion-custom'>
|
||||
{tagihanList}
|
||||
</Accordion>
|
||||
</form>
|
||||
{countJenisTagihan === 0 ? (
|
||||
<div className='flex justify-end gap-2 mt-3'>
|
||||
<Button
|
||||
label='Kirim'
|
||||
icon={`pi ${kirimLoading ? 'pi-spin pi-spinner' : 'pi-send'}`}
|
||||
className='p-button-sm p-button-info'
|
||||
onClick={handleBtnKirim}
|
||||
disabled={true}
|
||||
/>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Tambah Tagihan'
|
||||
icon='pi pi-plus'
|
||||
className='p-button-sm p-button-primary'
|
||||
onClick={handleTambahTagihan}
|
||||
disabled={false}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className='flex justify-end gap-2 mt-3'>
|
||||
<Button
|
||||
label='Kirim'
|
||||
icon={`pi ${kirimLoading ? 'pi-spin pi-spinner' : 'pi-send'}`}
|
||||
className='p-button-sm p-button-info'
|
||||
onClick={handleBtnKirim}
|
||||
disabled={
|
||||
values.detail_tagihan[countJenisTagihan - 1] !== undefined &&
|
||||
values.detail_tagihan[countJenisTagihan - 1].status === true
|
||||
? false
|
||||
: true
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Tambah Tagihan'
|
||||
icon='pi pi-plus'
|
||||
className='p-button-sm p-button-primary'
|
||||
onClick={handleTambahTagihan}
|
||||
disabled={
|
||||
values.detail_tagihan[countJenisTagihan - 1] !== undefined &&
|
||||
values.detail_tagihan[countJenisTagihan - 1].status === true
|
||||
? false
|
||||
: true
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)
|
||||
}}
|
||||
</Formik>
|
||||
</React.Fragment>
|
||||
)
|
||||
}}
|
||||
</Formik>
|
||||
</Card>
|
||||
</Belakang>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
export const getServerSideProps = async (ctx) => {
|
||||
let token = ctx.req.cookies.token
|
||||
|
||||
const optionJenisTagihan = await fetchAPI('jenis-tagihan/list', token)
|
||||
const optionJenisBelanja = await fetchAPI('jenis-belanja/list', token)
|
||||
|
||||
return {
|
||||
props: {
|
||||
optionJenisTagihan,
|
||||
optionJenisBelanja,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,863 @@
|
||||
import { Belakang } from '@/components/Layouts'
|
||||
import { Judul, LabelInput } from '@/components/TextCustom'
|
||||
import { DetailSPPGet, NoSPPListGet } from '@/services/pengajuan-tagihan/integrasi-service'
|
||||
import { TagihanAddDetail, TagihanCreate, TagihanSubmit } from '@/services/pengajuan-tagihan/tagihan-service'
|
||||
import { AkunList } from '@/services/referensi/akun-service'
|
||||
import { JenisKegiatanList } from '@/services/referensi/jenisKegiatan-service'
|
||||
import { PersyaratanSearch } from '@/services/referensi/persyaratan-service'
|
||||
import { useFormik } from 'formik'
|
||||
import { fetchAPI } from 'lib/fetch'
|
||||
import 'moment/locale/id'
|
||||
import Head from 'next/head'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Accordion, AccordionTab } from 'primereact/accordion'
|
||||
import { BlockUI } from 'primereact/blockui'
|
||||
import { BreadCrumb } from 'primereact/breadcrumb'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Calendar } from 'primereact/calendar'
|
||||
import { Card } from 'primereact/card'
|
||||
import { Checkbox } from 'primereact/checkbox'
|
||||
import { Chips } from 'primereact/chips'
|
||||
import { Dropdown } from 'primereact/dropdown'
|
||||
import { FileUpload } from 'primereact/fileupload'
|
||||
import { InputText } from 'primereact/inputtext'
|
||||
import { InputTextarea } from 'primereact/inputtextarea'
|
||||
import { Toast } from 'primereact/toast'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import Swal from 'sweetalert2'
|
||||
|
||||
export default function FormPengajuanTagihan({ optionJenisTagihan = null, optionJenisBelanja = null }) {
|
||||
const toast = useRef(null)
|
||||
const router = useRouter()
|
||||
const [countJenisTagihan, setCountJenisTagihan] = useState(0)
|
||||
const [optionAkun, setOptionAkun] = useState([])
|
||||
const [tagihanID, setTagihanID] = useState('')
|
||||
const [kirimLoading, setKirimLoading] = useState(false)
|
||||
const [optionJenisKegiatan, setOptionJenisKegiatan] = useState([])
|
||||
const [statusDraft, setStatusDraft] = useState(false)
|
||||
const [optionNoSpp, setOptionNoSpp] = useState([])
|
||||
const [jenisSPP, setJenisSPP] = useState('')
|
||||
const [uraianSPP, setUraianSPP] = useState('')
|
||||
const [itsGU, setItsGU] = useState(false)
|
||||
const [checkedGUGabungan, setCheckedGUGabungan] = useState(false)
|
||||
|
||||
const formik = useFormik({
|
||||
initialValues: {
|
||||
no_spp: '',
|
||||
tgl_spp: '',
|
||||
jenis_tagihan: '',
|
||||
kode_jenis_spp: '',
|
||||
spp_gabungan: [],
|
||||
},
|
||||
validate: (data) => {
|
||||
let errors = {}
|
||||
if (!data.no_spp) errors.no_spp = 'Nomor SPP is required.'
|
||||
if (!data.tgl_spp) errors.tgl_spp = 'Tanggal SPP is required.'
|
||||
if (!data.jenis_tagihan) errors.jenis_tagihan = 'Jenis Tagihan is required.'
|
||||
return errors
|
||||
},
|
||||
onSubmit: (data) => {
|
||||
const tanggalAsli = data.tgl_spp
|
||||
|
||||
/**
|
||||
* Check tagihan if > 0 just add tagihan
|
||||
* else request tagihan id
|
||||
*/
|
||||
if (countJenisTagihan > 0) {
|
||||
setCountJenisTagihan(countJenisTagihan + 1)
|
||||
} else {
|
||||
TagihanCreate(data)
|
||||
.then((res) => {
|
||||
if (res.status === 'success') {
|
||||
Object.assign(data, { tgl_spp: tanggalAsli })
|
||||
setCountJenisTagihan(countJenisTagihan + 1)
|
||||
formikDetailTagihan.setFieldValue('tagihan_id', res.data.tagihan_id)
|
||||
setTagihanID(res.data.tagihan_id)
|
||||
} else {
|
||||
Object.assign(data, { tgl_spp: tanggalAsli })
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: 'Error while creating data',
|
||||
closable: false,
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
// NOMER SPP
|
||||
useEffect(() => {
|
||||
NoSPPListGet()
|
||||
.then((res) => setOptionNoSpp(res.data))
|
||||
.catch((err) => console.log(err))
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
DetailSPPGet({ no_spp: formik.values.no_spp }).then((res) => {
|
||||
if (res.data.akun.length > 0) {
|
||||
setJenisSPP(res.data.jenis_spp)
|
||||
setUraianSPP(res.data.uraian)
|
||||
formik.setFieldValue('tgl_spp', new Date(res.data.tanggal_spp))
|
||||
setOptionAkun(res.data.akun)
|
||||
} else {
|
||||
let params = {}
|
||||
params.draw = 0
|
||||
setJenisSPP('')
|
||||
setUraianSPP('')
|
||||
formik.setFieldValue('tgl_spp', new Date())
|
||||
AkunList(params).then((res) => {
|
||||
setOptionAkun(res.data)
|
||||
})
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
const formikDetailTagihan = useFormik({
|
||||
initialValues: {
|
||||
tagihan_id: '',
|
||||
detail_tagihan: [],
|
||||
dataIndex: 0, //* For select data index
|
||||
},
|
||||
validate: (data) => {
|
||||
let errorsArray = []
|
||||
let errors = {}
|
||||
|
||||
if (!data.detail_tagihan[data.dataIndex]) {
|
||||
errors.jenis_belanja = 'Jenis Belanja is required.'
|
||||
errors.jenis_kegiatan = 'Jenis Kegiatan is required.'
|
||||
errors.akun = 'Akun is required.'
|
||||
}
|
||||
if (data.detail_tagihan[data.dataIndex] && data.detail_tagihan[data.dataIndex].jenis_belanja === '') {
|
||||
errors.jenis_belanja = 'Jenis Belanja is required dong.'
|
||||
}
|
||||
if (data.detail_tagihan[data.dataIndex] && !data.detail_tagihan[data.dataIndex].jenis_kegiatan) {
|
||||
errors.jenis_kegiatan = 'Jenis Kegiatan is required.'
|
||||
}
|
||||
if (data.detail_tagihan[data.dataIndex] && !data.detail_tagihan[data.dataIndex].akun_id) {
|
||||
errors.akun = 'Akun is required.'
|
||||
}
|
||||
|
||||
if (Object.keys(errors).length > 0) {
|
||||
errorsArray.push({ dataIndex: countJenisTagihan - 1, errors })
|
||||
}
|
||||
|
||||
return errorsArray
|
||||
},
|
||||
onSubmit: (data) => {
|
||||
formikDetailTagihan.setFieldValue('detail_tagihan[' + data.dataIndex + '].loading', true)
|
||||
|
||||
/**
|
||||
* *Filter data detail tagihan follow the button kirim
|
||||
*/
|
||||
const filterData = data.detail_tagihan.find((item, i) => i === data.dataIndex)
|
||||
|
||||
/**
|
||||
* *Create new format data detail tagihan reference to API
|
||||
*/
|
||||
const dataKirim = {
|
||||
tagihan_id: data.tagihan_id,
|
||||
detail_tagihan: [filterData],
|
||||
}
|
||||
|
||||
TagihanAddDetail(dataKirim)
|
||||
.then((res) => {
|
||||
if (res.status === 'ok') {
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
if (statusDraft) {
|
||||
router.push('/pengajuan-tagihan')
|
||||
}
|
||||
/**
|
||||
* *Add status to detail tagihan object for check if it is sended or not
|
||||
*/
|
||||
formikDetailTagihan.setFieldValue('detail_tagihan[' + data.dataIndex + '].status', true)
|
||||
formikDetailTagihan.setFieldValue('detail_tagihan[' + data.dataIndex + '].loading', false)
|
||||
} else {
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
/**
|
||||
* *Add status to detail tagihan object for check if it is sended or not
|
||||
*/
|
||||
formikDetailTagihan.setFieldValue('detail_tagihan[' + data.dataIndex + '].status', false)
|
||||
formikDetailTagihan.setFieldValue('detail_tagihan[' + data.dataIndex + '].loading', false)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: 'Error saat pembuatan data/file belum diupload',
|
||||
closable: false,
|
||||
})
|
||||
/**
|
||||
* *Add status to detail tagihan object for check if it is sended or not
|
||||
*/
|
||||
formikDetailTagihan.setFieldValue('detail_tagihan[' + data.dataIndex + '].status', false)
|
||||
formikDetailTagihan.setFieldValue('detail_tagihan[' + data.dataIndex + '].loading', false)
|
||||
})
|
||||
},
|
||||
validateOnChange: false,
|
||||
})
|
||||
|
||||
/**
|
||||
* *Message error for formik
|
||||
*/
|
||||
const isFieldValid = (field) => !!(formik.touched[field] && formik.errors[field])
|
||||
const errorFieldMessage = (field) => {
|
||||
return (
|
||||
isFieldValid(field) && (
|
||||
<small className='p-error' style={{ fontSize: '11px' }}>
|
||||
{formik.errors[field]}
|
||||
</small>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* *Message error for formik detail tagihan
|
||||
*/
|
||||
const isFieldValidDetail = (index, field) =>
|
||||
formikDetailTagihan.errors[0] &&
|
||||
formikDetailTagihan.errors[0].dataIndex === index &&
|
||||
formikDetailTagihan.errors[0].errors[field]
|
||||
const errorFieldMessageDetail = (index, field) => {
|
||||
return (
|
||||
isFieldValidDetail(index, field) && (
|
||||
<small className='p-error' style={{ fontSize: '11px' }}>
|
||||
{formikDetailTagihan.errors[0].errors[field]}
|
||||
</small>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* *Card Detail Tagihan
|
||||
*/
|
||||
const headerTemplate = (index) => {
|
||||
const handleDelete = (index) => {
|
||||
formikDetailTagihan.setValues({
|
||||
...formikDetailTagihan.values,
|
||||
detail_tagihan: formikDetailTagihan.values.detail_tagihan.filter((item, i) => i !== index),
|
||||
})
|
||||
setCountJenisTagihan(countJenisTagihan - 1)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='flex justify-between items-center w-full'>
|
||||
<p>Tagihan {index + 1}</p>
|
||||
<Button
|
||||
type='button'
|
||||
icon='pi pi-times'
|
||||
className='p-button-rounded p-button-danger p-button-text button-close-tagihan'
|
||||
aria-label='Cancel'
|
||||
onClick={() => handleDelete(index)}
|
||||
title='Delete tagihan'
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* *Dropdown list jenis kegiatan
|
||||
* *When jenis kegiatan change hit API to get list syarat
|
||||
*/
|
||||
const changeJenisKegiatan = (event, index) => {
|
||||
formikDetailTagihan.setFieldValue(`detail_tagihan[${index}].syarat`, [])
|
||||
formikDetailTagihan.setFieldValue(`detail_tagihan[${index}].jenis_kegiatan`, event.value)
|
||||
PersyaratanSearch({
|
||||
jenis_tagihan_id: formik.values.jenis_tagihan,
|
||||
jenis_belanja_id: formikDetailTagihan.values.detail_tagihan[index].jenis_belanja,
|
||||
jenis_kegiatan_id: event.value,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.data) {
|
||||
res.data.map((item, index2) => {
|
||||
formikDetailTagihan.setFieldValue(`detail_tagihan[${index}].syarat[${index2}].syarat_id`, item.syarat_id)
|
||||
formikDetailTagihan.setFieldValue(
|
||||
`detail_tagihan[${index}].syarat[${index2}].syarat_dokumen`,
|
||||
item.syarat_dokumen
|
||||
)
|
||||
formikDetailTagihan.setFieldValue(`detail_tagihan[${index}].syarat[${index2}].keterangan`, item.keterangan)
|
||||
})
|
||||
} else {
|
||||
formikDetailTagihan.setFieldValue(`detail_tagihan[${index}].syarat`, [])
|
||||
toast.current.show({
|
||||
severity: 'info',
|
||||
detail: 'Data syarat kosong',
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: 'Error while get data',
|
||||
closable: false,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* *Change Jenis Belanja
|
||||
* *Reset form detail tagihan
|
||||
*/
|
||||
const changeJenisBelanja = (event, index) => {
|
||||
formikDetailTagihan.setFieldValue(`detail_tagihan[${index}].syarat`, [])
|
||||
formikDetailTagihan.setFieldValue(`detail_tagihan[${index}].jenis_belanja`, event.value)
|
||||
|
||||
/**
|
||||
* SHOW LIST JENIS KEGIATAN WHERE JENIS_BELANJA_ID
|
||||
*/
|
||||
JenisKegiatanList({ jenis_belanja_id: event.value, draw: 0 })
|
||||
.then((res) => setOptionJenisKegiatan(res.data))
|
||||
.catch((err) => console.log(err))
|
||||
}
|
||||
|
||||
/**
|
||||
* *End of Dropdown list jenis kegiatan
|
||||
*/
|
||||
|
||||
/**
|
||||
* *Form Detail Tagihan
|
||||
* *Button Submit and draft
|
||||
*/
|
||||
const handleSubmitDetailTagihan = (e, { draft, index }) => {
|
||||
e.preventDefault()
|
||||
setStatusDraft(draft)
|
||||
formikDetailTagihan.setFieldValue(`detail_tagihan[${index}].draft`, draft)
|
||||
formikDetailTagihan.handleSubmit()
|
||||
}
|
||||
|
||||
const tagihanList = []
|
||||
const tagihan = (index) => {
|
||||
const customBase64Uploader = async (event) => {
|
||||
// convert file to base64 encoded
|
||||
const file = event.files[0]
|
||||
if (file) {
|
||||
if (file.type === 'application/pdf') {
|
||||
const reader = new FileReader()
|
||||
reader.readAsDataURL(file)
|
||||
|
||||
reader.onloadend = function () {
|
||||
const base64data = reader.result
|
||||
formikDetailTagihan.setFieldValue(`detail_tagihan[${index}].file`, base64data)
|
||||
formikDetailTagihan.setFieldValue(`detail_tagihan[${index}].file_name`, file.name)
|
||||
}
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: 'File berhasil diupload',
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
event.options.clear()
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: 'Invalid format file',
|
||||
closable: false,
|
||||
})
|
||||
formikDetailTagihan.setFieldValue(`detail_tagihan[${index}].file`, null)
|
||||
formikDetailTagihan.setFieldValue(`detail_tagihan[${index}].file_name`, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<AccordionTab key={index} headerTemplate={headerTemplate(index)} className={`accordion-tab-${index}`}>
|
||||
<BlockUI
|
||||
blocked={
|
||||
formikDetailTagihan.values.detail_tagihan[index] !== undefined &&
|
||||
formikDetailTagihan.values.detail_tagihan[index].status
|
||||
? true
|
||||
: false
|
||||
}
|
||||
className='block-ui-custom cursor-not-allowed'
|
||||
>
|
||||
<div className='p-fluid grid grid-cols-2 gap-3'>
|
||||
{/* JENIS BELANJA */}
|
||||
<div className='field col-12 md:col-4'>
|
||||
<LabelInput>Jenis Belanja</LabelInput>
|
||||
<Dropdown
|
||||
panelClassName='p-dropdown-form'
|
||||
name={`detail_tagihan[${index}].jenis_belanja`}
|
||||
options={optionJenisBelanja}
|
||||
optionLabel='nama'
|
||||
optionValue='jenis_id'
|
||||
value={
|
||||
formikDetailTagihan.values.detail_tagihan[index] !== undefined
|
||||
? formikDetailTagihan.values.detail_tagihan[index].jenis_belanja
|
||||
: formikDetailTagihan.setFieldValue(`detail_tagihan[${index}].jenis_belanja`, '')
|
||||
}
|
||||
onChange={(e) => changeJenisBelanja(e, index)}
|
||||
showClear={formikDetailTagihan.values.detail_tagihan[index] !== undefined ? true : false}
|
||||
scrollHeight='200px'
|
||||
placeholder='Select Jenis Belanja'
|
||||
/>
|
||||
{errorFieldMessageDetail(index, 'jenis_belanja')}
|
||||
</div>
|
||||
{/* JENIS KEGIATAN */}
|
||||
<div className='field col-12 md:col-4'>
|
||||
<LabelInput>Jenis Kegiatan</LabelInput>
|
||||
<Dropdown
|
||||
panelClassName='p-dropdown-form'
|
||||
name={`detail_tagihan[${index}].jenis_kegiatan`}
|
||||
options={optionJenisKegiatan}
|
||||
optionLabel='nama'
|
||||
optionValue='jenis_id'
|
||||
value={
|
||||
formikDetailTagihan.values.detail_tagihan[index] !== undefined
|
||||
? formikDetailTagihan.values.detail_tagihan[index].jenis_kegiatan
|
||||
: formikDetailTagihan.setFieldValue(`detail_tagihan[${index}].jenis_kegiatan`, '')
|
||||
}
|
||||
onChange={(e) => changeJenisKegiatan(e, index)}
|
||||
showClear={formikDetailTagihan.values.detail_tagihan[index] !== undefined ? true : false}
|
||||
scrollHeight='200px'
|
||||
placeholder='Select Jenis Kegiatan'
|
||||
filter
|
||||
/>
|
||||
{errorFieldMessageDetail(index, 'jenis_kegiatan')}
|
||||
</div>
|
||||
</div>
|
||||
{/* SYARAT */}
|
||||
<div className='p-fluid grid grid-cols-2 gap-3'>
|
||||
<div className='md:col-4 my-3'>
|
||||
<LabelInput>Syarat</LabelInput>
|
||||
<div className='relative mt-2 flex overflow-auto h-96'>
|
||||
<table className='w-full text-sm text-left '>
|
||||
<thead className='text-xs uppercase bg-gray-100 sticky top-0'>
|
||||
<tr>
|
||||
<th scope='col' className='py-3 px-6'>
|
||||
Persyaratan
|
||||
</th>
|
||||
<th scope='col' className='py-3 px-6'>
|
||||
Keterangan
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{formikDetailTagihan.values.detail_tagihan[index] &&
|
||||
formikDetailTagihan.values.detail_tagihan[index].syarat &&
|
||||
formikDetailTagihan.values.detail_tagihan[index].syarat.map((item, index2) => (
|
||||
<tr className='bg-white border-b' key={index2}>
|
||||
<td scope='row' className='py-4 px-6'>
|
||||
{item.syarat_dokumen}
|
||||
</td>
|
||||
<td scope='row' className='py-4 px-6'>
|
||||
{item.keterangan}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div className='upload-all my-3'>
|
||||
<LabelInput>Upload Syarat</LabelInput>
|
||||
<div className='py-4 px-6'>
|
||||
<FileUpload
|
||||
name='upload-syarat'
|
||||
url={'#'}
|
||||
accept='.pdf'
|
||||
// maxFileSize={1000000}
|
||||
emptyTemplate={<p className='m-0'>Drag and drop files to here to upload.</p>}
|
||||
customUpload
|
||||
uploadHandler={(e) => customBase64Uploader(e)}
|
||||
onClear={() => formikDetailTagihan.setFieldValue(`detail_tagihan[${index}].file`, false)}
|
||||
onRemove={() => formikDetailTagihan.setFieldValue(`detail_tagihan[${index}].file`, false)}
|
||||
uploadOptions={{
|
||||
className:
|
||||
formikDetailTagihan.values.detail_tagihan[index] !== undefined &&
|
||||
'file' in formikDetailTagihan.values.detail_tagihan[index]
|
||||
? 'p-disabled'
|
||||
: '',
|
||||
}}
|
||||
cancelOptions={{
|
||||
className:
|
||||
formikDetailTagihan.values.detail_tagihan[index] !== undefined &&
|
||||
'file' in formikDetailTagihan.values.detail_tagihan[index]
|
||||
? 'p-disabled'
|
||||
: '',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* AKUN */}
|
||||
<div className='p-fluid'>
|
||||
{optionNoSpp.length > 0 ? (
|
||||
<div className='field col-6 md:col-4 my-3 w-1/2'>
|
||||
<LabelInput>Akun</LabelInput>
|
||||
<Dropdown
|
||||
panelClassName='p-dropdown-form dd-panel-custom'
|
||||
name={`detail_tagihan[${index}].akun_id`}
|
||||
options={optionAkun}
|
||||
// optionLabel={(item) => `${item.KODE_AKUN} - ${item.URAIAN}`}
|
||||
// optionValue='KODE_AKUN'
|
||||
value={
|
||||
formikDetailTagihan.values.detail_tagihan[index] !== undefined
|
||||
? formikDetailTagihan.values.detail_tagihan[index].akun_id
|
||||
: formikDetailTagihan.setFieldValue(`detail_tagihan[${index}].akun_id`, '')
|
||||
}
|
||||
onChange={formikDetailTagihan.handleChange}
|
||||
showClear={formikDetailTagihan.values.detail_tagihan[index] !== undefined ? true : false}
|
||||
scrollHeight='300px'
|
||||
placeholder='Select Akun'
|
||||
filter
|
||||
maxLength={100}
|
||||
/>
|
||||
{errorFieldMessageDetail(index, 'akun')}
|
||||
</div>
|
||||
) : (
|
||||
<div className='field col-6 md:col-4 my-3 w-1/2'>
|
||||
<LabelInput>Akun</LabelInput>
|
||||
<Dropdown
|
||||
panelClassName='p-dropdown-form dd-panel-custom'
|
||||
name={`detail_tagihan[${index}].akun_id`}
|
||||
options={optionAkun}
|
||||
optionLabel={(item) => `${item.akun_id} - ${item.nama}`}
|
||||
optionValue='akun_id'
|
||||
value={
|
||||
formikDetailTagihan.values.detail_tagihan[index] !== undefined
|
||||
? formikDetailTagihan.values.detail_tagihan[index].akun_id
|
||||
: formikDetailTagihan.setFieldValue(`detail_tagihan[${index}].akun_id`, '')
|
||||
}
|
||||
onChange={formikDetailTagihan.handleChange}
|
||||
showClear={formikDetailTagihan.values.detail_tagihan[index] !== undefined ? true : false}
|
||||
scrollHeight='300px'
|
||||
placeholder='Select Akun'
|
||||
filter
|
||||
maxLength={100}
|
||||
/>
|
||||
{errorFieldMessageDetail(index, 'akun')}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className='field col-12 md:col-4 my-3'>
|
||||
<LabelInput>Keperluan</LabelInput>
|
||||
<InputTextarea
|
||||
name={`detail_tagihan[${index}].keperluan`}
|
||||
rows={5}
|
||||
cols={30}
|
||||
value={
|
||||
formikDetailTagihan.values.detail_tagihan[index] !== undefined
|
||||
? formikDetailTagihan.values.detail_tagihan[index].keperluan
|
||||
: ''
|
||||
}
|
||||
onChange={formikDetailTagihan.handleChange}
|
||||
placeholder='Masukkan detail keperluan'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</BlockUI>
|
||||
|
||||
{/* BUTTON DRAFT, SIMPAN AND MESSAGE */}
|
||||
<div className='flex justify-center gap-2 mt-3'>
|
||||
{formikDetailTagihan.values.detail_tagihan[index] !== undefined &&
|
||||
formikDetailTagihan.values.detail_tagihan[index].status === true ? (
|
||||
<div className='flex flex-col'>
|
||||
<label className='p-2 bg-green-300 rounded shadow text-center'>
|
||||
<i className='pi pi-check'></i> Tagihan Berhasil dikirim/draft
|
||||
</label>
|
||||
<small className='text-red-400'>Tagihan ini hanya bisa didelete, tidak bisa diubah kembali!</small>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Draft'
|
||||
icon='pi pi-save'
|
||||
className='p-button-sm p-button-info'
|
||||
onClick={(e) => handleSubmitDetailTagihan(e, { draft: true, index })}
|
||||
loading={
|
||||
formikDetailTagihan.values.detail_tagihan[index] &&
|
||||
formikDetailTagihan.values.detail_tagihan[index].loading
|
||||
? formikDetailTagihan.values.detail_tagihan[index].loading
|
||||
: false
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Simpan'
|
||||
icon='pi pi-send'
|
||||
className='p-button-sm p-button-success'
|
||||
onClick={(e) => handleSubmitDetailTagihan(e, { draft: false, index })}
|
||||
loading={
|
||||
formikDetailTagihan.values.detail_tagihan[index] &&
|
||||
formikDetailTagihan.values.detail_tagihan[index].loading
|
||||
? formikDetailTagihan.values.detail_tagihan[index].loading
|
||||
: false
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</AccordionTab>
|
||||
)
|
||||
}
|
||||
for (let index = 0; index < countJenisTagihan; index++) {
|
||||
tagihanList.push(tagihan(index))
|
||||
}
|
||||
/**
|
||||
* *End Detail Tagihan
|
||||
* *End Card Detail Tagihan
|
||||
*/
|
||||
|
||||
/**
|
||||
* *Breadcrumb
|
||||
*/
|
||||
const items = [{ label: 'Pengajuan Tagihan', url: '/pengajuan-tagihan' }, { label: 'Form' }]
|
||||
const home = { icon: 'pi pi-home', url: '/dashboard' }
|
||||
|
||||
const handleTambahTagihan = (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
Swal.fire({
|
||||
title: 'Apakah data yang diinputkan sudah benar benar?',
|
||||
// text: "Kamu tidak bisa pakai !",
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#3085d6',
|
||||
cancelButtonColor: '#d33',
|
||||
confirmButtonText: 'Ya, benar!',
|
||||
cancelButtonText: 'Tidak',
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
Swal.fire({
|
||||
title: 'Pastikan kembali!',
|
||||
text: 'Kamu tidak bisa pakai data ini kembali!',
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#3085d6',
|
||||
cancelButtonColor: '#d33',
|
||||
confirmButtonText: 'Ya, benar!',
|
||||
cancelButtonText: 'Tidak',
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
formikDetailTagihan.setFieldValue('dataIndex', countJenisTagihan)
|
||||
formik.handleSubmit()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleBtnKirim = () => {
|
||||
Swal.fire({
|
||||
title: 'Apakah data diatas sudah benar semua?',
|
||||
text: 'Tagihan yang sudah dikirim tidak bisa diubah kembali',
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#3085d6',
|
||||
cancelButtonColor: '#d33',
|
||||
confirmButtonText: 'Ya, kirim!',
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
setKirimLoading(true)
|
||||
TagihanSubmit({ ref_id: tagihanID })
|
||||
.then((res) => {
|
||||
if (res.status == 'ok') {
|
||||
setKirimLoading(false)
|
||||
Swal.fire({
|
||||
title: 'Success',
|
||||
text: 'Tagihan berhasil dikirim',
|
||||
icon: 'success',
|
||||
timer: 2000,
|
||||
timerProgressBar: true,
|
||||
}).then(() => {
|
||||
router.push('/pengajuan-tagihan')
|
||||
})
|
||||
} else {
|
||||
setKirimLoading(false)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
setKirimLoading(false)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toast ref={toast} />
|
||||
<Head>
|
||||
<title>Form Pengajuan Tagihan</title>
|
||||
</Head>
|
||||
<Belakang>
|
||||
<BreadCrumb model={items} home={home} />
|
||||
<div className='mb-3'></div>
|
||||
<Judul>Form Pengajuan Tagihan</Judul>
|
||||
<Card>
|
||||
<form onSubmit={formik.handleSubmit}>
|
||||
<div className='p-fluid grid grid-cols-2 gap-3'>
|
||||
<div className='field col-12 md:col-4'>
|
||||
<LabelInput>No SPP</LabelInput>
|
||||
<Dropdown
|
||||
panelClassName='p-dropdown-form'
|
||||
name='no_spp'
|
||||
options={optionNoSpp}
|
||||
value={formik.values.no_spp}
|
||||
onChange={formik.handleChange}
|
||||
showClear={formik.values.no_spp ? true : false}
|
||||
scrollHeight='200px'
|
||||
placeholder='Select No SPP'
|
||||
filter
|
||||
editable
|
||||
/>
|
||||
{errorFieldMessage('no_spp')}
|
||||
</div>
|
||||
<div className='field col-12 md:col-4'>
|
||||
<LabelInput>Tanggal SPP</LabelInput>
|
||||
<Calendar
|
||||
name='tgl_spp'
|
||||
value={formik.values.tgl_spp}
|
||||
onChange={formik.handleChange}
|
||||
showButtonBar
|
||||
dateFormat='dd MM yy'
|
||||
disabled={countJenisTagihan > 0 ? true : false}
|
||||
placeholder='Masukkan tanggal SPP'
|
||||
icon='pi pi-calendar'
|
||||
showIcon
|
||||
/>
|
||||
{errorFieldMessage('tgl_spp')}
|
||||
</div>
|
||||
<div className='field col-12 md:col-4'>
|
||||
<LabelInput>Jenis Tagihan</LabelInput>
|
||||
<Dropdown
|
||||
panelClassName='p-dropdown-form'
|
||||
name='jenis_tagihan'
|
||||
options={optionJenisTagihan}
|
||||
optionLabel='nama'
|
||||
optionValue='jenis_id'
|
||||
value={formik.values.jenis_tagihan}
|
||||
onChange={(e) => {
|
||||
formik.setFieldValue('jenis_tagihan', e.value)
|
||||
e.value === 'REF-a8be274d-bfe3-4875-b334-2e32bc7e1fca' ? setItsGU(true) : setItsGU(false)
|
||||
}}
|
||||
showClear={formik.values.jenis_tagihan ? true : false}
|
||||
scrollHeight='200px'
|
||||
placeholder='Select Jenis Tagihan'
|
||||
disabled={countJenisTagihan > 0 ? true : false}
|
||||
/>
|
||||
{errorFieldMessage('jenis_tagihan')}
|
||||
</div>
|
||||
<div className='field col-12 md:col-4'>
|
||||
<LabelInput>Jenis SPP</LabelInput>
|
||||
<InputText disabled value={jenisSPP} />
|
||||
</div>
|
||||
<div className='field col-12 md:col-4'>
|
||||
<LabelInput>Uraian</LabelInput>
|
||||
<InputTextarea rows={5} cols={30} value={uraianSPP} disabled />
|
||||
</div>
|
||||
{itsGU && (
|
||||
<div className='field col-12 md:col-4 flex flex-col gap-y-2'>
|
||||
<div className='field col-12 md:col-4 flex items-center gap-x-1'>
|
||||
<Checkbox
|
||||
inputId='ingredient1'
|
||||
name='pizza'
|
||||
value='Cheese'
|
||||
checked={checkedGUGabungan}
|
||||
onChange={(e) => {
|
||||
setCheckedGUGabungan(e.checked)
|
||||
!e.checked && formik.setFieldValue('spp_gabungan', [])
|
||||
}}
|
||||
/>
|
||||
<LabelInput>GU Gabungan</LabelInput>
|
||||
<span className='text-xs font-semibold tracking-wide'>*tekan enter pernomor spp</span>
|
||||
</div>
|
||||
{checkedGUGabungan && (
|
||||
<div className=''>
|
||||
<LabelInput>No SPP Gabungan</LabelInput>
|
||||
<Chips name='spp_gabungan' value={formik.values.spp_gabungan} onChange={formik.handleChange} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
<form onSubmit={formikDetailTagihan.handleSubmit}>
|
||||
<Accordion activeIndex={0} className='mt-5 accordion-custom'>
|
||||
{tagihanList}
|
||||
</Accordion>
|
||||
</form>
|
||||
{countJenisTagihan === 0 && formikDetailTagihan.values.detail_tagihan[countJenisTagihan] === undefined ? (
|
||||
<div className='flex justify-end gap-2 mt-3'>
|
||||
<Button
|
||||
label='Kirim'
|
||||
icon={`pi ${kirimLoading ? 'pi-spin pi-spinner' : 'pi-send'}`}
|
||||
className='p-button-sm p-button-info'
|
||||
onClick={handleBtnKirim}
|
||||
disabled={true}
|
||||
/>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Tambah Tagihan'
|
||||
icon='pi pi-plus'
|
||||
className='p-button-sm p-button-primary'
|
||||
onClick={handleTambahTagihan}
|
||||
disabled={false}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className='flex justify-end gap-2 mt-3'>
|
||||
<Button
|
||||
label='Kirim'
|
||||
icon={`pi ${kirimLoading ? 'pi-spin pi-spinner' : 'pi-send'}`}
|
||||
className='p-button-sm p-button-info'
|
||||
onClick={handleBtnKirim}
|
||||
disabled={
|
||||
formikDetailTagihan.values.detail_tagihan[countJenisTagihan - 1] !== undefined &&
|
||||
formikDetailTagihan.values.detail_tagihan[countJenisTagihan - 1].status === true
|
||||
? false
|
||||
: true
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Tambah Tagihan'
|
||||
icon='pi pi-plus'
|
||||
className='p-button-sm p-button-primary'
|
||||
onClick={handleTambahTagihan}
|
||||
disabled={
|
||||
formikDetailTagihan.values.detail_tagihan[countJenisTagihan - 1] !== undefined &&
|
||||
formikDetailTagihan.values.detail_tagihan[countJenisTagihan - 1].status === true
|
||||
? false
|
||||
: true
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</Belakang>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const getServerSideProps = async (ctx) => {
|
||||
let token = ctx.req.cookies.token
|
||||
|
||||
const optionJenisTagihan = await fetchAPI('jenis-tagihan/list', token)
|
||||
const optionJenisBelanja = await fetchAPI('jenis-belanja/list', token)
|
||||
|
||||
return {
|
||||
props: {
|
||||
optionJenisTagihan,
|
||||
optionJenisBelanja,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
import { DatatablePrimeV2 } from '@/components/Datatables-v2'
|
||||
import { Label } from '@/components/Label'
|
||||
import { Belakang } from '@/components/Layouts'
|
||||
import { Judul } from '@/components/TextCustom'
|
||||
import { TagihanDelete, TagihanList } from '@/services/pengajuan-tagihan/tagihan-service'
|
||||
import { JenisTagihanList } from '@/services/referensi/jenisTagihan-service'
|
||||
import { Formik } from 'formik'
|
||||
import moment from 'moment'
|
||||
import Head from 'next/head'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Calendar } from 'primereact/calendar'
|
||||
import { Card } from 'primereact/card'
|
||||
import { Column } from 'primereact/column'
|
||||
import { DataTable } from 'primereact/datatable'
|
||||
import { Dropdown } from 'primereact/dropdown'
|
||||
import { InputText } from 'primereact/inputtext'
|
||||
import { Toast } from 'primereact/toast'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
|
||||
export default function PengajuanTagihan() {
|
||||
const toast = useRef(null)
|
||||
const [data, setData] = useState([])
|
||||
const [dialogDelete, setDialogDelete] = useState({ visible: false })
|
||||
const [expandedRows, setExpandedRows] = useState(null)
|
||||
const [ddJenisTagihan, setDdJenisTagihan] = useState([])
|
||||
const [search, setSearch] = useState({})
|
||||
const router = useRouter()
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [refresh, setRefresh] = useState(0)
|
||||
|
||||
// DATATABLE
|
||||
const [sort, setSort] = useState([])
|
||||
const [orderDir, setOrderDir] = useState('desc')
|
||||
const [orderCol, setOrderCol] = useState(2)
|
||||
const [first, setFirst] = useState(0)
|
||||
const [page, setPage] = useState(0)
|
||||
const [length, setLength] = useState(10)
|
||||
const [totalRecords, setTotalRecords] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
JenisTagihanList().then((res) => setDdJenisTagihan(res.data))
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true)
|
||||
let params = {
|
||||
draw: page,
|
||||
start: first,
|
||||
length: length,
|
||||
order_col: orderCol,
|
||||
order_dir: orderDir,
|
||||
}
|
||||
|
||||
const { no_spp, jenis_tagihan, tanggal_input } = search
|
||||
params.no_spp = no_spp ? no_spp : ''
|
||||
params.jenis_tagihan = jenis_tagihan ? jenis_tagihan : ''
|
||||
params.tanggal_input = tanggal_input ? moment(tanggal_input).format('YYYY-MM-DD') : ''
|
||||
|
||||
TagihanList(params).then((res) => {
|
||||
setData(res.data)
|
||||
setLoading(false)
|
||||
setTotalRecords(
|
||||
res.recordsFiltered === 1 ? 1 : res.recordsFiltered < 1 ? res.recordsFiltered : res.recordsFiltered
|
||||
)
|
||||
})
|
||||
}, [page, first, length, orderCol, orderDir, refresh, search])
|
||||
|
||||
const editTagihan = (data) => {
|
||||
if (data.tanggal_kirim === '-' && data.detail.length > 0) {
|
||||
router.push({ pathname: '/pengajuan-tagihan/draft/[draft]', query: { draft: data.tagihan_id } })
|
||||
} else if (data.tanggal_kirim === '-') {
|
||||
router.push({ pathname: '/pengajuan-tagihan/force-close/[force]', query: { force: data.tagihan_id } })
|
||||
} else {
|
||||
router.push({ pathname: '/pengajuan-tagihan/ditolak/[ditolak]', query: { ditolak: data.tagihan_id } })
|
||||
}
|
||||
}
|
||||
|
||||
const deleteTagihan = () => {
|
||||
TagihanDelete({ ref_id: dialogDelete.ref_id }).then((res) => {
|
||||
if (res.status === 'success') {
|
||||
setRefresh(Math.random)
|
||||
setDialogDelete({ visible: false })
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
setDialogDelete({ visible: false })
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const actionBodyTemplate = (rowData) => {
|
||||
return (
|
||||
<div className='flex gap-x-2'>
|
||||
<Button
|
||||
icon='pi pi-pencil'
|
||||
className='p-button-sm p-button-rounded'
|
||||
onClick={() => editTagihan(rowData)}
|
||||
disabled={rowData.tanggal_kirim === '-' || rowData.editable || rowData.penolakan ? false : true}
|
||||
/>
|
||||
<Button
|
||||
icon='pi pi-trash'
|
||||
className='p-button-sm p-button-rounded p-button-danger'
|
||||
onClick={() => setDialogDelete({ ref_id: rowData.tagihan_id, visible: true })}
|
||||
disabled={rowData.tanggal_kirim === '-' || rowData.editable ? false : true}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const bodyTanggal = (rowData) => {
|
||||
return rowData.tanggal_spp !== '-' ? (
|
||||
moment(rowData.tanggal_spp).format('DD MMMM YYYY')
|
||||
) : (
|
||||
<Label type={'warning'}>Belum dikirim</Label>
|
||||
)
|
||||
}
|
||||
|
||||
const datatableDetail = (data) => {
|
||||
return (
|
||||
<DataTable value={data} className='mx-20'>
|
||||
<Column field='akun' header='Akun'></Column>
|
||||
<Column field='keperluan' header='Keperluan'></Column>
|
||||
<Column field='nomor_tagihan' header='Nomor Tagihan'></Column>
|
||||
</DataTable>
|
||||
)
|
||||
}
|
||||
|
||||
const expandRow = {
|
||||
expandedRows: expandedRows,
|
||||
onRowToggle: (e) => setExpandedRows(e.data),
|
||||
responsiveLayout: 'scroll',
|
||||
rowExpansionTemplate: (e) => datatableDetail(e.detail),
|
||||
dataKey: 'no_spp',
|
||||
}
|
||||
|
||||
const Pencarian = (
|
||||
<React.Fragment>
|
||||
<Formik
|
||||
initialValues={{ no_spp: '', jenis_tagihan: '', tanggal_input: '' }}
|
||||
onSubmit={(values) => {
|
||||
const tempTanggalInput = values.tanggal_input
|
||||
values.tanggal_input &&
|
||||
Object.assign(values, { tanggal_input: moment(values.tanggal_input).format('YYYY-MM-D') })
|
||||
setSearch(values)
|
||||
values.tanggal_input = tempTanggalInput
|
||||
}}
|
||||
>
|
||||
{({ values, handleChange, handleSubmit }) => {
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className='flex gap-x-5 items-center justify-center'>
|
||||
<div className='flex flex-col'>
|
||||
<label htmlFor='no_spp'>No SPP</label>
|
||||
<InputText
|
||||
id='no_spp'
|
||||
name='no_spp'
|
||||
value={values.no_spp}
|
||||
onChange={handleChange}
|
||||
placeholder='Masukkan Nomor SPP'
|
||||
/>
|
||||
</div>
|
||||
<div className='flex flex-col'>
|
||||
<label htmlFor='no_spp'>Jenis Tagihan</label>
|
||||
<Dropdown
|
||||
name='jenis_tagihan'
|
||||
value={values.jenis_tagihan}
|
||||
optionValue='jenis_id'
|
||||
optionLabel='nama'
|
||||
options={ddJenisTagihan}
|
||||
onChange={handleChange}
|
||||
placeholder='Select Jenis Tagihan'
|
||||
showClear={values.jenis_tagihan ? true : false}
|
||||
className='w-52'
|
||||
/>
|
||||
</div>
|
||||
<div className='flex flex-col'>
|
||||
<label htmlFor='no_spp'>Tanggal Input</label>
|
||||
<Calendar
|
||||
name='tanggal_input'
|
||||
value={values.tanggal_input}
|
||||
onChange={handleChange}
|
||||
placeholder='e.g. 17 Agustus 1945'
|
||||
dateFormat='dd MM yy'
|
||||
showButtonBar
|
||||
></Calendar>
|
||||
</div>
|
||||
<div className='flex flex-col'>
|
||||
<label htmlFor=''> </label>
|
||||
<Button type='submit' label='Cari' className='p-button-sm p-button-success' icon='pi pi-search' />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}}
|
||||
</Formik>
|
||||
</React.Fragment>
|
||||
)
|
||||
|
||||
const bodyStatus = (rowData) => {
|
||||
let status
|
||||
if (rowData.tahapan === 6) {
|
||||
status = <Label type={'success'}>Selesai</Label>
|
||||
} else if (rowData.penolakan === true || rowData.pernah_ditolak === true) {
|
||||
status = <Label type={'danger'}>Ditolak/pernah ditolak</Label>
|
||||
} else {
|
||||
status = '-'
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
const allowExpansion = (rowData) => {
|
||||
return rowData.detail.length > 0
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toast ref={toast} />
|
||||
<Head>
|
||||
<title>Pengajuan Tagihan</title>
|
||||
</Head>
|
||||
<Belakang>
|
||||
<Judul>Pengajuan Tagihan</Judul>
|
||||
<Card className='card-search my-3'>{Pencarian}</Card>
|
||||
<div className='flex justify-end mb-3'>
|
||||
<Button
|
||||
className='p-button-sm'
|
||||
label={'Tambah Tagihan'}
|
||||
onClick={() => {
|
||||
router.push('/pengajuan-tagihan/form')
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<DatatablePrimeV2
|
||||
data={data}
|
||||
dialogDelete={dialogDelete}
|
||||
setDialogDelete={setDialogDelete}
|
||||
deleteTagihan={deleteTagihan}
|
||||
first={first}
|
||||
length={length}
|
||||
loading={loading}
|
||||
onSort={(e) => {
|
||||
setSort(e.multiSortMeta)
|
||||
if (e.multiSortMeta.length > 0) {
|
||||
switch (e.multiSortMeta[0].field) {
|
||||
case 'no_spp':
|
||||
setOrderCol(0)
|
||||
break
|
||||
|
||||
case 'nama_tagihan':
|
||||
setOrderCol(1)
|
||||
break
|
||||
|
||||
case 'tanggal':
|
||||
setOrderCol(2)
|
||||
break
|
||||
}
|
||||
|
||||
switch (e.multiSortMeta[0].order) {
|
||||
case -1:
|
||||
setOrderDir('desc')
|
||||
break
|
||||
|
||||
case 1:
|
||||
setOrderDir('asc')
|
||||
break
|
||||
}
|
||||
}
|
||||
}}
|
||||
setFirst={setFirst}
|
||||
setLength={setLength}
|
||||
setPage={setPage}
|
||||
sort={sort}
|
||||
totalRecords={totalRecords}
|
||||
page={page}
|
||||
expandRow={expandRow}
|
||||
>
|
||||
<Column expander={allowExpansion} style={{ width: '50px' }} />
|
||||
<Column field='no_spp' header='Nomor SPP' sortable></Column>
|
||||
<Column field='nama_tagihan' header='Jenis Tagihan' sortable></Column>
|
||||
<Column field='tanggal_spp' header='Tanggal SPP' body={bodyTanggal} sortable></Column>
|
||||
<Column header='Status' body={bodyStatus}></Column>
|
||||
<Column header='Action' body={actionBodyTemplate}></Column>
|
||||
</DatatablePrimeV2>
|
||||
</Belakang>
|
||||
</>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user