Initial commit, repo migration

This commit is contained in:
unknown
2024-05-14 22:19:14 +07:00
commit b51b940ac4
137 changed files with 19393 additions and 0 deletions
+651
View File
@@ -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,
},
}
}