Initial commit, repo migration
This commit is contained in:
@@ -0,0 +1,346 @@
|
||||
/* eslint-disable no-mixed-spaces-and-tabs */
|
||||
import { CardCustom } from '@/components/CardCustom'
|
||||
import { Belakang } from '@/components/Layouts'
|
||||
import { Judul, SubJudul } from '@/components/TextCustom'
|
||||
import { TagihanGet, VerifikasiSave } from '@/services/otorisasi/verifikasi-service'
|
||||
import { Formik } from 'formik'
|
||||
import 'moment/locale/id'
|
||||
import Head from 'next/head'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Accordion, AccordionTab } from 'primereact/accordion'
|
||||
import { BreadCrumb } from 'primereact/breadcrumb'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Card } from 'primereact/card'
|
||||
import { InputText } from 'primereact/inputtext'
|
||||
import { InputTextarea } from 'primereact/inputtextarea'
|
||||
import { RadioButton } from 'primereact/radiobutton'
|
||||
import { TabPanel, TabView } from 'primereact/tabview'
|
||||
import { Toast } from 'primereact/toast'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import Swal from 'sweetalert2'
|
||||
|
||||
export default function FormPengesahan() {
|
||||
const router = useRouter()
|
||||
const { tagihan_id } = router.query
|
||||
const [dataTagihan, setDataTagihan] = useState([])
|
||||
const [dataDetail, setDataDetail] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
tagihan_id &&
|
||||
TagihanGet({ ref_id: tagihan_id })
|
||||
.then((res) => {
|
||||
setDataTagihan(res.data[0])
|
||||
setDataDetail(res.data[1])
|
||||
})
|
||||
.catch((err) => console.log(err))
|
||||
}, [tagihan_id])
|
||||
|
||||
const toast = useRef(null)
|
||||
const [activeIndex, setActiveIndex] = useState(0)
|
||||
|
||||
/**
|
||||
* *Breadcrumb
|
||||
*/
|
||||
const items = [{ label: 'Pengesahan', url: '/otorisasi/pengesahan' }, { label: 'Form' }]
|
||||
const home = { icon: 'pi pi-home', url: '/dashboard' }
|
||||
|
||||
const formikInitialValues = () => {
|
||||
const detail =
|
||||
dataDetail.length > 0 &&
|
||||
dataDetail.map((value) => {
|
||||
return {
|
||||
detail_id: value.detail_id,
|
||||
jenis_belanja: value.jenis_belanja,
|
||||
jenis_kegiatan: value.jenis_kegiatan,
|
||||
akun: value.akun_id,
|
||||
keperluan_untuk: value.keperluan,
|
||||
syarat: value.syarat.map((valueSyarat) => {
|
||||
return {
|
||||
syarat_id: valueSyarat.syarat_id,
|
||||
nama_persyaratan: valueSyarat.syarat_dokumen,
|
||||
catatans: {
|
||||
verif: valueSyarat.verif_note,
|
||||
bendahara: valueSyarat.bendahara_note,
|
||||
spm: valueSyarat.spm_note,
|
||||
},
|
||||
ms: valueSyarat.ms,
|
||||
}
|
||||
}),
|
||||
}
|
||||
})
|
||||
return { details: detail, persetujuan: false, tagihan_id: tagihan_id }
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toast ref={toast} />
|
||||
<Head>
|
||||
<title>Form Pengesahan</title>
|
||||
</Head>
|
||||
<Belakang>
|
||||
<BreadCrumb model={items} home={home} />
|
||||
<div className='mb-5'></div>
|
||||
<Judul>Form Pengesahan</Judul>
|
||||
<Card>
|
||||
<div className='grid grid-cols-2 gap-3'>
|
||||
<CardCustom header={'No SPP'} content={dataTagihan && dataTagihan.no_spp} />
|
||||
<CardCustom header={'Unit PPK'} content={dataTagihan && dataTagihan.unit} />
|
||||
<CardCustom header={'Jenis Tagihan'} content={dataTagihan && dataTagihan.tagihan} />
|
||||
</div>
|
||||
</Card>
|
||||
{dataDetail.length > 0 && (
|
||||
<Formik
|
||||
initialValues={formikInitialValues()}
|
||||
onSubmit={async (values, { setSubmitting }) => {
|
||||
VerifikasiSave(values)
|
||||
.then((res) => {
|
||||
if (res.status == 'ok') {
|
||||
Swal.fire({
|
||||
title: 'Success',
|
||||
text: 'Verifikasi berhasil',
|
||||
icon: 'success',
|
||||
timer: 2000,
|
||||
timerProgressBar: true,
|
||||
}).then(() => {
|
||||
router.push('/otorisasi/pengesahan')
|
||||
})
|
||||
}
|
||||
setSubmitting(false)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
setSubmitting(false)
|
||||
})
|
||||
}}
|
||||
validate={(data) => {
|
||||
let errors = {}
|
||||
data.details.map((values, indexTagihan) => {
|
||||
const indexTagihanx = indexTagihan + 1
|
||||
values.syarat.map((valuesSyarats, indexSyarat) => {
|
||||
const indexSyaratx = indexSyarat + 1
|
||||
if (!valuesSyarats.ms) {
|
||||
errors.ms = 'required'
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
summary: 'Error Message',
|
||||
detail: 'Tagihan ' + indexTagihanx + ' Syarat ke ' + indexSyaratx + ' MS/TMS Tidak Boleh Kosong',
|
||||
})
|
||||
} else if (valuesSyarats.ms === 'tms') {
|
||||
if (!valuesSyarats.catatans.bendahara) {
|
||||
errors.keterangan = 'required'
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
summary: 'Error Message',
|
||||
detail:
|
||||
'Tagihan ' + indexTagihanx + ' Syarat ke ' + indexSyaratx + ' Catatan Tidak Boleh Kosong',
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return errors
|
||||
}}
|
||||
validateOnChange={false}
|
||||
>
|
||||
{({ values, handleChange, handleSubmit, setFieldValue, isSubmitting }) => {
|
||||
const handleKirim = () => {
|
||||
setFieldValue('persetujuan', true)
|
||||
handleSubmit()
|
||||
}
|
||||
|
||||
const handleTolak = () => {
|
||||
setFieldValue('persetujuan', false)
|
||||
handleSubmit()
|
||||
}
|
||||
|
||||
let cekTMSTolak = []
|
||||
values.details.map((valRekomKabag) => {
|
||||
valRekomKabag.syarat.map((valCekSyarat) => {
|
||||
cekTMSTolak.push(valCekSyarat.ms)
|
||||
})
|
||||
})
|
||||
let cekTMSTolakH = cekTMSTolak.find((isi_tms) => isi_tms === 'tms')
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Accordion
|
||||
activeIndex={activeIndex}
|
||||
onTabChange={(e) => setActiveIndex(e.index)}
|
||||
className='mt-3 shadow'
|
||||
>
|
||||
{dataDetail.map((dataTagihan, indexTagihan) => {
|
||||
return (
|
||||
<AccordionTab header={`Tagihan ${indexTagihan + 1}`} key={indexTagihan}>
|
||||
<div className='grid grid-cols-2 gap-3'>
|
||||
<CardCustom header={'Jenis Belanja'} content={dataTagihan.jenis_belanja} type='cream' />
|
||||
<CardCustom header={'Jenis Kegiatan'} content={dataTagihan.jenis_kegiatan} type='cream' />
|
||||
<CardCustom header={'Akun'} content={dataTagihan.akun_id} type='cream' />
|
||||
<CardCustom header={'Keperluan Untuk'} content={dataTagihan.keperluan} type='cream' />
|
||||
<div className='border text-center'>
|
||||
<p className='font-semibold py-1 bg-green-100'>File</p>
|
||||
<div className='flex items-center justify-center h-[40px]'>
|
||||
<Link href={process.env.NEXT_PUBLIC_API_FILE + dataTagihan.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>
|
||||
<div className='my-3'>
|
||||
<SubJudul className='text-lg mb-1'>Syarat</SubJudul>
|
||||
<div className='flex border rounded py-1 px-3 text-xs gap-5 w-fit mb-1'>
|
||||
<div className='flex gap-3 items-center'>
|
||||
<span className='bg-logo-pink1 w-10 h-3 rounded'></span>
|
||||
<p>Syarat dengan status TMS (Tidak memenuhi syarat)</p>
|
||||
</div>
|
||||
<div className='flex gap-3 items-center'>
|
||||
<span className='bg-logo-kuning1 w-10 h-3 rounded'></span>
|
||||
<p>Syarat dengan catatan</p>
|
||||
</div>
|
||||
</div>
|
||||
<Accordion className='accordion-syarat'>
|
||||
{dataTagihan.syarat.map((valueSyarat, indexSyarat) => {
|
||||
return (
|
||||
<AccordionTab
|
||||
header={valueSyarat.syarat_dokumen}
|
||||
key={indexSyarat}
|
||||
headerClassName={`${
|
||||
values.details[indexTagihan].syarat[indexSyarat].ms === 'tms'
|
||||
? 'p-accordion-syarat-tms'
|
||||
: values.details[indexTagihan].syarat[indexSyarat].catatans.verif
|
||||
? 'p-accordion-syarat-catatan'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
<div className='mx-32'>
|
||||
<table className='custom-table'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>MS</th>
|
||||
<th>TMS</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width={'10%'}>
|
||||
<div className='flex flex-col justify-center items-center'>
|
||||
<RadioButton
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.ms`}
|
||||
value='ms'
|
||||
onChange={(e) => {
|
||||
handleChange(e)
|
||||
setFieldValue('tolak', false)
|
||||
}}
|
||||
checked={values.details[indexTagihan].syarat[indexSyarat].ms === 'ms'}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<td className='text-center' width={'10%'}>
|
||||
<RadioButton
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.ms`}
|
||||
value='tms'
|
||||
onChange={(e) => {
|
||||
handleChange(e)
|
||||
setFieldValue('tolak', true)
|
||||
}}
|
||||
checked={values.details[indexTagihan].syarat[indexSyarat].ms === 'tms'}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr className=''>
|
||||
<td colSpan={4}>
|
||||
<p className='text-xl font-semibold mb-1'>Catatan</p>
|
||||
<TabView key={indexSyarat} activeIndex={1}>
|
||||
<TabPanel header='Catatan Verif'>
|
||||
<InputTextarea
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.catatans.verif`}
|
||||
rows={5}
|
||||
cols={30}
|
||||
value={
|
||||
values.details[indexTagihan].syarat[indexSyarat].catatans.verif
|
||||
}
|
||||
onChange={handleChange}
|
||||
className='w-full'
|
||||
placeholder='Masukkan catatan Verif'
|
||||
readOnly
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel header='Catatan Bendahara'>
|
||||
<InputTextarea
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.catatans.bendahara`}
|
||||
rows={5}
|
||||
cols={30}
|
||||
value={
|
||||
values.details[indexTagihan].syarat[indexSyarat].catatans
|
||||
.bendahara
|
||||
? values.details[indexTagihan].syarat[indexSyarat].catatans
|
||||
.bendahara
|
||||
: ''
|
||||
}
|
||||
onChange={handleChange}
|
||||
className='w-full'
|
||||
placeholder='Masukkan catatan Bendahara'
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel header='Catatan SPM'>
|
||||
<InputTextarea
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.catatans.spm`}
|
||||
rows={5}
|
||||
cols={30}
|
||||
value={
|
||||
values.details[indexTagihan].syarat[indexSyarat].catatans.spm
|
||||
}
|
||||
onChange={handleChange}
|
||||
className='w-full'
|
||||
placeholder='Masukkan catatan SPM'
|
||||
readOnly
|
||||
/>
|
||||
</TabPanel>
|
||||
</TabView>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</AccordionTab>
|
||||
)
|
||||
})}
|
||||
</Accordion>
|
||||
</div>
|
||||
<SubJudul className='text-lg mb-1'>Rekomendasi Kabag</SubJudul>
|
||||
<InputText value={dataTagihan.rekomendasi_kabag} className='w-72' readOnly />
|
||||
</AccordionTab>
|
||||
)
|
||||
})}
|
||||
</Accordion>
|
||||
<div className='mt-3 flex justify-center gap-x-3'>
|
||||
<Button
|
||||
type='button'
|
||||
label='Tolak'
|
||||
onClick={() => handleTolak()}
|
||||
className='p-button-sm p-button-danger'
|
||||
disabled={cekTMSTolakH ? false : true}
|
||||
loading={isSubmitting}
|
||||
/>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Kirim'
|
||||
onClick={() => handleKirim()}
|
||||
className='p-button-sm'
|
||||
disabled={cekTMSTolakH ? true : false}
|
||||
loading={isSubmitting}
|
||||
/>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)
|
||||
}}
|
||||
</Formik>
|
||||
)}
|
||||
</Belakang>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
import { DatatablePrimeV2 } from '@/components/Datatables-v2'
|
||||
import { Belakang } from '@/components/Layouts'
|
||||
import { Judul } from '@/components/TextCustom'
|
||||
import { VerifikasiList } from '@/services/otorisasi/verifikasi-service'
|
||||
import { Formik } from 'formik'
|
||||
import moment from 'moment'
|
||||
import Head from 'next/head'
|
||||
import Link from 'next/link'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Calendar } from 'primereact/calendar'
|
||||
import { Card } from 'primereact/card'
|
||||
import { Column } from 'primereact/column'
|
||||
import { InputText } from 'primereact/inputtext'
|
||||
import { Toast } from 'primereact/toast'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
|
||||
export default function Pengesahan() {
|
||||
const toast = useRef(null)
|
||||
const [data, setData] = useState([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [search, setSearch] = useState({})
|
||||
|
||||
// DATATABLE
|
||||
const [first, setFirst] = useState(0)
|
||||
const [page, setPage] = useState(1)
|
||||
const [length, setLength] = useState(10)
|
||||
const [totalRecords, setTotalRecords] = useState(0)
|
||||
const [orderDir, setOrderDir] = useState('desc')
|
||||
const [orderCol, setOrderCol] = useState(2)
|
||||
const [sort, setSort] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true)
|
||||
let params = {
|
||||
draw: page,
|
||||
start: first,
|
||||
length: length,
|
||||
order_col: orderCol,
|
||||
order_dir: orderDir,
|
||||
}
|
||||
|
||||
const { no_spp, tanggal_input } = search
|
||||
params.no_spp = no_spp ? no_spp : ''
|
||||
params.tanggal_input = tanggal_input ? moment(tanggal_input).format('YYYY-MM-DD') : ''
|
||||
|
||||
VerifikasiList(params).then((res) => {
|
||||
setData(res.data)
|
||||
setLoading(false)
|
||||
setTotalRecords(
|
||||
res.recordsFiltered === 1 ? 1 : res.recordsFiltered < 1 ? res.recordsFiltered : res.recordsFiltered
|
||||
)
|
||||
})
|
||||
}, [page, first, length, search, orderCol, orderDir])
|
||||
|
||||
const actionBodyTemplate = (rowData) => {
|
||||
return (
|
||||
<div className='flex gap-x-2'>
|
||||
<Link href={`/otorisasi/pengesahan/form?tagihan_id=${rowData.tagihan_id}`}>
|
||||
<Button icon='pi pi-play' label='Proses' className='p-button-sm p-button-rounded' />
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const Pencarian = (
|
||||
<React.Fragment>
|
||||
<Formik
|
||||
initialValues={{ no_spp: '', 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'>Tanggal Kirim</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 bodyTanggal = (rowData) => {
|
||||
return rowData.tanggal_kirim === '-' ? rowData.tanggal_kirim : moment(rowData.tanggal_kirim).format('DD MMMM YYYY')
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toast ref={toast} />
|
||||
<Head>
|
||||
<title>Pengesahan</title>
|
||||
</Head>
|
||||
<Belakang>
|
||||
<Judul>Pengesahan</Judul>
|
||||
<Card className='card-search my-3'>{Pencarian}</Card>
|
||||
<DatatablePrimeV2
|
||||
data={data}
|
||||
first={first}
|
||||
length={length}
|
||||
loading={loading}
|
||||
page={page}
|
||||
setFirst={setFirst}
|
||||
setLength={setLength}
|
||||
setPage={setPage}
|
||||
totalRecords={totalRecords}
|
||||
sort={sort}
|
||||
onSort={(e) => {
|
||||
setSort(e.multiSortMeta)
|
||||
if (e.multiSortMeta.length > 0) {
|
||||
switch (e.multiSortMeta[0].field) {
|
||||
case 'no_spp':
|
||||
setOrderCol(0)
|
||||
break
|
||||
|
||||
case 'tanggal':
|
||||
setOrderCol(2)
|
||||
break
|
||||
}
|
||||
|
||||
switch (e.multiSortMeta[0].order) {
|
||||
case -1:
|
||||
setOrderDir('desc')
|
||||
break
|
||||
|
||||
case 1:
|
||||
setOrderDir('asc')
|
||||
break
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Column field='no_spp' header='Nomor SPP' sortable></Column>
|
||||
<Column field='unit' header='Unit' sortable></Column>
|
||||
<Column field='tanggal' header='Tanggal Kirim' body={bodyTanggal} sortable></Column>
|
||||
<Column header='Action' body={actionBodyTemplate} exportable={false}></Column>
|
||||
</DatatablePrimeV2>
|
||||
</Belakang>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,403 @@
|
||||
/* eslint-disable no-mixed-spaces-and-tabs */
|
||||
import { CardCustom } from '@/components/CardCustom'
|
||||
import { Belakang } from '@/components/Layouts'
|
||||
import { Judul, SubJudul } from '@/components/TextCustom'
|
||||
import { ddRekomendasi } from '@/constant/globalData'
|
||||
import { TagihanGet, VerifikasiSave } from '@/services/otorisasi/verifikasi-service'
|
||||
import { Formik } from 'formik'
|
||||
import 'moment/locale/id'
|
||||
import Head from 'next/head'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Accordion, AccordionTab } from 'primereact/accordion'
|
||||
import { BreadCrumb } from 'primereact/breadcrumb'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Card } from 'primereact/card'
|
||||
import { Dropdown } from 'primereact/dropdown'
|
||||
import { InputText } from 'primereact/inputtext'
|
||||
import { InputTextarea } from 'primereact/inputtextarea'
|
||||
import { RadioButton } from 'primereact/radiobutton'
|
||||
import { TabPanel, TabView } from 'primereact/tabview'
|
||||
import { Toast } from 'primereact/toast'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import Swal from 'sweetalert2'
|
||||
|
||||
export default function FormPersetujuan() {
|
||||
const router = useRouter()
|
||||
const { tagihan_id } = router.query
|
||||
const [dataTagihan, setDataTagihan] = useState([])
|
||||
const [dataDetail, setDataDetail] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
tagihan_id &&
|
||||
TagihanGet({ ref_id: tagihan_id })
|
||||
.then((res) => {
|
||||
setDataTagihan(res.data[0])
|
||||
setDataDetail(res.data[1])
|
||||
})
|
||||
.catch((err) => console.log(err))
|
||||
}, [tagihan_id])
|
||||
|
||||
const toast = useRef(null)
|
||||
const [activeIndex, setActiveIndex] = useState(0)
|
||||
|
||||
/**
|
||||
* *Breadcrumb
|
||||
*/
|
||||
const items = [{ label: 'Persetujuan', url: '/otorisasi/persetujuan' }, { label: 'Form' }]
|
||||
const home = { icon: 'pi pi-home', url: '/dashboard' }
|
||||
|
||||
const formikInitialValues = () => {
|
||||
const detail =
|
||||
dataDetail.length > 0 &&
|
||||
dataDetail.map((value) => {
|
||||
let cekTMS = []
|
||||
value.syarat.map((valueTMS) => {
|
||||
cekTMS.push(valueTMS.ms)
|
||||
})
|
||||
let cekTMSH = cekTMS.find((isi_tms) => isi_tms === 'tms')
|
||||
return {
|
||||
detail_id: value.detail_id,
|
||||
jenis_belanja: value.jenis_belanja,
|
||||
jenis_kegiatan: value.jenis_kegiatan,
|
||||
akun: value.akun_id,
|
||||
keperluan_untuk: value.keperluan,
|
||||
rekom_kabag: cekTMSH === 'tms' ? 'Ditolak/dikembalikan' : '',
|
||||
syarat: value.syarat.map((valueSyarat) => {
|
||||
return {
|
||||
syarat_id: valueSyarat.syarat_id,
|
||||
nama_persyaratan: valueSyarat.syarat_dokumen,
|
||||
catatans: {
|
||||
verif: valueSyarat.verif_note,
|
||||
bendahara: valueSyarat.bendahara_note,
|
||||
spm: valueSyarat.spm_note,
|
||||
},
|
||||
ms: valueSyarat.ms,
|
||||
}
|
||||
}),
|
||||
}
|
||||
})
|
||||
return { details: detail, persetujuan: false, tagihan_id: tagihan_id }
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toast ref={toast} />
|
||||
<Head>
|
||||
<title>Form Persetujuan</title>
|
||||
</Head>
|
||||
<Belakang>
|
||||
<BreadCrumb model={items} home={home} />
|
||||
<div className='mb-5'></div>
|
||||
<Judul>Form Persetujuan</Judul>
|
||||
<Card>
|
||||
<div className='grid grid-cols-2 gap-3'>
|
||||
<CardCustom header={'No SPP'} content={dataTagihan && dataTagihan.no_spp} />
|
||||
<CardCustom header={'Unit PPK'} content={dataTagihan && dataTagihan.unit} />
|
||||
<CardCustom header={'Jenis Tagihan'} content={dataTagihan && dataTagihan.tagihan} />
|
||||
</div>
|
||||
</Card>
|
||||
{dataDetail.length > 0 && (
|
||||
<Formik
|
||||
initialValues={formikInitialValues()}
|
||||
onSubmit={async (values, { setSubmitting }) => {
|
||||
VerifikasiSave(values)
|
||||
.then((res) => {
|
||||
if (res.status == 'ok') {
|
||||
Swal.fire({
|
||||
title: 'Success',
|
||||
text: 'Verifikasi berhasil',
|
||||
icon: 'success',
|
||||
timer: 2000,
|
||||
timerProgressBar: true,
|
||||
}).then(() => {
|
||||
router.push('/otorisasi/persetujuan')
|
||||
})
|
||||
}
|
||||
setSubmitting(false)
|
||||
})
|
||||
.catch((err) => {
|
||||
setSubmitting(false)
|
||||
console.log(err)
|
||||
})
|
||||
}}
|
||||
validate={(data) => {
|
||||
let errors = {}
|
||||
data.details.map((values, indexTagihan) => {
|
||||
const indexTagihanx = indexTagihan + 1
|
||||
if (!values.rekom_kabag) {
|
||||
errors.rekom = 'required'
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
summary: 'Error Message',
|
||||
detail: 'Rekomendasi Tagihan ' + indexTagihanx + ' Tidak Boleh Kosong',
|
||||
})
|
||||
}
|
||||
values.syarat.map((valuesSyarats, indexSyarat) => {
|
||||
const indexSyaratx = indexSyarat + 1
|
||||
if (!valuesSyarats.ms) {
|
||||
errors.ms = 'required'
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
summary: 'Error Message',
|
||||
detail: 'Tagihan ' + indexTagihanx + ' Syarat ke ' + indexSyaratx + ' MS/TMS Tidak Boleh Kosong',
|
||||
})
|
||||
} else if (valuesSyarats.ms === 'tms') {
|
||||
if (!valuesSyarats.catatans.verif) {
|
||||
errors.keterangan = 'required'
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
summary: 'Error Message',
|
||||
detail:
|
||||
'Tagihan ' + indexTagihanx + ' Syarat ke ' + indexSyaratx + ' Catatan Tidak Boleh Kosong',
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return errors
|
||||
}}
|
||||
validateOnChange={false}
|
||||
>
|
||||
{({ values, handleChange, handleSubmit, setFieldValue, isSubmitting }) => {
|
||||
const handleKirim = () => {
|
||||
setFieldValue('persetujuan', true)
|
||||
handleSubmit()
|
||||
}
|
||||
|
||||
const handleTolak = () => {
|
||||
setFieldValue('persetujuan', false)
|
||||
handleSubmit()
|
||||
}
|
||||
let cekTMSTolak = []
|
||||
values.details.map((valRekomKabag) => {
|
||||
cekTMSTolak.push(valRekomKabag.rekom_kabag)
|
||||
})
|
||||
let cekTMSTolakH = cekTMSTolak.find((isi_tms) => isi_tms === 'Ditolak/dikembalikan')
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Accordion
|
||||
activeIndex={activeIndex}
|
||||
onTabChange={(e) => setActiveIndex(e.index)}
|
||||
className='mt-3 shadow'
|
||||
>
|
||||
{dataDetail.map((dataTagihan, indexTagihan) => {
|
||||
let cekTMS = []
|
||||
values.details[indexTagihan].syarat.map((valueTMS) => {
|
||||
cekTMS.push(valueTMS.ms)
|
||||
})
|
||||
let cekTMSH = cekTMS.find((isi_tms) => isi_tms === 'tms')
|
||||
|
||||
return (
|
||||
<AccordionTab header={`Tagihan ${indexTagihan + 1}`} key={indexTagihan}>
|
||||
<div className='grid grid-cols-2 gap-3'>
|
||||
<CardCustom header={'Jenis Belanja'} content={dataTagihan.jenis_belanja} type='cream' />
|
||||
<CardCustom header={'Jenis Kegiatan'} content={dataTagihan.jenis_kegiatan} type='cream' />
|
||||
<CardCustom header={'Akun'} content={dataTagihan.akun_id} type='cream' />
|
||||
<CardCustom header={'Keperluan Untuk'} content={dataTagihan.keperluan} type='cream' />
|
||||
<div className='border text-center'>
|
||||
<p className='font-semibold py-1 bg-green-100'>File</p>
|
||||
<div className='flex items-center justify-center h-[40px]'>
|
||||
<Link href={process.env.NEXT_PUBLIC_API_FILE + dataTagihan.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>
|
||||
<div className='my-3'>
|
||||
<SubJudul className='text-lg mb-1'>Syarat</SubJudul>
|
||||
<div className='flex border rounded py-1 px-3 text-xs gap-5 w-fit mb-1'>
|
||||
<div className='flex gap-3 items-center'>
|
||||
<span className='bg-logo-pink1 w-10 h-3 rounded'></span>
|
||||
<p>Syarat dengan status TMS (Tidak memenuhi syarat)</p>
|
||||
</div>
|
||||
<div className='flex gap-3 items-center'>
|
||||
<span className='bg-logo-kuning1 w-10 h-3 rounded'></span>
|
||||
<p>Syarat dengan catatan</p>
|
||||
</div>
|
||||
</div>
|
||||
<Accordion className='accordion-syarat'>
|
||||
{dataTagihan.syarat.map((valueSyarat, indexSyarat) => {
|
||||
return (
|
||||
<AccordionTab
|
||||
header={valueSyarat.syarat_dokumen}
|
||||
key={indexSyarat}
|
||||
headerClassName={`${
|
||||
values.details[indexTagihan].syarat[indexSyarat].ms === 'tms'
|
||||
? 'p-accordion-syarat-tms'
|
||||
: values.details[indexTagihan].syarat[indexSyarat].catatans.verif
|
||||
? 'p-accordion-syarat-catatan'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
<div className='mx-32'>
|
||||
<table className='custom-table'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>MS</th>
|
||||
<th>TMS</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width={'10%'}>
|
||||
<div className='flex flex-col justify-center items-center'>
|
||||
<RadioButton
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.ms`}
|
||||
value='ms'
|
||||
onChange={(e) => {
|
||||
handleChange(e)
|
||||
setFieldValue(`details.${indexTagihan}.rekom_kabag`, null)
|
||||
}}
|
||||
checked={values.details[indexTagihan].syarat[indexSyarat].ms === 'ms'}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<td className='text-center' width={'10%'}>
|
||||
<RadioButton
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.ms`}
|
||||
value='tms'
|
||||
onChange={(e) => {
|
||||
handleChange(e)
|
||||
setFieldValue(
|
||||
`details.${indexTagihan}.rekom_kabag`,
|
||||
'Ditolak/dikembalikan'
|
||||
)
|
||||
}}
|
||||
checked={values.details[indexTagihan].syarat[indexSyarat].ms === 'tms'}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr className=''>
|
||||
<td colSpan={4}>
|
||||
<p className='text-xl font-semibold mb-1'>Catatan</p>
|
||||
<TabView key={indexSyarat} activeIndex={0}>
|
||||
<TabPanel header='Catatan Verifikator'>
|
||||
<InputTextarea
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.catatans.verif`}
|
||||
rows={5}
|
||||
cols={30}
|
||||
value={
|
||||
values.details[indexTagihan].syarat[indexSyarat].catatans.verif
|
||||
}
|
||||
onChange={handleChange}
|
||||
className='w-full'
|
||||
placeholder='Masukkan catatan Verifikator'
|
||||
/>
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel header='Catatan Bendahara'>
|
||||
<InputTextarea
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.catatans.bendahara`}
|
||||
rows={5}
|
||||
cols={30}
|
||||
value={
|
||||
values.details[indexTagihan].syarat[indexSyarat].catatans
|
||||
.bendahara
|
||||
}
|
||||
onChange={handleChange}
|
||||
className='w-full'
|
||||
placeholder='Masukkan catatan Bendahara'
|
||||
readOnly
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel header='Catatan SPM'>
|
||||
<InputTextarea
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.catatans.spm`}
|
||||
rows={5}
|
||||
cols={30}
|
||||
value={
|
||||
values.details[indexTagihan].syarat[indexSyarat].catatans.spm
|
||||
}
|
||||
onChange={handleChange}
|
||||
className='w-full'
|
||||
placeholder='Masukkan catatan SPM'
|
||||
readOnly
|
||||
/>
|
||||
</TabPanel>
|
||||
</TabView>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</AccordionTab>
|
||||
)
|
||||
})}
|
||||
</Accordion>
|
||||
</div>
|
||||
<SubJudul className='text-lg mb-1'>Rekomendasi Verif</SubJudul>
|
||||
<InputText
|
||||
value={dataTagihan.rekomendasi_verifikator}
|
||||
className='w-72'
|
||||
readOnly
|
||||
style={{ marginBottom: '12px' }}
|
||||
/>
|
||||
<SubJudul className='text-lg mb-1'>Rekomendasi Kasub</SubJudul>
|
||||
<InputText
|
||||
value={dataTagihan.rekomendasi_kasub}
|
||||
className='w-72'
|
||||
readOnly
|
||||
style={{ marginBottom: '12px' }}
|
||||
/>
|
||||
<SubJudul className='text-lg mb-1'>Rekomendasi Kabag</SubJudul>
|
||||
<Dropdown
|
||||
name={`details.${indexTagihan}.rekom_kabag`}
|
||||
optionLabel='name'
|
||||
optionValue='name'
|
||||
value={
|
||||
cekTMSH === 'tms' ? 'Ditolak/dikembalikan' : values.details[indexTagihan].rekom_kabag
|
||||
}
|
||||
options={
|
||||
cekTMSH === 'tms'
|
||||
? ddRekomendasi.filter((val) => val.name === 'Ditolak/dikembalikan')
|
||||
: ddRekomendasi.filter((val) => val.name !== 'Ditolak/dikembalikan')
|
||||
}
|
||||
onChange={handleChange}
|
||||
placeholder='Pilih rekomendasi'
|
||||
className='w-80'
|
||||
showClear={
|
||||
values.details[indexTagihan].rekom_kabag
|
||||
? values.details[indexTagihan].rekom_kabag === 'Ditolak/dikembalikan'
|
||||
? false
|
||||
: true
|
||||
: false
|
||||
}
|
||||
readOnly={cekTMSH === 'tms' ? true : false}
|
||||
/>
|
||||
</AccordionTab>
|
||||
)
|
||||
})}
|
||||
</Accordion>
|
||||
<div className='mt-3 flex justify-center gap-x-3'>
|
||||
<Button
|
||||
type='button'
|
||||
label='Tolak'
|
||||
onClick={() => handleTolak()}
|
||||
className='p-button-sm p-button-danger'
|
||||
disabled={cekTMSTolakH === 'Ditolak/dikembalikan' ? false : true}
|
||||
loading={isSubmitting}
|
||||
/>
|
||||
<Button
|
||||
type='button'
|
||||
label='Kirim'
|
||||
onClick={() => handleKirim()}
|
||||
className='p-button-sm'
|
||||
disabled={cekTMSTolakH === 'Ditolak/dikembalikan' ? true : false}
|
||||
loading={isSubmitting}
|
||||
/>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)
|
||||
}}
|
||||
</Formik>
|
||||
)}
|
||||
</Belakang>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
import { DatatablePrimeV2 } from '@/components/Datatables-v2'
|
||||
import { Belakang } from '@/components/Layouts'
|
||||
import { Judul } from '@/components/TextCustom'
|
||||
import { VerifikasiList } from '@/services/otorisasi/verifikasi-service'
|
||||
import { Formik } from 'formik'
|
||||
import moment from 'moment'
|
||||
import Head from 'next/head'
|
||||
import Link from 'next/link'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Calendar } from 'primereact/calendar'
|
||||
import { Card } from 'primereact/card'
|
||||
import { Column } from 'primereact/column'
|
||||
import { InputText } from 'primereact/inputtext'
|
||||
import { Toast } from 'primereact/toast'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
|
||||
export default function Rekomendasi() {
|
||||
const toast = useRef(null)
|
||||
const [data, setData] = useState([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [search, setSearch] = useState({})
|
||||
|
||||
// DATATABLE
|
||||
const [first, setFirst] = useState(0)
|
||||
const [page, setPage] = useState(1)
|
||||
const [length, setLength] = useState(10)
|
||||
const [totalRecords, setTotalRecords] = useState(0)
|
||||
const [orderDir, setOrderDir] = useState('desc')
|
||||
const [orderCol, setOrderCol] = useState(2)
|
||||
const [sort, setSort] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true)
|
||||
let params = {
|
||||
draw: page,
|
||||
start: first,
|
||||
length: length,
|
||||
order_col: orderCol,
|
||||
order_dir: orderDir,
|
||||
}
|
||||
|
||||
const { no_spp, tanggal_input } = search
|
||||
params.no_spp = no_spp ? no_spp : ''
|
||||
params.tanggal_input = tanggal_input ? moment(tanggal_input).format('YYYY-MM-DD') : ''
|
||||
|
||||
VerifikasiList(params).then((res) => {
|
||||
setData(res.data)
|
||||
setLoading(false)
|
||||
setTotalRecords(
|
||||
res.recordsFiltered === 1 ? 1 : res.recordsFiltered < 1 ? res.recordsFiltered : res.recordsFiltered
|
||||
)
|
||||
})
|
||||
}, [page, first, length, search, orderCol, orderDir])
|
||||
|
||||
const actionBodyTemplate = (rowData) => {
|
||||
return (
|
||||
<div className='flex gap-x-2'>
|
||||
<Link href={`/otorisasi/persetujuan/form?tagihan_id=${rowData.tagihan_id}`}>
|
||||
<Button icon='pi pi-play' label='Proses' className='p-button-sm p-button-rounded' />
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const Pencarian = (
|
||||
<React.Fragment>
|
||||
<Formik
|
||||
initialValues={{ no_spp: '', 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'>Tanggal Kirim</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 bodyTanggal = (rowData) => {
|
||||
return rowData.tanggal_kirim === '-' ? rowData.tanggal_kirim : moment(rowData.tanggal_kirim).format('DD MMMM YYYY')
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toast ref={toast} />
|
||||
<Head>
|
||||
<title>Persetujuan</title>
|
||||
</Head>
|
||||
<Belakang>
|
||||
<Judul>Persetujuan</Judul>
|
||||
<Card className='card-search my-3'>{Pencarian}</Card>
|
||||
<DatatablePrimeV2
|
||||
data={data}
|
||||
first={first}
|
||||
length={length}
|
||||
loading={loading}
|
||||
page={page}
|
||||
setFirst={setFirst}
|
||||
setLength={setLength}
|
||||
setPage={setPage}
|
||||
totalRecords={totalRecords}
|
||||
sort={sort}
|
||||
onSort={(e) => {
|
||||
setSort(e.multiSortMeta)
|
||||
if (e.multiSortMeta.length > 0) {
|
||||
switch (e.multiSortMeta[0].field) {
|
||||
case 'no_spp':
|
||||
setOrderCol(0)
|
||||
break
|
||||
|
||||
case 'tanggal':
|
||||
setOrderCol(2)
|
||||
break
|
||||
}
|
||||
|
||||
switch (e.multiSortMeta[0].order) {
|
||||
case -1:
|
||||
setOrderDir('desc')
|
||||
break
|
||||
|
||||
case 1:
|
||||
setOrderDir('asc')
|
||||
break
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Column field='no_spp' header='Nomor SPP' sortable></Column>
|
||||
<Column field='unit' header='Unit' sortable></Column>
|
||||
<Column field='tanggal' header='Tanggal Kirim' body={bodyTanggal} sortable></Column>
|
||||
<Column header='Action' body={actionBodyTemplate} exportable={false}></Column>
|
||||
</DatatablePrimeV2>
|
||||
</Belakang>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,367 @@
|
||||
/* eslint-disable no-mixed-spaces-and-tabs */
|
||||
import { CardCustom } from '@/components/CardCustom'
|
||||
import { Belakang } from '@/components/Layouts'
|
||||
import { Judul, SubJudul } from '@/components/TextCustom'
|
||||
import { ddRekomendasi } from '@/constant/globalData'
|
||||
import { TagihanGet, VerifikasiSave } from '@/services/otorisasi/verifikasi-service'
|
||||
import { Formik } from 'formik'
|
||||
import 'moment/locale/id'
|
||||
import Head from 'next/head'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Accordion, AccordionTab } from 'primereact/accordion'
|
||||
import { BreadCrumb } from 'primereact/breadcrumb'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Card } from 'primereact/card'
|
||||
import { Dropdown } from 'primereact/dropdown'
|
||||
import { InputText } from 'primereact/inputtext'
|
||||
import { InputTextarea } from 'primereact/inputtextarea'
|
||||
import { RadioButton } from 'primereact/radiobutton'
|
||||
import { TabPanel, TabView } from 'primereact/tabview'
|
||||
import { Toast } from 'primereact/toast'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import Swal from 'sweetalert2'
|
||||
|
||||
export default function FormRekomendasi() {
|
||||
const router = useRouter()
|
||||
const { tagihan_id } = router.query
|
||||
const [dataTagihan, setDataTagihan] = useState([])
|
||||
const [dataDetail, setDataDetail] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
tagihan_id &&
|
||||
TagihanGet({ ref_id: tagihan_id })
|
||||
.then((res) => {
|
||||
setDataTagihan(res.data[0])
|
||||
setDataDetail(res.data[1])
|
||||
})
|
||||
.catch((err) => console.log(err))
|
||||
}, [tagihan_id])
|
||||
|
||||
const toast = useRef(null)
|
||||
const [activeIndex, setActiveIndex] = useState(0)
|
||||
|
||||
/**
|
||||
* *Breadcrumb
|
||||
*/
|
||||
const items = [{ label: 'Verifikasi', url: '/otorisasi/verifikasi' }, { label: 'Form' }]
|
||||
const home = { icon: 'pi pi-home', url: '/dashboard' }
|
||||
|
||||
const formikInitialValues = () => {
|
||||
const detail =
|
||||
dataDetail.length > 0 &&
|
||||
dataDetail.map((value) => {
|
||||
let cekTMS = []
|
||||
value.syarat.map((valueTMS) => {
|
||||
cekTMS.push(valueTMS.ms)
|
||||
})
|
||||
let cekTMSH = cekTMS.find((isi_tms) => isi_tms === 'tms')
|
||||
return {
|
||||
detail_id: value.detail_id,
|
||||
jenis_belanja: value.jenis_belanja,
|
||||
jenis_kegiatan: value.jenis_kegiatan,
|
||||
akun: value.akun_id,
|
||||
keperluan_untuk: value.keperluan,
|
||||
syarat: value.syarat.map((valueSyarat) => {
|
||||
return {
|
||||
syarat_id: valueSyarat.syarat_id,
|
||||
nama_persyaratan: valueSyarat.syarat_dokumen,
|
||||
catatans: {
|
||||
verif: valueSyarat.verif_note,
|
||||
bendahara: valueSyarat.bendahara_note,
|
||||
spm: valueSyarat.spm_note,
|
||||
},
|
||||
ms: valueSyarat.ms,
|
||||
}
|
||||
}),
|
||||
rekom_kasub: cekTMSH === 'tms' ? 'Ditolak/dikembalikan' : '',
|
||||
}
|
||||
})
|
||||
return { details: detail, persetujuan: true, tagihan_id: tagihan_id }
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toast ref={toast} />
|
||||
<Head>
|
||||
<title>Form Rekomendasi</title>
|
||||
</Head>
|
||||
<Belakang>
|
||||
<BreadCrumb model={items} home={home} />
|
||||
<div className='mb-5'></div>
|
||||
<Judul>Form Rekomendasi</Judul>
|
||||
<Card>
|
||||
<div className='grid grid-cols-2 gap-3'>
|
||||
<CardCustom header={'No SPP'} content={dataTagihan && dataTagihan.no_spp} />
|
||||
<CardCustom header={'Unit PPK'} content={dataTagihan && dataTagihan.unit} />
|
||||
<CardCustom header={'Jenis Tagihan'} content={dataTagihan && dataTagihan.tagihan} />
|
||||
</div>
|
||||
</Card>
|
||||
{dataDetail.length > 0 && (
|
||||
<Formik
|
||||
enableReinitialize
|
||||
initialValues={formikInitialValues()}
|
||||
onSubmit={async (values, { setSubmitting }) => {
|
||||
VerifikasiSave(values)
|
||||
.then((res) => {
|
||||
if (res.status == 'ok') {
|
||||
Swal.fire({
|
||||
title: 'Success',
|
||||
text: 'Verifikasi berhasil',
|
||||
icon: 'success',
|
||||
timer: 2000,
|
||||
timerProgressBar: true,
|
||||
}).then(() => {
|
||||
router.push('/otorisasi/rekomendasi')
|
||||
})
|
||||
setSubmitting(false)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
setSubmitting(false)
|
||||
console.log(err)
|
||||
})
|
||||
}}
|
||||
validate={(data) => {
|
||||
let errors = {}
|
||||
data.details.map((values, indexTagihan) => {
|
||||
const indexTagihanx = indexTagihan + 1
|
||||
if (!values.rekom_kasub) {
|
||||
errors.rekom = 'required'
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
summary: 'Error Message',
|
||||
detail: 'Rekomendasi Tagihan ' + indexTagihanx + ' Tidak Boleh Kosong',
|
||||
})
|
||||
}
|
||||
|
||||
values.syarat.map((valuesSyarats, indexSyarat) => {
|
||||
const indexSyaratx = indexSyarat + 1
|
||||
if (!valuesSyarats.ms) {
|
||||
errors.ms = 'required'
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
summary: 'Error Message',
|
||||
detail: 'Tagihan ' + indexTagihanx + ' Syarat ke ' + indexSyaratx + ' MS/TMS Tidak Boleh Kosong',
|
||||
})
|
||||
} else if (valuesSyarats.ms === 'tms') {
|
||||
if (!valuesSyarats.catatans.verif) {
|
||||
errors.keterangan = 'required'
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
summary: 'Error Message',
|
||||
detail:
|
||||
'Tagihan ' + indexTagihanx + ' Syarat ke ' + indexSyaratx + ' Catatan Tidak Boleh Kosong',
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return errors
|
||||
}}
|
||||
validateOnChange={false}
|
||||
>
|
||||
{({ values, handleChange, handleSubmit, isSubmitting, setFieldValue }) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Accordion
|
||||
activeIndex={activeIndex}
|
||||
onTabChange={(e) => setActiveIndex(e.index)}
|
||||
className='mt-3 shadow'
|
||||
>
|
||||
{dataDetail.map((dataTagihan, indexTagihan) => {
|
||||
let cekTMS = []
|
||||
values.details[indexTagihan].syarat.map((valueTMS) => {
|
||||
cekTMS.push(valueTMS.ms)
|
||||
})
|
||||
let cekTMSH = cekTMS.find((isi_tms) => isi_tms === 'tms')
|
||||
return (
|
||||
<AccordionTab header={`Tagihan ${indexTagihan + 1}`} key={indexTagihan}>
|
||||
<div className='grid grid-cols-2 gap-3'>
|
||||
<CardCustom header={'Jenis Belanja'} content={dataTagihan.jenis_belanja} type='cream' />
|
||||
<CardCustom header={'Jenis Kegiatan'} content={dataTagihan.jenis_kegiatan} type='cream' />
|
||||
<CardCustom header={'Akun'} content={dataTagihan.akun_id} type='cream' />
|
||||
<CardCustom header={'Keperluan Untuk'} content={dataTagihan.keperluan} type='cream' />
|
||||
<div className='border text-center'>
|
||||
<p className='font-semibold py-1 bg-green-100'>File</p>
|
||||
<div className='flex items-center justify-center h-[40px]'>
|
||||
<Link href={process.env.NEXT_PUBLIC_API_FILE + dataTagihan.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>
|
||||
<div className='my-3'>
|
||||
<SubJudul className='text-lg mb-1'>Syarat</SubJudul>
|
||||
<div className='flex border rounded py-1 px-3 text-xs gap-5 w-fit mb-1'>
|
||||
<div className='flex gap-3 items-center'>
|
||||
<span className='bg-logo-pink1 w-10 h-3 rounded'></span>
|
||||
<p>Syarat dengan status TMS (Tidak memenuhi syarat)</p>
|
||||
</div>
|
||||
<div className='flex gap-3 items-center'>
|
||||
<span className='bg-logo-kuning1 w-10 h-3 rounded'></span>
|
||||
<p>Syarat dengan catatan</p>
|
||||
</div>
|
||||
</div>
|
||||
<Accordion className='accordion-syarat'>
|
||||
{dataTagihan.syarat.map((valueSyarat, indexSyarat) => {
|
||||
return (
|
||||
<AccordionTab
|
||||
header={valueSyarat.syarat_dokumen}
|
||||
key={indexSyarat}
|
||||
headerClassName={`${
|
||||
values.details[indexTagihan].syarat[indexSyarat].ms === 'tms'
|
||||
? 'p-accordion-syarat-tms'
|
||||
: values.details[indexTagihan].syarat[indexSyarat].catatans.verif
|
||||
? 'p-accordion-syarat-catatan'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
<div className='mx-32'>
|
||||
<table className='custom-table'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>MS</th>
|
||||
<th>TMS</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width={'10%'}>
|
||||
<div className='flex flex-col justify-center items-center'>
|
||||
<RadioButton
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.ms`}
|
||||
value='ms'
|
||||
onChange={handleChange}
|
||||
checked={values.details[indexTagihan].syarat[indexSyarat].ms === 'ms'}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<td className='text-center' width={'10%'}>
|
||||
<RadioButton
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.ms`}
|
||||
value='tms'
|
||||
onChange={(e) => {
|
||||
handleChange(e)
|
||||
setFieldValue(
|
||||
`details.${indexTagihan}.rekom_kasub`,
|
||||
'Ditolak/dikembalikan'
|
||||
)
|
||||
}}
|
||||
checked={values.details[indexTagihan].syarat[indexSyarat].ms === 'tms'}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr className=''>
|
||||
<td colSpan={4}>
|
||||
<p className='text-xl font-semibold mb-1'>Catatan</p>
|
||||
<TabView key={indexSyarat}>
|
||||
<TabPanel header='Catatan Verifikator'>
|
||||
<InputTextarea
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.catatans.verif`}
|
||||
rows={5}
|
||||
cols={30}
|
||||
value={
|
||||
values.details[indexTagihan].syarat[indexSyarat].catatans.verif
|
||||
}
|
||||
onChange={handleChange}
|
||||
className='w-full'
|
||||
placeholder='Masukkan catatan Verifikator'
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel header='Catatan Bendahara'>
|
||||
<InputTextarea
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.catatans.bendahara`}
|
||||
rows={5}
|
||||
cols={30}
|
||||
value={
|
||||
values.details[indexTagihan].syarat[indexSyarat].catatans
|
||||
.bendahara
|
||||
}
|
||||
onChange={handleChange}
|
||||
className='w-full'
|
||||
placeholder='Masukkan catatan Bendahara'
|
||||
readOnly
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel header='Catatan SPM'>
|
||||
<InputTextarea
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.catatans.spm`}
|
||||
rows={5}
|
||||
cols={30}
|
||||
value={
|
||||
values.details[indexTagihan].syarat[indexSyarat].catatans.spm
|
||||
}
|
||||
onChange={handleChange}
|
||||
className='w-full'
|
||||
placeholder='Masukkan catatan SPM'
|
||||
readOnly
|
||||
/>
|
||||
</TabPanel>
|
||||
</TabView>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</AccordionTab>
|
||||
)
|
||||
})}
|
||||
</Accordion>
|
||||
</div>
|
||||
<SubJudul className='text-lg mb-1'>Rekomendasi Verif</SubJudul>
|
||||
<InputText
|
||||
value={dataTagihan.rekomendasi_verifikator}
|
||||
className='w-72'
|
||||
readOnly
|
||||
style={{ marginBottom: '12px' }}
|
||||
/>
|
||||
<SubJudul className='text-lg mb-1'>Rekomendasi Validasi</SubJudul>
|
||||
<Dropdown
|
||||
name={`details.${indexTagihan}.rekom_kasub`}
|
||||
optionLabel='name'
|
||||
optionValue='name'
|
||||
value={values.details[indexTagihan].rekom_kasub}
|
||||
options={
|
||||
cekTMSH === 'tms'
|
||||
? ddRekomendasi.filter((val) => val.name === 'Ditolak/dikembalikan')
|
||||
: ddRekomendasi.filter((val) => val.name !== 'Ditolak/dikembalikan')
|
||||
}
|
||||
onChange={handleChange}
|
||||
placeholder='Pilih rekomendasi'
|
||||
className='w-80'
|
||||
showClear={
|
||||
values.details[indexTagihan].rekom_kasub
|
||||
? values.details[indexTagihan].rekom_kasub === 'Ditolak/dikembalikan'
|
||||
? false
|
||||
: true
|
||||
: false
|
||||
}
|
||||
readOnly={cekTMSH === 'tms' ? true : false}
|
||||
/>
|
||||
</AccordionTab>
|
||||
)
|
||||
})}
|
||||
</Accordion>
|
||||
<div className='mt-3 flex justify-center'>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Kirim'
|
||||
onClick={handleSubmit}
|
||||
className='p-button-sm'
|
||||
loading={isSubmitting}
|
||||
/>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)
|
||||
}}
|
||||
</Formik>
|
||||
)}
|
||||
</Belakang>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
import { DatatablePrimeV2 } from '@/components/Datatables-v2'
|
||||
import { Belakang } from '@/components/Layouts'
|
||||
import { Judul } from '@/components/TextCustom'
|
||||
import { VerifikasiList } from '@/services/otorisasi/verifikasi-service'
|
||||
import { Formik } from 'formik'
|
||||
import moment from 'moment'
|
||||
import Head from 'next/head'
|
||||
import Link from 'next/link'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Calendar } from 'primereact/calendar'
|
||||
import { Card } from 'primereact/card'
|
||||
import { Column } from 'primereact/column'
|
||||
import { InputText } from 'primereact/inputtext'
|
||||
import { Toast } from 'primereact/toast'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
|
||||
export default function Rekomendasi() {
|
||||
const toast = useRef(null)
|
||||
const [data, setData] = useState([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [search, setSearch] = useState({})
|
||||
|
||||
// DATATABLE
|
||||
const [first, setFirst] = useState(0)
|
||||
const [page, setPage] = useState(0)
|
||||
const [length, setLength] = useState(10)
|
||||
const [totalRecords, setTotalRecords] = useState(0)
|
||||
const [orderDir, setOrderDir] = useState('desc')
|
||||
const [orderCol, setOrderCol] = useState(2)
|
||||
const [sort, setSort] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true)
|
||||
let params = {
|
||||
draw: page,
|
||||
start: first,
|
||||
length: length,
|
||||
order_col: orderCol,
|
||||
order_dir: orderDir,
|
||||
}
|
||||
|
||||
const { no_spp, tanggal_input } = search
|
||||
params.no_spp = no_spp ? no_spp : ''
|
||||
params.tanggal_input = tanggal_input ? moment(tanggal_input).format('YYYY-MM-DD') : ''
|
||||
|
||||
VerifikasiList(params).then((res) => {
|
||||
setData(res.data)
|
||||
setLoading(false)
|
||||
setTotalRecords(
|
||||
res.recordsFiltered === 1 ? 1 : res.recordsFiltered < 1 ? res.recordsFiltered : res.recordsFiltered
|
||||
)
|
||||
})
|
||||
}, [page, first, length, search, orderCol, orderDir])
|
||||
|
||||
const actionBodyTemplate = (rowData) => {
|
||||
return (
|
||||
<div className='flex gap-x-2'>
|
||||
<Link href={`/otorisasi/rekomendasi/form?tagihan_id=${rowData.tagihan_id}`}>
|
||||
<Button icon='pi pi-play' label='Proses' className='p-button-sm p-button-rounded' />
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const Pencarian = (
|
||||
<React.Fragment>
|
||||
<Formik
|
||||
initialValues={{ no_spp: '', 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'>Tanggal Kirim</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 bodyTanggal = (rowData) => {
|
||||
return rowData.tanggal_kirim === '-' ? rowData.tanggal_kirim : moment(rowData.tanggal_kirim).format('DD MMMM YYYY')
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toast ref={toast} />
|
||||
<Head>
|
||||
<title>Rekomendasi</title>
|
||||
</Head>
|
||||
<Belakang>
|
||||
<Judul>Rekomendasi</Judul>
|
||||
<Card className='card-search my-3'>{Pencarian}</Card>
|
||||
<DatatablePrimeV2
|
||||
data={data}
|
||||
first={first}
|
||||
length={length}
|
||||
loading={loading}
|
||||
page={page}
|
||||
setFirst={setFirst}
|
||||
setLength={setLength}
|
||||
setPage={setPage}
|
||||
totalRecords={totalRecords}
|
||||
sort={sort}
|
||||
onSort={(e) => {
|
||||
setSort(e.multiSortMeta)
|
||||
if (e.multiSortMeta.length > 0) {
|
||||
switch (e.multiSortMeta[0].field) {
|
||||
case 'no_spp':
|
||||
setOrderCol(0)
|
||||
break
|
||||
|
||||
case 'tanggal':
|
||||
setOrderCol(2)
|
||||
break
|
||||
}
|
||||
|
||||
switch (e.multiSortMeta[0].order) {
|
||||
case -1:
|
||||
setOrderDir('desc')
|
||||
break
|
||||
|
||||
case 1:
|
||||
setOrderDir('asc')
|
||||
break
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Column field='no_spp' header='Nomor SPP' sortable></Column>
|
||||
<Column field='unit' header='Unit'></Column>
|
||||
<Column field='tanggal' header='Tanggal Kirim' body={bodyTanggal} sortable></Column>
|
||||
<Column header='Action' body={actionBodyTemplate} exportable={false}></Column>
|
||||
</DatatablePrimeV2>
|
||||
</Belakang>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
/* eslint-disable no-mixed-spaces-and-tabs */
|
||||
import { CardCustom } from '@/components/CardCustom'
|
||||
import { Belakang } from '@/components/Layouts'
|
||||
import { Judul, SubJudul } from '@/components/TextCustom'
|
||||
import { TagihanGet, VerifikasiSave } from '@/services/otorisasi/verifikasi-service'
|
||||
import { Formik } from 'formik'
|
||||
import 'moment/locale/id'
|
||||
import Head from 'next/head'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Accordion, AccordionTab } from 'primereact/accordion'
|
||||
import { BreadCrumb } from 'primereact/breadcrumb'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Card } from 'primereact/card'
|
||||
import { InputText } from 'primereact/inputtext'
|
||||
import { InputTextarea } from 'primereact/inputtextarea'
|
||||
import { RadioButton } from 'primereact/radiobutton'
|
||||
import { TabPanel, TabView } from 'primereact/tabview'
|
||||
import { Toast } from 'primereact/toast'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import Swal from 'sweetalert2'
|
||||
|
||||
export default function FormSpm() {
|
||||
const router = useRouter()
|
||||
const { tagihan_id } = router.query
|
||||
const [dataTagihan, setDataTagihan] = useState([])
|
||||
const [dataDetail, setDataDetail] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
tagihan_id &&
|
||||
TagihanGet({ ref_id: tagihan_id })
|
||||
.then((res) => {
|
||||
setDataTagihan(res.data[0])
|
||||
setDataDetail(res.data[1])
|
||||
})
|
||||
.catch((err) => console.log(err))
|
||||
}, [tagihan_id])
|
||||
|
||||
const toast = useRef(null)
|
||||
const [activeIndex, setActiveIndex] = useState(0)
|
||||
|
||||
/**
|
||||
* *Breadcrumb
|
||||
*/
|
||||
const items = [{ label: 'PPSPM', url: '/otorisasi/spm' }, { label: 'Form' }]
|
||||
const home = { icon: 'pi pi-home', url: '/dashboard' }
|
||||
|
||||
const formikInitialValues = () => {
|
||||
const detail =
|
||||
dataDetail.length > 0 &&
|
||||
dataDetail.map((value) => {
|
||||
return {
|
||||
detail_id: value.detail_id,
|
||||
jenis_belanja: value.jenis_belanja,
|
||||
jenis_kegiatan: value.jenis_kegiatan,
|
||||
akun: value.akun_id,
|
||||
keperluan_untuk: value.keperluan,
|
||||
rekom_verif: '',
|
||||
syarat: value.syarat.map((valueSyarat) => {
|
||||
return {
|
||||
syarat_id: valueSyarat.syarat_id,
|
||||
nama_persyaratan: valueSyarat.syarat_dokumen,
|
||||
catatans: {
|
||||
verif: valueSyarat.verif_note,
|
||||
kasub: valueSyarat.kasub_note,
|
||||
kabag: valueSyarat.kabag_note,
|
||||
bendahara: valueSyarat.bendahara_note,
|
||||
spm: valueSyarat.spm_note,
|
||||
},
|
||||
ms: valueSyarat.ms,
|
||||
}
|
||||
}),
|
||||
}
|
||||
})
|
||||
return { details: detail, persetujuan: false, tagihan_id: tagihan_id }
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toast ref={toast} />
|
||||
<Head>
|
||||
<title>Form PPSPM</title>
|
||||
</Head>
|
||||
<Belakang>
|
||||
<BreadCrumb model={items} home={home} />
|
||||
<div className='mb-5'></div>
|
||||
<Judul>Form PPSPM</Judul>
|
||||
<Card>
|
||||
<div className='grid grid-cols-2 gap-3'>
|
||||
<CardCustom header={'No SPP'} content={dataTagihan && dataTagihan.no_spp} />
|
||||
<CardCustom header={'Unit PPK'} content={dataTagihan && dataTagihan.unit} />
|
||||
<CardCustom header={'Jenis Tagihan'} content={dataTagihan && dataTagihan.tagihan} />
|
||||
</div>
|
||||
</Card>
|
||||
{dataDetail.length > 0 && (
|
||||
<Formik
|
||||
initialValues={formikInitialValues()}
|
||||
onSubmit={async (values, { setSubmitting }) => {
|
||||
VerifikasiSave(values)
|
||||
.then((res) => {
|
||||
if (res.status == 'ok') {
|
||||
Swal.fire({
|
||||
title: 'Success',
|
||||
text: 'Verifikasi berhasil',
|
||||
icon: 'success',
|
||||
timer: 2000,
|
||||
timerProgressBar: true,
|
||||
}).then(() => {
|
||||
router.push('/otorisasi/spm')
|
||||
})
|
||||
}
|
||||
setSubmitting(false)
|
||||
})
|
||||
.catch((err) => {
|
||||
setSubmitting(false)
|
||||
console.log(err)
|
||||
})
|
||||
}}
|
||||
validate={(data) => {
|
||||
let errors = {}
|
||||
data.details.map((values, indexTagihan) => {
|
||||
const indexTagihanx = indexTagihan + 1
|
||||
values.syarat.map((valuesSyarats, indexSyarat) => {
|
||||
const indexSyaratx = indexSyarat + 1
|
||||
if (!valuesSyarats.ms) {
|
||||
errors.ms = 'required'
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
summary: 'Error Message',
|
||||
detail: 'Tagihan ' + indexTagihanx + ' Syarat ke ' + indexSyaratx + ' MS/TMS Tidak Boleh Kosong',
|
||||
})
|
||||
} else if (valuesSyarats.ms === 'tms') {
|
||||
if (!valuesSyarats.catatans.spm) {
|
||||
errors.keterangan = 'required'
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
summary: 'Error Message',
|
||||
detail:
|
||||
'Tagihan ' + indexTagihanx + ' Syarat ke ' + indexSyaratx + ' Catatan Tidak Boleh Kosong',
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return errors
|
||||
}}
|
||||
validateOnChange={false}
|
||||
>
|
||||
{({ values, handleChange, handleSubmit, setFieldValue, isSubmitting }) => {
|
||||
const handleKirim = () => {
|
||||
setFieldValue('persetujuan', true)
|
||||
handleSubmit()
|
||||
}
|
||||
|
||||
const handleTolak = () => {
|
||||
setFieldValue('persetujuan', false)
|
||||
handleSubmit()
|
||||
}
|
||||
|
||||
let cekTMSTolak = []
|
||||
values.details.map((valRekomKabag) => {
|
||||
valRekomKabag.syarat.map((valCekSyarat) => {
|
||||
cekTMSTolak.push(valCekSyarat.ms)
|
||||
})
|
||||
})
|
||||
let cekTMSTolakH = cekTMSTolak.find((isi_tms) => isi_tms === 'tms')
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Accordion
|
||||
activeIndex={activeIndex}
|
||||
onTabChange={(e) => setActiveIndex(e.index)}
|
||||
className='mt-3 shadow'
|
||||
>
|
||||
{dataDetail.map((dataTagihan, indexTagihan) => {
|
||||
return (
|
||||
<AccordionTab header={`Tagihan ${indexTagihan + 1}`} key={indexTagihan}>
|
||||
<div className='grid grid-cols-2 gap-3'>
|
||||
<CardCustom header={'Jenis Belanja'} content={dataTagihan.jenis_belanja} type='cream' />
|
||||
<CardCustom header={'Jenis Kegiatan'} content={dataTagihan.jenis_kegiatan} type='cream' />
|
||||
<CardCustom header={'Akun'} content={dataTagihan.akun_id} type='cream' />
|
||||
<CardCustom header={'Keperluan Untuk'} content={dataTagihan.keperluan} type='cream' />
|
||||
<div className='border text-center'>
|
||||
<p className='font-semibold py-1 bg-green-100'>File</p>
|
||||
<div className='flex items-center justify-center h-[40px]'>
|
||||
<Link href={process.env.NEXT_PUBLIC_API_FILE + dataTagihan.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>
|
||||
<div className='my-3'>
|
||||
<SubJudul className='text-lg mb-1'>Syarat</SubJudul>
|
||||
<div className='flex border rounded py-1 px-3 text-xs gap-5 w-fit mb-1'>
|
||||
<div className='flex gap-3 items-center'>
|
||||
<span className='bg-logo-pink1 w-10 h-3 rounded'></span>
|
||||
<p>Syarat dengan status TMS (Tidak memenuhi syarat)</p>
|
||||
</div>
|
||||
<div className='flex gap-3 items-center'>
|
||||
<span className='bg-logo-kuning1 w-10 h-3 rounded'></span>
|
||||
<p>Syarat dengan catatan</p>
|
||||
</div>
|
||||
</div>
|
||||
<Accordion className='accordion-syarat'>
|
||||
{dataTagihan.syarat.map((valueSyarat, indexSyarat) => {
|
||||
return (
|
||||
<AccordionTab
|
||||
header={valueSyarat.syarat_dokumen}
|
||||
key={indexSyarat}
|
||||
headerClassName={`${
|
||||
values.details[indexTagihan].syarat[indexSyarat].ms === 'tms'
|
||||
? 'p-accordion-syarat-tms'
|
||||
: values.details[indexTagihan].syarat[indexSyarat].catatans.bendahara
|
||||
? 'p-accordion-syarat-catatan'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
<div className='mx-32'>
|
||||
<table className='custom-table'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>MS</th>
|
||||
<th>TMS</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width={'10%'}>
|
||||
<div className='flex flex-col justify-center items-center'>
|
||||
<RadioButton
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.ms`}
|
||||
value='ms'
|
||||
onChange={(e) => {
|
||||
handleChange(e)
|
||||
setFieldValue('tolak', false)
|
||||
}}
|
||||
checked={values.details[indexTagihan].syarat[indexSyarat].ms === 'ms'}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<td className='text-center' width={'10%'}>
|
||||
<RadioButton
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.ms`}
|
||||
value='tms'
|
||||
onChange={(e) => {
|
||||
handleChange(e)
|
||||
setFieldValue('tolak', true)
|
||||
}}
|
||||
checked={values.details[indexTagihan].syarat[indexSyarat].ms === 'tms'}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr className=''>
|
||||
<td colSpan={4}>
|
||||
<p className='text-xl font-semibold mb-1'>Catatan</p>
|
||||
<TabView key={indexSyarat} activeIndex={2}>
|
||||
<TabPanel header='Catatan Verif'>
|
||||
<InputTextarea
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.catatans.verif`}
|
||||
rows={5}
|
||||
cols={30}
|
||||
value={
|
||||
values.details[indexTagihan].syarat[indexSyarat].catatans.verif
|
||||
}
|
||||
onChange={handleChange}
|
||||
className='w-full'
|
||||
placeholder='Masukkan catatan Verif'
|
||||
readOnly
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel header='Catatan Bendahara'>
|
||||
<InputTextarea
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.catatans.bendahara`}
|
||||
rows={5}
|
||||
cols={30}
|
||||
value={
|
||||
values.details[indexTagihan].syarat[indexSyarat].catatans
|
||||
.bendahara
|
||||
}
|
||||
onChange={handleChange}
|
||||
className='w-full'
|
||||
placeholder='Masukkan catatan Bendahara'
|
||||
readOnly
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel header='Catatan SPM'>
|
||||
<InputTextarea
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.catatans.spm`}
|
||||
rows={5}
|
||||
cols={30}
|
||||
value={
|
||||
values.details[indexTagihan].syarat[indexSyarat].catatans.spm
|
||||
? values.details[indexTagihan].syarat[indexSyarat].catatans.spm
|
||||
: ''
|
||||
}
|
||||
onChange={handleChange}
|
||||
className='w-full'
|
||||
placeholder='Masukkan catatan SPM'
|
||||
/>
|
||||
</TabPanel>
|
||||
</TabView>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</AccordionTab>
|
||||
)
|
||||
})}
|
||||
</Accordion>
|
||||
</div>
|
||||
<SubJudul className='text-lg mb-1'>Rekomendasi Kabag</SubJudul>
|
||||
<InputText value={dataTagihan.rekomendasi_kabag} className='w-72' readOnly />
|
||||
</AccordionTab>
|
||||
)
|
||||
})}
|
||||
</Accordion>
|
||||
<div className='mt-3 flex justify-center gap-x-3'>
|
||||
<Button
|
||||
type='button'
|
||||
label='Tolak'
|
||||
onClick={() => handleTolak()}
|
||||
className='p-button-sm p-button-danger'
|
||||
loading={isSubmitting}
|
||||
disabled={cekTMSTolakH ? false : true}
|
||||
/>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Kirim'
|
||||
onClick={() => handleKirim()}
|
||||
className='p-button-sm'
|
||||
loading={isSubmitting}
|
||||
disabled={cekTMSTolakH ? true : false}
|
||||
/>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)
|
||||
}}
|
||||
</Formik>
|
||||
)}
|
||||
</Belakang>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
import { DatatablePrimeV2 } from '@/components/Datatables-v2'
|
||||
import { Belakang } from '@/components/Layouts'
|
||||
import { Judul } from '@/components/TextCustom'
|
||||
import { SPMList } from '@/services/otorisasi/spm-service'
|
||||
import { Formik } from 'formik'
|
||||
import moment from 'moment'
|
||||
import Head from 'next/head'
|
||||
import Link from 'next/link'
|
||||
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 { InputText } from 'primereact/inputtext'
|
||||
import { Tag } from 'primereact/tag'
|
||||
import { Toast } from 'primereact/toast'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
|
||||
export default function Ppspm() {
|
||||
const toast = useRef(null)
|
||||
const [data, setData] = useState([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [search, setSearch] = useState({})
|
||||
|
||||
// DATATABLE
|
||||
const [first, setFirst] = useState(0)
|
||||
const [page, setPage] = useState(1)
|
||||
const [length, setLength] = useState(10)
|
||||
const [totalRecords, setTotalRecords] = useState(0)
|
||||
const [orderDir, setOrderDir] = useState('desc')
|
||||
const [orderCol, setOrderCol] = useState(2)
|
||||
const [sort, setSort] = useState([])
|
||||
const [expandedRows, setExpandedRows] = useState(null)
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true)
|
||||
let params = {
|
||||
draw: page,
|
||||
start: first,
|
||||
length: length,
|
||||
order_col: orderCol,
|
||||
order_dir: orderDir,
|
||||
}
|
||||
|
||||
const { no_spp, tanggal_input } = search
|
||||
params.no_spp = no_spp ? no_spp : ''
|
||||
params.tanggal_input = tanggal_input ? moment(tanggal_input).format('YYYY-MM-DD') : ''
|
||||
|
||||
SPMList(params).then((res) => {
|
||||
setData(res.data)
|
||||
setLoading(false)
|
||||
setTotalRecords(
|
||||
res.recordsFiltered === 1 ? 1 : res.recordsFiltered < 1 ? res.recordsFiltered : res.recordsFiltered
|
||||
)
|
||||
})
|
||||
}, [page, first, length, search, orderCol, orderDir])
|
||||
|
||||
const actionBodyTemplate = (rowData) => {
|
||||
let all_spp_exist = 'all_spp_exist' in rowData ? rowData.all_spp_exist : true
|
||||
if (all_spp_exist) {
|
||||
if (rowData.detail.length > 1) {
|
||||
if (rowData.nama_tagihan === 'LS') {
|
||||
return (
|
||||
<div className='flex gap-x-2'>
|
||||
{rowData.status === 6 ? (
|
||||
<Tag severity='success' value='Done'></Tag>
|
||||
) : (
|
||||
<Link href={`/otorisasi/spm/form?tagihan_id=${rowData.tagihan_id}`}>
|
||||
<Button icon='pi pi-play' label='Proses' className='p-button-sm p-button-rounded' />
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
} else {
|
||||
return (
|
||||
<div className='flex gap-x-2'>
|
||||
{rowData.status === 6 ? (
|
||||
<Tag severity='success' value='Done'></Tag>
|
||||
) : (
|
||||
<Link href={`/otorisasi/spm/form?tagihan_id=${rowData.tagihan_id}`}>
|
||||
<Button icon='pi pi-play' label='Proses' className='p-button-sm p-button-rounded' />
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
const actionBodyTemplate2 = (rowData, belomSPM, all_spp_exist) => {
|
||||
return (
|
||||
rowData && (
|
||||
<div className='flex gap-x-2'>
|
||||
{rowData.status === 6 ? (
|
||||
<Tag severity='success' value='Done'></Tag>
|
||||
) : rowData.status < 5 ? (
|
||||
<Tag severity='warning' value='On Process'></Tag>
|
||||
) : belomSPM || !all_spp_exist ? (
|
||||
<Tag severity='warning' value='Waiting Others'></Tag>
|
||||
) : (
|
||||
<Link href={`/otorisasi/spm/form?tagihan_id=${rowData.tagihan_id}`}>
|
||||
<Button icon='pi pi-play' label='Proses' className='p-button-sm p-button-rounded' />
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const datatableDetail = (data, all_spp_exist) => {
|
||||
let cekStatus = []
|
||||
data.map((val) => {
|
||||
cekStatus.push(val.status)
|
||||
})
|
||||
let belomSPM = cekStatus.find((val) => val < 5)
|
||||
return (
|
||||
<DataTable value={data} responsiveLayout='scroll' style={{ width: '100%' }} className='dt-detail mx-20'>
|
||||
<Column field='no_spp' header='No SPP'></Column>
|
||||
<Column
|
||||
header='Action/status'
|
||||
body={(e) => actionBodyTemplate2(e, belomSPM, all_spp_exist)}
|
||||
exportable={false}
|
||||
></Column>
|
||||
</DataTable>
|
||||
)
|
||||
}
|
||||
|
||||
const allowExpansion = (rowData) => {
|
||||
return rowData.detail.length > 0 && rowData.nama_tagihan === 'GUP'
|
||||
}
|
||||
|
||||
const expandRow = {
|
||||
expandedRows: expandedRows,
|
||||
onRowToggle: (e) => setExpandedRows(e.data),
|
||||
responsiveLayout: 'scroll',
|
||||
rowExpansionTemplate: (e) => {
|
||||
let all_spp_exist = 'all_spp_exist' in e ? e.all_spp_exist : true
|
||||
return datatableDetail(e.detail, all_spp_exist)
|
||||
},
|
||||
dataKey: 'no_spp',
|
||||
}
|
||||
|
||||
const Pencarian = (
|
||||
<React.Fragment>
|
||||
<Formik
|
||||
initialValues={{ no_spp: '', 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'>Tanggal Kirim</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 bodyTanggal = (rowData) => {
|
||||
return rowData.tanggal_kirim === '-' ? rowData.tanggal_kirim : moment(rowData.tanggal_kirim).format('DD MMMM YYYY')
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toast ref={toast} />
|
||||
<Head>
|
||||
<title>PPSPM</title>
|
||||
</Head>
|
||||
<Belakang>
|
||||
<Judul>PPSPM</Judul>
|
||||
<Card className='card-search my-3'>{Pencarian}</Card>
|
||||
<DatatablePrimeV2
|
||||
data={data}
|
||||
first={first}
|
||||
length={length}
|
||||
loading={loading}
|
||||
page={page}
|
||||
setFirst={setFirst}
|
||||
setLength={setLength}
|
||||
setPage={setPage}
|
||||
totalRecords={totalRecords}
|
||||
sort={sort}
|
||||
onSort={(e) => {
|
||||
setSort(e.multiSortMeta)
|
||||
if (e.multiSortMeta.length > 0) {
|
||||
switch (e.multiSortMeta[0].field) {
|
||||
case 'no_spp':
|
||||
setOrderCol(0)
|
||||
break
|
||||
|
||||
case 'tanggal':
|
||||
setOrderCol(3)
|
||||
break
|
||||
}
|
||||
|
||||
switch (e.multiSortMeta[0].order) {
|
||||
case -1:
|
||||
setOrderDir('desc')
|
||||
break
|
||||
|
||||
case 1:
|
||||
setOrderDir('asc')
|
||||
break
|
||||
}
|
||||
}
|
||||
}}
|
||||
expandRow={expandRow}
|
||||
>
|
||||
<Column expander={allowExpansion} />
|
||||
<Column field='no_spp' header='Nomor SPP' sortable></Column>
|
||||
<Column field='nama_tagihan' header='Nama Tagihan'></Column>
|
||||
<Column field='spp_gabungan' header='SPP Gabungan'></Column>
|
||||
<Column field='tanggal' header='Tanggal Kirim' body={bodyTanggal} sortable></Column>
|
||||
<Column header='Action/status' body={actionBodyTemplate} exportable={false}></Column>
|
||||
</DatatablePrimeV2>
|
||||
</Belakang>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,379 @@
|
||||
/* eslint-disable no-mixed-spaces-and-tabs */
|
||||
import { CardCustom } from '@/components/CardCustom'
|
||||
import { Belakang } from '@/components/Layouts'
|
||||
import { Judul, SubJudul } from '@/components/TextCustom'
|
||||
import { ddRekomendasi } from '@/constant/globalData'
|
||||
import { TagihanGet, VerifikasiSave } from '@/services/otorisasi/verifikasi-service'
|
||||
import { Formik } from 'formik'
|
||||
import 'moment/locale/id'
|
||||
import Head from 'next/head'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Accordion, AccordionTab } from 'primereact/accordion'
|
||||
import { BreadCrumb } from 'primereact/breadcrumb'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Calendar } from 'primereact/calendar'
|
||||
import { Card } from 'primereact/card'
|
||||
import { Dropdown } from 'primereact/dropdown'
|
||||
import { InputText } from 'primereact/inputtext'
|
||||
import { InputTextarea } from 'primereact/inputtextarea'
|
||||
import { RadioButton } from 'primereact/radiobutton'
|
||||
import { TabPanel, TabView } from 'primereact/tabview'
|
||||
import { Toast } from 'primereact/toast'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import Swal from 'sweetalert2'
|
||||
|
||||
export default function FormVerifikasi() {
|
||||
const router = useRouter()
|
||||
const { tagihan_id } = router.query
|
||||
const [dataTagihan, setDataTagihan] = useState([])
|
||||
const [dataDetail, setDataDetail] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
tagihan_id &&
|
||||
TagihanGet({ ref_id: tagihan_id })
|
||||
.then((res) => {
|
||||
setDate(res.data[0].tanggal_bast)
|
||||
setDataTagihan(res.data[0])
|
||||
setDataDetail(res.data[1])
|
||||
})
|
||||
.catch((err) => console.log(err))
|
||||
}, [tagihan_id])
|
||||
|
||||
const toast = useRef(null)
|
||||
const [activeIndex, setActiveIndex] = useState(0)
|
||||
const [date, setDate] = useState(null)
|
||||
|
||||
/**
|
||||
* *Breadcrumb
|
||||
*/
|
||||
const items = [{ label: 'Verifikasi', url: '/otorisasi/verifikasi' }, { label: 'Form' }]
|
||||
const home = { icon: 'pi pi-home', url: '/dashboard' }
|
||||
|
||||
const formikInitialValues = () => {
|
||||
const detail =
|
||||
dataDetail.length > 0 &&
|
||||
dataDetail.map((value) => {
|
||||
return {
|
||||
detail_id: value.detail_id,
|
||||
jenis_belanja: value.jenis_belanja,
|
||||
jenis_kegiatan: value.jenis_kegiatan,
|
||||
akun: value.akun_id,
|
||||
keperluan_untuk: value.keperluan,
|
||||
rekom_verif: value.rekomendasi_verifikator,
|
||||
tanggal_bast: date,
|
||||
syarat: value.syarat.map((valueSyarat) => {
|
||||
return {
|
||||
syarat_id: valueSyarat.syarat_id,
|
||||
nama_persyaratan: valueSyarat.syarat_dokumen,
|
||||
catatans: {
|
||||
verif: valueSyarat.verif_note,
|
||||
bendahara: valueSyarat.bendahara_note,
|
||||
spm: valueSyarat.spm_note,
|
||||
},
|
||||
ms: valueSyarat.ms,
|
||||
}
|
||||
}),
|
||||
}
|
||||
})
|
||||
return { tanggal_bast: date, details: detail, persetujuan: true, tagihan_id: tagihan_id }
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toast ref={toast} />
|
||||
<Head>
|
||||
<title>Form Verifikasi</title>
|
||||
</Head>
|
||||
<Belakang>
|
||||
<BreadCrumb model={items} home={home} />
|
||||
<div className='mb-5'></div>
|
||||
<Judul>Form Verifikasi</Judul>
|
||||
<Card>
|
||||
<div className='grid grid-cols-2 gap-3'>
|
||||
<CardCustom header={'No SPP'} content={dataTagihan && dataTagihan.no_spp} />
|
||||
<CardCustom header={'Unit PPK'} content={dataTagihan && dataTagihan.unit} />
|
||||
<CardCustom header={'Jenis Tagihan'} content={dataTagihan && dataTagihan.tagihan} />
|
||||
</div>
|
||||
</Card>
|
||||
{dataDetail.length > 0 && (
|
||||
<Formik
|
||||
initialValues={formikInitialValues()}
|
||||
onSubmit={async (values) => {
|
||||
VerifikasiSave(values)
|
||||
.then((res) => {
|
||||
if (res.status == 'ok') {
|
||||
Swal.fire({
|
||||
title: 'Success',
|
||||
text: 'Verifikasi berhasil',
|
||||
icon: 'success',
|
||||
timer: 2000,
|
||||
timerProgressBar: true,
|
||||
}).then(() => {
|
||||
router.push('/otorisasi/verifikasi')
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch((err) => console.log(err))
|
||||
}}
|
||||
validate={(data) => {
|
||||
let errors = {}
|
||||
data.details.map((values, indexTagihan) => {
|
||||
const indexTagihanx = indexTagihan + 1
|
||||
if (!values.rekom_verif) {
|
||||
errors.rekom = 'required'
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
summary: 'Error Message',
|
||||
detail: 'Rekomendasi Tagihan ' + indexTagihanx + ' Tidak Boleh Kosong',
|
||||
})
|
||||
}
|
||||
|
||||
values.syarat.map((valuesSyarats, indexSyarat) => {
|
||||
const indexSyaratx = indexSyarat + 1
|
||||
if (!valuesSyarats.ms) {
|
||||
errors.ms = 'required'
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
summary: 'Error Message',
|
||||
detail: 'Tagihan ' + indexTagihanx + ' Syarat ke ' + indexSyaratx + ' MS/TMS Tidak Boleh Kosong',
|
||||
})
|
||||
} else if (valuesSyarats.ms === 'tms') {
|
||||
if (!valuesSyarats.catatans.verif) {
|
||||
errors.keterangan = 'required'
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
summary: 'Error Message',
|
||||
detail:
|
||||
'Tagihan ' + indexTagihanx + ' Syarat ke ' + indexSyaratx + ' Catatan Tidak Boleh Kosong',
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return errors
|
||||
}}
|
||||
validateOnChange={false}
|
||||
>
|
||||
{({ values, handleChange, handleSubmit, setFieldValue }) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Accordion
|
||||
activeIndex={activeIndex}
|
||||
onTabChange={(e) => setActiveIndex(e.index)}
|
||||
className='mt-3 shadow'
|
||||
>
|
||||
{dataDetail.map((dataTagihan, indexTagihan) => {
|
||||
let cekTMS = []
|
||||
values.details[indexTagihan].syarat.map((valueTMS) => {
|
||||
cekTMS.push(valueTMS.ms)
|
||||
})
|
||||
let cekTMSH = cekTMS.find((isi_tms) => isi_tms === 'tms')
|
||||
|
||||
return (
|
||||
<AccordionTab header={`Tagihan ${indexTagihan + 1}`} key={indexTagihan}>
|
||||
<div className='grid grid-cols-2 gap-3'>
|
||||
<CardCustom header={'Jenis Belanja'} content={dataTagihan.jenis_belanja} type='biru' />
|
||||
<CardCustom header={'Jenis Kegiatan'} content={dataTagihan.jenis_kegiatan} type='biru' />
|
||||
<CardCustom header={'Akun'} content={dataTagihan.akun_id} type='biru' />
|
||||
<CardCustom header={'Keperluan Untuk'} content={dataTagihan.keperluan} type='biru' />
|
||||
{dataTagihan && dataTagihan.tanggal_bast ? (
|
||||
<CardCustom
|
||||
header={'Tanggal Selesai Kegiatan/serah terima'}
|
||||
content={
|
||||
<InputText
|
||||
value={`details.${indexTagihan}.tanggal_bast`}
|
||||
disabled
|
||||
className='text-center'
|
||||
/>
|
||||
}
|
||||
type='biru'
|
||||
/>
|
||||
) : (
|
||||
<CardCustom
|
||||
header={'Tanggal Selesai Kegiatan/serah terima'}
|
||||
content={
|
||||
<Calendar
|
||||
name={`details.${indexTagihan}.tanggal_bast`}
|
||||
value={values.details[indexTagihan].tanggal_bast}
|
||||
onChange={handleChange}
|
||||
dateFormat='dd MM yy'
|
||||
showButtonBar
|
||||
className='h-[40px]'
|
||||
></Calendar>
|
||||
}
|
||||
type='biru'
|
||||
/>
|
||||
)}
|
||||
<div className='border text-center'>
|
||||
<p className='font-semibold py-1 bg-biru'>File</p>
|
||||
<div className='flex items-center justify-center h-[40px]'>
|
||||
<Link href={process.env.NEXT_PUBLIC_API_FILE + dataTagihan.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>
|
||||
<div className='my-3'>
|
||||
<SubJudul className='text-lg mb-1'>Syarat</SubJudul>
|
||||
<div className='flex border rounded py-1 px-3 text-xs gap-5 w-fit mb-1'>
|
||||
<div className='flex gap-3 items-center'>
|
||||
<span className='bg-logo-pink1 w-10 h-3 rounded'></span>
|
||||
<p>Syarat dengan status TMS (Tidak memenuhi syarat)</p>
|
||||
</div>
|
||||
<div className='flex gap-3 items-center'>
|
||||
<span className='bg-logo-kuning1 w-10 h-3 rounded'></span>
|
||||
<p>Syarat dengan catatan</p>
|
||||
</div>
|
||||
</div>
|
||||
<Accordion className='accordion-syarat'>
|
||||
{dataTagihan.syarat.map((valueSyarat, indexSyarat) => {
|
||||
return (
|
||||
<AccordionTab
|
||||
header={valueSyarat.syarat_dokumen}
|
||||
key={indexSyarat}
|
||||
headerClassName={`${
|
||||
values.details[indexTagihan].syarat[indexSyarat].ms === 'tms'
|
||||
? 'p-accordion-syarat-tms'
|
||||
: values.details[indexTagihan].syarat[indexSyarat].catatans.verif
|
||||
? 'p-accordion-syarat-catatan'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
<div className='mx-32'>
|
||||
<table className='custom-table'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>MS</th>
|
||||
<th>TMS</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width={'10%'}>
|
||||
<div className='flex flex-col justify-center items-center'>
|
||||
<RadioButton
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.ms`}
|
||||
value='ms'
|
||||
onChange={handleChange}
|
||||
checked={values.details[indexTagihan].syarat[indexSyarat].ms === 'ms'}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<td className='text-center' width={'10%'}>
|
||||
<RadioButton
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.ms`}
|
||||
value='tms'
|
||||
onChange={(e) => {
|
||||
handleChange(e)
|
||||
setFieldValue(
|
||||
`details.${indexTagihan}.rekom_verif`,
|
||||
'Ditolak/dikembalikan'
|
||||
)
|
||||
}}
|
||||
checked={values.details[indexTagihan].syarat[indexSyarat].ms === 'tms'}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr className=''>
|
||||
<td colSpan={4}>
|
||||
<p className='text-xl font-semibold mb-1'>Catatan</p>
|
||||
<TabView key={indexSyarat}>
|
||||
<TabPanel header='Catatan Verifikator'>
|
||||
<InputTextarea
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.catatans.verif`}
|
||||
rows={5}
|
||||
cols={30}
|
||||
value={
|
||||
values.details[indexTagihan].syarat[indexSyarat].catatans.verif
|
||||
}
|
||||
onChange={handleChange}
|
||||
className='w-full'
|
||||
placeholder='Masukkan catatan Verifikator'
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel header='Catatan Bendahara'>
|
||||
<InputTextarea
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.catatans.bendahara`}
|
||||
rows={5}
|
||||
cols={30}
|
||||
value={
|
||||
values.details[indexTagihan].syarat[indexSyarat].catatans
|
||||
.bendahara
|
||||
}
|
||||
onChange={handleChange}
|
||||
className='w-full'
|
||||
placeholder='Masukkan catatan Bendahara'
|
||||
readOnly
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel header='Catatan SPM'>
|
||||
<InputTextarea
|
||||
name={`details.${indexTagihan}.syarat.${indexSyarat}.catatans.spm`}
|
||||
rows={5}
|
||||
cols={30}
|
||||
value={
|
||||
values.details[indexTagihan].syarat[indexSyarat].catatans.spm
|
||||
}
|
||||
onChange={handleChange}
|
||||
className='w-full'
|
||||
placeholder='Masukkan catatan SPM'
|
||||
readOnly
|
||||
/>
|
||||
</TabPanel>
|
||||
</TabView>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</AccordionTab>
|
||||
)
|
||||
})}
|
||||
</Accordion>
|
||||
</div>
|
||||
<SubJudul className='text-lg mb-1'>Rekomendasi</SubJudul>
|
||||
<Dropdown
|
||||
name={`details.${indexTagihan}.rekom_verif`}
|
||||
optionLabel='name'
|
||||
optionValue='name'
|
||||
value={
|
||||
cekTMSH === 'tms' ? 'Ditolak/dikembalikan' : values.details[indexTagihan].rekom_verif
|
||||
}
|
||||
options={
|
||||
cekTMSH === 'tms'
|
||||
? ddRekomendasi.filter((val) => val.name === 'Ditolak/dikembalikan')
|
||||
: ddRekomendasi.filter((val) => val.name !== 'Ditolak/dikembalikan')
|
||||
}
|
||||
onChange={handleChange}
|
||||
placeholder='Pilih rekomendasi'
|
||||
className='w-80'
|
||||
showClear={
|
||||
values.details[indexTagihan].rekom_verif
|
||||
? values.details[indexTagihan].rekom_verif === 'Ditolak/dikembalikan'
|
||||
? false
|
||||
: true
|
||||
: false
|
||||
}
|
||||
readOnly={cekTMSH === 'tms' ? true : false}
|
||||
/>
|
||||
</AccordionTab>
|
||||
)
|
||||
})}
|
||||
</Accordion>
|
||||
<div className='mt-3 flex justify-center'>
|
||||
<Button type='submit' label='Kirim' onClick={handleSubmit} className='p-button-sm' />
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)
|
||||
}}
|
||||
</Formik>
|
||||
)}
|
||||
</Belakang>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
import { DatatablePrimeV2 } from '@/components/Datatables-v2'
|
||||
import { Label } from '@/components/Label'
|
||||
import { Belakang } from '@/components/Layouts'
|
||||
import { Judul } from '@/components/TextCustom'
|
||||
import { VerifikasiList } from '@/services/otorisasi/verifikasi-service'
|
||||
import { Formik } from 'formik'
|
||||
import moment from 'moment'
|
||||
import Head from 'next/head'
|
||||
import Link from 'next/link'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Calendar } from 'primereact/calendar'
|
||||
import { Card } from 'primereact/card'
|
||||
import { Column } from 'primereact/column'
|
||||
import { InputText } from 'primereact/inputtext'
|
||||
import { Toast } from 'primereact/toast'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
|
||||
export default function Verifikasi() {
|
||||
const toast = useRef(null)
|
||||
const [data, setData] = useState([])
|
||||
const [search, setSearch] = useState('')
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
// DATATABLE
|
||||
const [first, setFirst] = useState(0)
|
||||
const [page, setPage] = useState(0)
|
||||
const [length, setLength] = useState(10)
|
||||
const [totalRecords, setTotalRecords] = useState(0)
|
||||
const [sort, setSort] = useState([])
|
||||
const [orderDir, setOrderDir] = useState('desc')
|
||||
const [orderCol, setOrderCol] = useState(2)
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true)
|
||||
let params = {
|
||||
draw: page,
|
||||
start: first,
|
||||
length: length,
|
||||
order_col: orderCol,
|
||||
order_dir: orderDir,
|
||||
}
|
||||
|
||||
const { no_spp, tanggal_input } = search
|
||||
params.no_spp = no_spp ? no_spp : ''
|
||||
params.tanggal_input = tanggal_input ? moment(tanggal_input).format('YYYY-MM-DD') : ''
|
||||
|
||||
VerifikasiList(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, search])
|
||||
|
||||
const actionBodyTemplate = (rowData) => {
|
||||
return (
|
||||
<div className='flex gap-x-2'>
|
||||
<Link href={`/otorisasi/verifikasi/form?tagihan_id=${rowData.tagihan_id}`}>
|
||||
<Button icon='pi pi-play' label='Proses' className='p-button-sm p-button-rounded' />
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const bodyTanggal = (rowData) => {
|
||||
return rowData.tanggal_kirim === '-' ? rowData.tanggal_kirim : moment(rowData.tanggal_kirim).format('DD MMMM YYYY')
|
||||
}
|
||||
|
||||
const Pencarian = (
|
||||
<React.Fragment>
|
||||
<Formik
|
||||
initialValues={{ no_spp: '', 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'>Tanggal Kirim</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 bodyUrgensi = (rowData) => {
|
||||
let color
|
||||
switch (rowData.urgensi.toLowerCase()) {
|
||||
case 'low':
|
||||
color = 'success'
|
||||
break
|
||||
|
||||
case 'medium':
|
||||
color = 'warning'
|
||||
break
|
||||
|
||||
case 'high':
|
||||
color = 'danger'
|
||||
break
|
||||
}
|
||||
return <Label type={color}>{rowData.urgensi}</Label>
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toast ref={toast} />
|
||||
<Head>
|
||||
<title>Verifikasi</title>
|
||||
</Head>
|
||||
<Belakang>
|
||||
<Judul>Verifikasi</Judul>
|
||||
<Card className='card-search my-3'>{Pencarian}</Card>
|
||||
<DatatablePrimeV2
|
||||
data={data}
|
||||
first={first}
|
||||
length={length}
|
||||
page={page}
|
||||
setFirst={setFirst}
|
||||
setLength={setLength}
|
||||
setPage={setPage}
|
||||
onSort={(e) => {
|
||||
setSort(e.multiSortMeta)
|
||||
if (e.multiSortMeta.length > 0) {
|
||||
switch (e.multiSortMeta[0].field) {
|
||||
case 'no_spp':
|
||||
setOrderCol(0)
|
||||
break
|
||||
|
||||
case 'tanggal':
|
||||
setOrderCol(2)
|
||||
break
|
||||
}
|
||||
|
||||
switch (e.multiSortMeta[0].order) {
|
||||
case -1:
|
||||
setOrderDir('desc')
|
||||
break
|
||||
|
||||
case 1:
|
||||
setOrderDir('asc')
|
||||
break
|
||||
}
|
||||
}
|
||||
}}
|
||||
totalRecords={totalRecords}
|
||||
sort={sort}
|
||||
loading={loading}
|
||||
>
|
||||
<Column field='no_spp' header='Nomor SPP' sortable></Column>
|
||||
<Column field='unit' header='Unit'></Column>
|
||||
<Column field='tanggal' header='Tanggal Kirim' body={bodyTanggal} sortable></Column>
|
||||
<Column header='Urgensi' body={bodyUrgensi}></Column>
|
||||
<Column header='Action' body={actionBodyTemplate} exportable={false}></Column>
|
||||
</DatatablePrimeV2>
|
||||
</Belakang>
|
||||
</>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user