Initial commit, repo migration
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Number } from './Number'
|
||||
import { Word } from './Word'
|
||||
|
||||
const days = ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT']
|
||||
|
||||
export const Clock = ({ h24 = true }) => {
|
||||
const [hour, setHour] = useState(0)
|
||||
const [minute, setMinute] = useState(0)
|
||||
const [second, setSecond] = useState(0)
|
||||
const [day, setDay] = useState(0)
|
||||
const [pm, setPm] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const update = () => {
|
||||
const date = new Date()
|
||||
let hour = date.getHours()
|
||||
if (!h24) {
|
||||
hour = hour % 12 || 12
|
||||
}
|
||||
setHour(hour)
|
||||
setMinute(date.getMinutes())
|
||||
setSecond(date.getSeconds())
|
||||
setDay(date.getDay())
|
||||
setPm(date.getHours() >= 12)
|
||||
}
|
||||
|
||||
update()
|
||||
|
||||
const interval = setInterval(() => {
|
||||
update()
|
||||
}, 1000)
|
||||
|
||||
return () => clearInterval(interval)
|
||||
}, [h24])
|
||||
|
||||
return (
|
||||
<div className='clock shadow border m-2 rounded-lg flex items-center flex-col overflow-hidden px-4 py-7'>
|
||||
<div className='calendar flex gap-4 2xl:text-3xl text-xl mb-3'>
|
||||
{days.map((value, index) => (
|
||||
<Word key={value} value={value} hidden={index != day} />
|
||||
))}
|
||||
</div>
|
||||
<div className='clock-row flex gap-4 items-center'>
|
||||
<div className='hour 2xl:text-7xl text-5xl'>
|
||||
<Number value={hour} />
|
||||
<Word value={':'} />
|
||||
<Number value={minute} />
|
||||
<Word value={':'} />
|
||||
<Number value={second} />
|
||||
</div>
|
||||
<div className='ampm 2xl:text-5xl text-4xl'>
|
||||
<Word value={'AM'} hidden={pm} />
|
||||
<Word value={'PM'} hidden={!pm} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export const Number = ({ value = 0 }) => {
|
||||
const result = String(value).padStart(2, '0')
|
||||
return (
|
||||
<div className='digital'>
|
||||
<p>88</p>
|
||||
<p>{result}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
export const Word = ({ value, hidden = false }) => {
|
||||
const getStyle = () => {
|
||||
return {
|
||||
visibility: hidden ? 'hidden' : 'visible',
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div className='digital'>
|
||||
<p>{value}</p>
|
||||
<p style={getStyle()}>{value}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
export const CardCustom = ({ header, content, type = 'success' }) => {
|
||||
let backgroundColor
|
||||
switch (type) {
|
||||
case 'success':
|
||||
backgroundColor = 'bg-green-100'
|
||||
break
|
||||
|
||||
case 'secondary':
|
||||
backgroundColor = 'bg-gray-100'
|
||||
break
|
||||
|
||||
case 'biru':
|
||||
backgroundColor = 'bg-biru'
|
||||
break
|
||||
|
||||
default:
|
||||
backgroundColor = 'bg-green-100'
|
||||
break
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='border text-center'>
|
||||
<p className={`font-semibold py-1 ${backgroundColor}`}>{header}</p>
|
||||
<p className='p-1'>{content}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const CardFilterLaporan = ({ header, children }) => {
|
||||
return (
|
||||
<div className='border rounded shadow mb-5'>
|
||||
<p className={`font-semibold p-1 bg-orange3 text-center text-xl`}>{header}</p>
|
||||
<div className='flex justify-center p-3'>{children}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import { DataTable } from 'primereact/datatable'
|
||||
import { Dropdown } from 'primereact/dropdown'
|
||||
import { Paginator } from 'primereact/paginator'
|
||||
import { DialogDashboard, DialogDelete } from './Dialog'
|
||||
|
||||
export const DatatablePrimeV2 = ({
|
||||
children,
|
||||
data,
|
||||
loading,
|
||||
sort,
|
||||
first,
|
||||
setLength,
|
||||
totalRecords,
|
||||
setPage,
|
||||
setFirst,
|
||||
length,
|
||||
onSort,
|
||||
dataDialog = { no_spp: false },
|
||||
displayDialog,
|
||||
setDisplayDialog,
|
||||
dialogDelete = { visible: false },
|
||||
setDialogDelete,
|
||||
deleteTagihan,
|
||||
page,
|
||||
expandRow,
|
||||
}) => {
|
||||
const onPageChange = (event) => {
|
||||
setPage(event.page)
|
||||
setFirst(event.first)
|
||||
}
|
||||
|
||||
const templatePaginator = {
|
||||
layout: 'RowsPerPageDropdown FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink CurrentPageReport',
|
||||
RowsPerPageDropdown: (options) => {
|
||||
const dropdownOptions = [
|
||||
{ label: 10, value: 10 },
|
||||
{ label: 50, value: 50 },
|
||||
{ label: 100, value: 100 },
|
||||
{ label: 'All', value: options.totalRecords },
|
||||
]
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
value={options.value}
|
||||
options={dropdownOptions}
|
||||
onChange={(e) => {
|
||||
options.onChange(e)
|
||||
setLength(e.value)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
CurrentPageReport: (options) => {
|
||||
return (
|
||||
<span className='w-fit ml-4' style={{ color: 'var(--text-color)', userSelect: 'none', textAlign: 'center' }}>
|
||||
{options.first} - {options.last} of {options.totalRecords}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<DataTable
|
||||
value={data}
|
||||
size='small'
|
||||
className='dt-dashboard'
|
||||
stripedRows
|
||||
loading={loading}
|
||||
sortMode='multiple'
|
||||
removableSort={true}
|
||||
multiSortMeta={sort}
|
||||
onSort={onSort}
|
||||
{...expandRow}
|
||||
>
|
||||
{children}
|
||||
</DataTable>
|
||||
{(data.length > 5 || page > 0) && (
|
||||
<Paginator
|
||||
first={first}
|
||||
rows={length}
|
||||
template={templatePaginator}
|
||||
totalRecords={totalRecords}
|
||||
onPageChange={onPageChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
{dataDialog.no_spp && (
|
||||
<DialogDashboard data={dataDialog} displayDialog={displayDialog} setDisplayDialog={setDisplayDialog} />
|
||||
)}
|
||||
|
||||
{dialogDelete.visible === true && (
|
||||
<DialogDelete data={dialogDelete} setDelete={setDialogDelete} onCallback={deleteTagihan} />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
import { useRouter } from 'next/router'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Card } from 'primereact/card'
|
||||
import { DataTable } from 'primereact/datatable'
|
||||
import { InputText } from 'primereact/inputtext'
|
||||
import { Paginator } from 'primereact/paginator'
|
||||
import { Toolbar } from 'primereact/toolbar'
|
||||
|
||||
/**
|
||||
* CUSTOM DATATABLES PRIME REACT
|
||||
* @children column-column data
|
||||
* @data isi dari datatables
|
||||
* @dataDrawPrev untuk cek apakah ada data sebelumnya
|
||||
* @dataDrawNext untuk cek apakah ada data setelahnya
|
||||
* @draw untuk merubah draw/page datatables
|
||||
* @setDraw untuk merubah draw/page datatables
|
||||
* @buttonUpload button untuk upload data
|
||||
* @buttonUploadLabel label button add
|
||||
* @buttonAdd button untuk add data
|
||||
* @buttonAddLabel label button add
|
||||
* @dialogForm usestate untuk open/close dialog/modal form
|
||||
* @setDialogForm set dialog form modal untuk set open/close
|
||||
* @Form form modal dynamic
|
||||
* @dataEdit state yang menyimpan data yang akan di edit
|
||||
* @setDataEdit set state data edit
|
||||
* @refresh state untuk refresh datatables
|
||||
* @toast reference untuk toast
|
||||
* @setSearch set state untuk pencarian pada datatables
|
||||
* @buttonAddCustom untuk add pengajuan tagihan
|
||||
* @buttonAddCustomLabel label button add custom
|
||||
* @expandRow untuk detail row
|
||||
* @fiturSearch untuk tampil kolom search
|
||||
*/
|
||||
export const DatatablePrime = ({
|
||||
children,
|
||||
data,
|
||||
dataDrawPrev = 0,
|
||||
dataDrawNext = 0,
|
||||
draw,
|
||||
setDraw,
|
||||
buttonUpload,
|
||||
buttonUploadLabel,
|
||||
buttonAdd,
|
||||
buttonAddLabel,
|
||||
dialogForm,
|
||||
setDialogForm,
|
||||
setDialogUpload,
|
||||
Form,
|
||||
dataEdit,
|
||||
setDataEdit,
|
||||
refresh,
|
||||
toast,
|
||||
setSearch,
|
||||
buttonAddCustom,
|
||||
buttonAddCustomLabel,
|
||||
linkCustom,
|
||||
expandRow,
|
||||
fiturSearch = true,
|
||||
showHeader = true,
|
||||
}) => {
|
||||
const first = 0
|
||||
const rows = 30
|
||||
const router = useRouter()
|
||||
const keyUpSearch = (data) => {
|
||||
setSearch(data)
|
||||
}
|
||||
|
||||
const leftToolbarTemplate = () => {
|
||||
return <></>
|
||||
}
|
||||
|
||||
const rightToolbarTemplate = () => {
|
||||
return (
|
||||
<>
|
||||
<div className='flex gap-x-3'>
|
||||
{buttonUpload && (
|
||||
<Button
|
||||
className='p-button-sm'
|
||||
label={buttonUploadLabel}
|
||||
onClick={() => {
|
||||
setDialogUpload(true)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{buttonAdd && (
|
||||
<Button
|
||||
className='p-button-sm p-button-success'
|
||||
label={buttonAddLabel}
|
||||
onClick={() => {
|
||||
setDialogForm(true), setDataEdit([])
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{buttonAddCustom && (
|
||||
<Button
|
||||
className='p-button-sm'
|
||||
label={buttonAddCustomLabel}
|
||||
onClick={() => {
|
||||
router.push(linkCustom)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const dtHeader = () => {
|
||||
return (
|
||||
<form className='flex justify-end'>
|
||||
<div className='flex gap-x-2'>
|
||||
{fiturSearch && (
|
||||
<div className='p-inputgroup'>
|
||||
<InputText
|
||||
className='p-inputtext-sm dt-search'
|
||||
onChange={(e) => keyUpSearch(e.target.value)}
|
||||
placeholder='Search'
|
||||
/>
|
||||
<span className='p-inputgroup-addon'>
|
||||
<i className='pi pi-search' />
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
const templatePaginator = {
|
||||
layout: 'PrevPageLink NextPageLink',
|
||||
PrevPageLink: () => {
|
||||
return (
|
||||
<button
|
||||
type='button'
|
||||
className='p-paginator-prev p-paginator-element p-link'
|
||||
onClick={() => setDraw(draw > 1 ? draw - 1 : draw)}
|
||||
disabled={dataDrawPrev.length > 0 ? false : true}
|
||||
>
|
||||
<span className='p-3'>Previous</span>
|
||||
</button>
|
||||
)
|
||||
},
|
||||
NextPageLink: () => {
|
||||
return (
|
||||
<button
|
||||
type='button'
|
||||
className='p-paginator-next p-paginator-element p-link'
|
||||
onClick={() => setDraw(draw + 1)}
|
||||
disabled={dataDrawNext.length > 0 ? false : true}
|
||||
>
|
||||
<span className='p-3'>Next</span>
|
||||
</button>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card className='dt-card'>
|
||||
<Toolbar className='dt-toolbar' left={leftToolbarTemplate} right={rightToolbarTemplate} />
|
||||
<DataTable
|
||||
value={data}
|
||||
className='dt-prime'
|
||||
stripedRows
|
||||
scrollable
|
||||
scrollHeight='40rem'
|
||||
size='small'
|
||||
header={showHeader && dtHeader}
|
||||
{...expandRow}
|
||||
>
|
||||
{children}
|
||||
</DataTable>
|
||||
|
||||
{(data.length === 30 || draw > 1) && (
|
||||
<Paginator first={first} rows={rows} template={templatePaginator}></Paginator>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
{dialogForm && (
|
||||
<Form
|
||||
dialogForm={dialogForm}
|
||||
setDialogForm={setDialogForm}
|
||||
dataEdit={dataEdit}
|
||||
setDataEdit={setDataEdit}
|
||||
refresh={refresh}
|
||||
toast={toast}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
import axios from 'axios'
|
||||
import moment from 'moment'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Dialog } from 'primereact/dialog'
|
||||
import { TabPanel, TabView } from 'primereact/tabview'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
export const DialogDelete = ({ data, setDelete, onCallback }) => {
|
||||
const modelFooter = (
|
||||
<div className='flex justify-end'>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Cancel'
|
||||
className='p-button-sm p-button-secondary'
|
||||
onClick={() => setDelete({ visible: false })}
|
||||
/>
|
||||
<Button type='submit' label='Delete' className='p-button-sm p-button-primary' onClick={onCallback} />
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
header='Delete Data'
|
||||
className='p-dialog-delete'
|
||||
visible={data.visible}
|
||||
draggable={false}
|
||||
resizable={false}
|
||||
position={'center'}
|
||||
closable={false}
|
||||
style={{ width: '25vw' }}
|
||||
footer={modelFooter}
|
||||
>
|
||||
<p>Are you sure you want to delete this data?</p>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const DialogDashboard = ({ data, displayDialog, setDisplayDialog }) => {
|
||||
const [dataFilter, setDataFilter] = useState([{ NO_SP2D: '-', TGL_SPM: '', NO_SPM: '-', TGL_SP2D: '' }])
|
||||
|
||||
// NOMER SPP
|
||||
useEffect(() => {
|
||||
axios({
|
||||
method: 'get',
|
||||
url: process.env.NEXT_PUBLIC_API_SAKTI_REALISASI,
|
||||
headers: {
|
||||
Authorization: `Bearer ` + process.env.NEXT_PUBLIC_TOKEN_SAKTI,
|
||||
},
|
||||
})
|
||||
.then(function (response) {
|
||||
let dataFilter = response.data.filter((dataResponse) => dataResponse.NO_SPP === data.no_spp)
|
||||
if (dataFilter.length > 0) {
|
||||
setDataFilter(dataFilter)
|
||||
}
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log(error)
|
||||
})
|
||||
}, [data.no_spp])
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
header='Histori Pengajuan'
|
||||
visible={displayDialog}
|
||||
onHide={() => setDisplayDialog(false)}
|
||||
headerClassName='header-history'
|
||||
className='dialog-history 2xl:w-[90vw] lg:w-[80vw]'
|
||||
draggable={false}
|
||||
>
|
||||
<div className='grid grid-cols-2 gap-x-3 border-b-2 pb-7 mb-3'>
|
||||
<table className='h-fit border rounded'>
|
||||
<tbody>
|
||||
<tr className=''>
|
||||
<td className='border-b px-3 py-2 w-44 font-semibold'>Unit PPK</td>
|
||||
<td className='border-b px-3 py-2 w-1'>:</td>
|
||||
<td className='border-b px-3 py-2'>{data.nama_unit}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='border-b px-3 py-2 font-semibold'>No SPP</td>
|
||||
<td className='border-b px-3 py-2 w-1'>:</td>
|
||||
<td className='border-b px-3 py-2'>{data.no_spp}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='border-b px-3 py-2 font-semibold'>Kategori</td>
|
||||
<td className='border-b px-3 py-2 w-1'>:</td>
|
||||
<td className='border-b px-3 py-2'>{data.nama_tagihan}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='border-b px-3 py-2 font-semibold'>No SP2D</td>
|
||||
<td className='border-b px-3 py-2 w-1'>:</td>
|
||||
<td className='border-b px-3 py-2'>{dataFilter[0].NO_SP2D}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='border-b px-3 py-2 font-semibold'>Tanggal SP2D</td>
|
||||
<td className='border-b px-3 py-2 w-1'>:</td>
|
||||
<td className='border-b px-3 py-2'>
|
||||
{dataFilter[0].TGL_SP2D ? moment(dataFilter[0].TGL_SP2D).format('DD MMMM YYYY') : '-'}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='border-b px-3 py-2 font-semibold'>No SPM</td>
|
||||
<td className='border-b px-3 py-2 w-1'>:</td>
|
||||
<td className='border-b px-3 py-2'>{dataFilter[0].NO_SPM}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='border-b px-3 py-2 font-semibold'>Tanggal SPM</td>
|
||||
<td className='border-b px-3 py-2 w-1'>:</td>
|
||||
<td className='border-b px-3 py-2'>
|
||||
{dataFilter[0].TGL_SPM ? moment(dataFilter[0].TGL_SPM).format('DD MMMM YYYY') : '-'}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div className='block h-72 overflow-y-scroll'>
|
||||
<table style={{ width: '100%' }}>
|
||||
<thead className='bg-green-200'>
|
||||
<tr>
|
||||
<th className='border px-3 py-2 font-semibold'>#</th>
|
||||
<th className='border px-3 py-2 font-semibold'>Tanggal</th>
|
||||
<th className='border px-3 py-2 font-semibold'>Keterangan</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.history.map((value, index) => {
|
||||
return (
|
||||
<tr key={index}>
|
||||
<td className='border px-3 py-2'>{index + 1}</td>
|
||||
<td className='border px-3 py-2'>{moment(value.tanggal).format('DD MMM YYYY - HH:mm')}</td>
|
||||
<td className={`border px-3 py-2 ${value.status === 'Berkas dikembalikan' && 'text-red-500'}`}>
|
||||
{value.status}
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className='text-xl font-semibold mb-1'>Catatan</p>
|
||||
<TabView>
|
||||
{data.details.map((value, index) => {
|
||||
return (
|
||||
<TabPanel header={`Tagihan ${index + 1}`} key={index}>
|
||||
<table className='w-full mb-7'>
|
||||
<thead className='bg-red-200'>
|
||||
<tr>
|
||||
<th className='border px-3 py-2 font-semibold lg:text-sm'>No Tagihan</th>
|
||||
<th className='border px-3 py-2 font-semibold lg:text-sm'>Akun</th>
|
||||
<th className='border px-3 py-2 font-semibold lg:text-sm'>Untuk Keperluan</th>
|
||||
<th className='border px-3 py-2 font-semibold lg:text-sm'>Status Tagihan</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className='border px-3 py-2 lg:text-sm'>{value.nomor_tagihan}</td>
|
||||
<td className='border px-3 py-2 lg:text-sm'>{value.akun_name}</td>
|
||||
<td className='border px-3 py-2 lg:text-sm'>{value.keperluan}</td>
|
||||
<td className='border px-3 py-2 lg:text-sm'>-</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<TabView>
|
||||
{value.syarat.map((valueSyarat, indexSyarat) => {
|
||||
return (
|
||||
<TabPanel header={valueSyarat.nama} key={indexSyarat}>
|
||||
<table className='w-full'>
|
||||
<thead className='bg-blue-200'>
|
||||
<tr>
|
||||
<th className='border px-3 py-2 font-semibold lg:text-sm'>Catatan Verif</th>
|
||||
<th className='border px-3 py-2 font-semibold lg:text-sm'>Catatan Bendahara</th>
|
||||
<th className='border px-3 py-2 font-semibold lg:text-sm'>Catatan SPM</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className='border px-3 py-2 lg:text-sm'>{valueSyarat.verif_note}</td>
|
||||
<td className='border px-3 py-2 lg:text-sm'>{valueSyarat.bendahara_note}</td>
|
||||
<td className='border px-3 py-2 lg:text-sm'>{valueSyarat.spm_note}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</TabPanel>
|
||||
)
|
||||
})}
|
||||
</TabView>
|
||||
</TabPanel>
|
||||
)
|
||||
})}
|
||||
</TabView>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
import { AkunCreate, AkunUpdate } from '@/services/referensi/akun-service'
|
||||
import { useFormik } from 'formik'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Dialog } from 'primereact/dialog'
|
||||
import { InputText } from 'primereact/inputtext'
|
||||
|
||||
export default function FormAkun({ dialogForm, setDialogForm, dataEdit, setDataEdit, refresh, toast }) {
|
||||
const formik = useFormik({
|
||||
initialValues: {
|
||||
akun_id: dataEdit.akun_id || '',
|
||||
nama: dataEdit.nama || '',
|
||||
},
|
||||
validate: (data) => {
|
||||
let errors = {}
|
||||
if (!data.akun_id) errors.akun_id = 'Error akun is required.'
|
||||
if (!data.nama) errors.nama = 'Error name is required.'
|
||||
return errors
|
||||
},
|
||||
onSubmit: (data) => {
|
||||
if (data.akun_id) {
|
||||
AkunUpdate(data).then((res) => {
|
||||
if (res.status === 'success') {
|
||||
refresh(Math.random)
|
||||
formik.resetForm()
|
||||
setDataEdit([])
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
AkunCreate(data).then((res) => {
|
||||
if (res.status === 'success') {
|
||||
refresh(Math.random)
|
||||
formik.resetForm()
|
||||
setDataEdit([])
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const isFieldValid = (field) => !!(formik.touched[field] && formik.errors[field])
|
||||
const errorFieldMessage = (field) => {
|
||||
return (
|
||||
isFieldValid(field) && (
|
||||
<small className='p-error' style={{ fontSize: '11px' }}>
|
||||
{formik.errors[field]}
|
||||
</small>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const dialogFooter = (
|
||||
<div className='flex justify-end'>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Cancel'
|
||||
className='p-button-sm p-button-secondary'
|
||||
onClick={() => {
|
||||
formik.resetForm()
|
||||
setDialogForm(false)
|
||||
}}
|
||||
/>
|
||||
<Button type='submit' label='Save' className='p-button-sm p-button-primary' onClick={formik.handleSubmit} />
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
header={formik.values.akun_id ? 'Update Akun' : 'Add Akun'}
|
||||
className='p-dialog-form'
|
||||
visible={dialogForm}
|
||||
draggable={false}
|
||||
resizable={false}
|
||||
position={'center'}
|
||||
closable={false}
|
||||
style={{ width: '30vw' }}
|
||||
footer={dialogFooter}
|
||||
>
|
||||
<form onSubmit={formik.handleSubmit}>
|
||||
<div className='p-fluid grid'>
|
||||
<div className='field col-12 md:col-12 mb-3'>
|
||||
<label className='block'>Nomor Akun</label>
|
||||
<InputText
|
||||
className='p-inputtext-sm'
|
||||
name='akun_id'
|
||||
value={formik.values.akun_id}
|
||||
onChange={formik.handleChange}
|
||||
placeholder='Nomor Akun'
|
||||
/>
|
||||
{errorFieldMessage('akun_id')}
|
||||
</div>
|
||||
<div className='field col-12 md:col-12 mb-0'>
|
||||
<label className='block'>Nama Akun</label>
|
||||
<InputText
|
||||
className='p-inputtext-sm'
|
||||
name='nama'
|
||||
value={formik.values.nama}
|
||||
onChange={formik.handleChange}
|
||||
placeholder='Nama Akun'
|
||||
/>
|
||||
{errorFieldMessage('nama')}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
import { JamKerjaCreate } from '@/services/manajemen/jam-kerja-service'
|
||||
import { Formik } from 'formik'
|
||||
import moment from 'moment'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Calendar } from 'primereact/calendar'
|
||||
import { Dialog } from 'primereact/dialog'
|
||||
import { MultiSelect } from 'primereact/multiselect'
|
||||
|
||||
const dbHari = [
|
||||
{ name: 'Senin', code: 'senin' },
|
||||
{ name: 'Selasa', code: 'selasa' },
|
||||
{ name: 'Rabu', code: 'rabu' },
|
||||
{ name: 'Kamis', code: 'kamis' },
|
||||
{ name: 'Jumat', code: 'jumat' },
|
||||
{ name: 'Sabtu', code: 'sabtu' },
|
||||
{ name: 'Minggu', code: 'minggu' },
|
||||
]
|
||||
|
||||
export default function FormJamKerja({ dialogForm, setDialogForm, dataEdit, toast, refresh }) {
|
||||
return (
|
||||
<>
|
||||
<Formik
|
||||
initialValues={{
|
||||
user_id: dataEdit.user_id || '',
|
||||
hari: dataEdit.hari || null,
|
||||
jam_masuk: dataEdit.jam_masuk || null,
|
||||
jam_keluar: dataEdit.jam_keluar || null,
|
||||
}}
|
||||
onSubmit={(data, { setSubmitting }) => {
|
||||
Object.assign(data, {
|
||||
jam_masuk: moment(data.jam_masuk).format('HH:mm'),
|
||||
jam_keluar: moment(data.jam_keluar).format('HH:mm'),
|
||||
})
|
||||
let time = data.jam_masuk + '-' + data.jam_keluar
|
||||
let days = ''
|
||||
let panjang_array = data.hari.length
|
||||
data.hari.map((values, i) => {
|
||||
let plus1 = i + 1
|
||||
let koma = plus1 < panjang_array ? ',' : ''
|
||||
days += values.code + koma
|
||||
})
|
||||
|
||||
let dataKirim = { days, time }
|
||||
JamKerjaCreate(dataKirim).then((res) => {
|
||||
if (res.status === 'success') {
|
||||
setDialogForm(false)
|
||||
refresh(Math.random)
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
setDialogForm(false)
|
||||
refresh(Math.random)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
setSubmitting(false)
|
||||
}}
|
||||
validateOnChange={false}
|
||||
>
|
||||
{({ values, handleChange, handleSubmit, isSubmitting, resetForm }) => {
|
||||
const dialogFooter = (
|
||||
<div className='flex justify-end'>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Cancel'
|
||||
className='p-button-sm p-button-secondary'
|
||||
onClick={() => {
|
||||
resetForm()
|
||||
setDialogForm(false)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Save'
|
||||
className='p-button-sm p-button-primary'
|
||||
onClick={handleSubmit}
|
||||
loading={isSubmitting}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
return (
|
||||
<Dialog
|
||||
header={values.user_id ? 'Update Jam Kerja' : 'Add Jam Kerja'}
|
||||
className='p-dialog-form'
|
||||
visible={dialogForm}
|
||||
draggable={false}
|
||||
resizable={false}
|
||||
position={'center'}
|
||||
closable={false}
|
||||
style={{ width: '30vw' }}
|
||||
footer={dialogFooter}
|
||||
>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className='p-fluid grid'>
|
||||
<div className='field col-12 md:col-12 mb-2'>
|
||||
<label className='block'>Pilih Hari</label>
|
||||
<MultiSelect
|
||||
id='hari'
|
||||
value={values.hari}
|
||||
onChange={handleChange}
|
||||
options={dbHari}
|
||||
optionLabel='name'
|
||||
placeholder='Select Hari'
|
||||
maxSelectedLabels={3}
|
||||
className='w-full md:w-20rem'
|
||||
/>
|
||||
</div>
|
||||
<div className='grid grid-cols-2 gap-3'>
|
||||
<div className='mb-2'>
|
||||
<label className='block'>Jam Masuk</label>
|
||||
<Calendar
|
||||
id='jam_masuk'
|
||||
value={values.jam_masuk}
|
||||
onChange={handleChange}
|
||||
placeholder='Masukkan Jam Masuk'
|
||||
timeOnly
|
||||
hourFormat='24'
|
||||
/>
|
||||
</div>
|
||||
<div className='mb-2'>
|
||||
<label className='block'>Jam Keluar</label>
|
||||
<Calendar
|
||||
id='jam_keluar'
|
||||
value={values.jam_keluar}
|
||||
onChange={handleChange}
|
||||
placeholder='Masukkan Jam Keluar'
|
||||
timeOnly
|
||||
hourFormat='24'
|
||||
minDate={values.jam_masuk}
|
||||
disabled={values.jam_masuk ? false : true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Dialog>
|
||||
)
|
||||
}}
|
||||
</Formik>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
import { JenisBelanjaCreate, JenisBelanjaUpdate } from '@/services/referensi/jenisBelanja-service'
|
||||
import { useFormik } from 'formik'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Dialog } from 'primereact/dialog'
|
||||
import { InputText } from 'primereact/inputtext'
|
||||
|
||||
export default function FormJenisBelanja({ dialogForm, setDialogForm, dataEdit, setDataEdit, refresh, toast }) {
|
||||
const formik = useFormik({
|
||||
initialValues: {
|
||||
ref_id: dataEdit.jenis_id || '',
|
||||
nama: dataEdit.nama || '',
|
||||
},
|
||||
validate: (data) => {
|
||||
let errors = {}
|
||||
if (!data.nama) errors.nama = 'Nama is required.'
|
||||
return errors
|
||||
},
|
||||
onSubmit: (data) => {
|
||||
if (data.ref_id) {
|
||||
JenisBelanjaUpdate(data).then((res) => {
|
||||
if (res.status === 'success') {
|
||||
refresh(Math.random)
|
||||
formik.resetForm()
|
||||
setDataEdit([])
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
JenisBelanjaCreate(data).then((res) => {
|
||||
if (res.status === 'success') {
|
||||
refresh(Math.random)
|
||||
formik.resetForm()
|
||||
setDataEdit([])
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const isFieldValid = (field) => !!(formik.touched[field] && formik.errors[field])
|
||||
const errorFieldMessage = (field) => {
|
||||
return (
|
||||
isFieldValid(field) && (
|
||||
<small className='p-error' style={{ fontSize: '11px' }}>
|
||||
{formik.errors[field]}
|
||||
</small>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const dialogFooter = (
|
||||
<div className='flex justify-end'>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Cancel'
|
||||
className='p-button-sm p-button-secondary'
|
||||
onClick={() => {
|
||||
formik.resetForm()
|
||||
setDialogForm(false)
|
||||
}}
|
||||
/>
|
||||
<Button type='submit' label='Save' className='p-button-sm p-button-primary' onClick={formik.handleSubmit} />
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
header={formik.values.ref_id ? 'Update Jenis Belanja' : 'Add Jenis Belanja'}
|
||||
className='p-dialog-form'
|
||||
visible={dialogForm}
|
||||
draggable={false}
|
||||
resizable={false}
|
||||
position={'center'}
|
||||
closable={false}
|
||||
style={{ width: '30vw' }}
|
||||
footer={dialogFooter}
|
||||
>
|
||||
<form onSubmit={formik.handleSubmit}>
|
||||
<div className='p-fluid grid'>
|
||||
<div className='field col-12 md:col-12 mb-0'>
|
||||
<label className='block'>Nama</label>
|
||||
<InputText
|
||||
className='p-inputtext-sm'
|
||||
name='nama'
|
||||
value={formik.values.nama}
|
||||
onChange={formik.handleChange}
|
||||
placeholder='Masukkan nama jenis belanja'
|
||||
/>
|
||||
{errorFieldMessage('nama')}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
import { JenisBelanjaList } from '@/services/referensi/jenisBelanja-service'
|
||||
import { JenisKegiatanCreate, JenisKegiatanUpdate } from '@/services/referensi/jenisKegiatan-service'
|
||||
import { useFormik } from 'formik'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Dialog } from 'primereact/dialog'
|
||||
import { Dropdown } from 'primereact/dropdown'
|
||||
import { InputText } from 'primereact/inputtext'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
export default function FormJenisKegiatan({ dialogForm, setDialogForm, dataEdit, setDataEdit, refresh, toast }) {
|
||||
const [optionJenisBelanja, setOptionJenisBelanja] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
JenisBelanjaList({ draw: 0 }).then((res) => setOptionJenisBelanja(res.data))
|
||||
}, [])
|
||||
|
||||
const formik = useFormik({
|
||||
initialValues: {
|
||||
ref_id: dataEdit.jenis_id || '',
|
||||
nama: dataEdit.nama || '',
|
||||
jenis_belanja_id: dataEdit.jenis_belanja_id || '',
|
||||
},
|
||||
validate: (data) => {
|
||||
let errors = {}
|
||||
if (!data.nama) errors.nama = 'Nama is required.'
|
||||
if (!data.jenis_belanja_id) errors.jenis_belanja_id = 'Jenis Belanja is required.'
|
||||
return errors
|
||||
},
|
||||
onSubmit: (data) => {
|
||||
if (data.ref_id) {
|
||||
JenisKegiatanUpdate(data).then((res) => {
|
||||
if (res.status === 'success') {
|
||||
refresh(Math.random)
|
||||
formik.resetForm()
|
||||
setDataEdit([])
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
JenisKegiatanCreate(data).then((res) => {
|
||||
if (res.status === 'success') {
|
||||
refresh(Math.random)
|
||||
formik.resetForm()
|
||||
setDataEdit([])
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const isFieldValid = (field) => !!(formik.touched[field] && formik.errors[field])
|
||||
const errorFieldMessage = (field) => {
|
||||
return (
|
||||
isFieldValid(field) && (
|
||||
<small className='p-error' style={{ fontSize: '11px' }}>
|
||||
{formik.errors[field]}
|
||||
</small>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const dialogFooter = (
|
||||
<div className='flex justify-end'>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Cancel'
|
||||
className='p-button-sm p-button-secondary'
|
||||
onClick={() => {
|
||||
formik.resetForm()
|
||||
setDialogForm(false)
|
||||
}}
|
||||
/>
|
||||
<Button type='submit' label='Save' className='p-button-sm p-button-primary' onClick={formik.handleSubmit} />
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
header={formik.values.ref_id ? 'Update Jenis Kegiatan' : 'Add Jenis Kegiatan'}
|
||||
className='p-dialog-form'
|
||||
visible={dialogForm}
|
||||
draggable={false}
|
||||
resizable={false}
|
||||
position={'center'}
|
||||
closable={false}
|
||||
style={{ width: '30vw' }}
|
||||
footer={dialogFooter}
|
||||
>
|
||||
<form onSubmit={formik.handleSubmit}>
|
||||
<div className='p-fluid grid gap-y-3'>
|
||||
<div className='field col-12 md:col-12'>
|
||||
<label className='block'>Nama</label>
|
||||
<InputText
|
||||
className='p-inputtext-sm'
|
||||
name='nama'
|
||||
value={formik.values.nama}
|
||||
onChange={formik.handleChange}
|
||||
placeholder='Masukkan nama jenis kegiatan'
|
||||
/>
|
||||
{errorFieldMessage('nama')}
|
||||
</div>
|
||||
<div className='field col-12 md:col-4'>
|
||||
<label className='block'>Jenis Belanja</label>
|
||||
<Dropdown
|
||||
panelClassName='p-dropdown-form'
|
||||
name='jenis_belanja_id'
|
||||
options={optionJenisBelanja}
|
||||
optionLabel='nama'
|
||||
optionValue='jenis_id'
|
||||
value={formik.values.jenis_belanja_id}
|
||||
onChange={formik.handleChange}
|
||||
showClear={formik.values.jenis_belanja_id ? true : false}
|
||||
scrollHeight='200px'
|
||||
placeholder='Select Jenis Belanja'
|
||||
/>
|
||||
{errorFieldMessage('jenis_belanja_id')}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
import { JenisTagihanCreate, JenisTagihanUpdate } from '@/services/referensi/jenisTagihan-service'
|
||||
import { useFormik } from 'formik'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Dialog } from 'primereact/dialog'
|
||||
import { InputText } from 'primereact/inputtext'
|
||||
|
||||
export default function FormJenisTagihan({ dialogForm, setDialogForm, dataEdit, setDataEdit, refresh, toast }) {
|
||||
const formik = useFormik({
|
||||
initialValues: {
|
||||
ref_id: dataEdit.jenis_id || '',
|
||||
nama: dataEdit.nama || '',
|
||||
},
|
||||
validate: (data) => {
|
||||
let errors = {}
|
||||
if (!data.nama) errors.nama = 'Nama is required.'
|
||||
return errors
|
||||
},
|
||||
onSubmit: (data) => {
|
||||
if (data.ref_id) {
|
||||
JenisTagihanUpdate(data).then((res) => {
|
||||
if (res.status === 'success') {
|
||||
refresh(Math.random)
|
||||
formik.resetForm()
|
||||
setDataEdit([])
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
JenisTagihanCreate(data).then((res) => {
|
||||
if (res.status === 'success') {
|
||||
refresh(Math.random)
|
||||
formik.resetForm()
|
||||
setDataEdit([])
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const isFieldValid = (field) => !!(formik.touched[field] && formik.errors[field])
|
||||
const errorFieldMessage = (field) => {
|
||||
return (
|
||||
isFieldValid(field) && (
|
||||
<small className='p-error' style={{ fontSize: '11px' }}>
|
||||
{formik.errors[field]}
|
||||
</small>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const dialogFooter = (
|
||||
<div className='flex justify-end'>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Cancel'
|
||||
className='p-button-sm p-button-secondary'
|
||||
onClick={() => {
|
||||
formik.resetForm()
|
||||
setDialogForm(false)
|
||||
}}
|
||||
/>
|
||||
<Button type='submit' label='Save' className='p-button-sm p-button-primary' onClick={formik.handleSubmit} />
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
header={formik.values.ref_id ? 'Update Jenis Tagihan' : 'Add Jenis Tagihan'}
|
||||
className='p-dialog-form'
|
||||
visible={dialogForm}
|
||||
draggable={false}
|
||||
resizable={false}
|
||||
position={'center'}
|
||||
closable={false}
|
||||
style={{ width: '30vw' }}
|
||||
footer={dialogFooter}
|
||||
>
|
||||
<form onSubmit={formik.handleSubmit}>
|
||||
<div className='p-fluid grid'>
|
||||
<div className='field col-12 md:col-12 mb-0'>
|
||||
<label className='block'>Nama</label>
|
||||
<InputText
|
||||
className='p-inputtext-sm'
|
||||
name='nama'
|
||||
value={formik.values.nama}
|
||||
onChange={formik.handleChange}
|
||||
placeholder='Masukkan nama jenis tagihan'
|
||||
/>
|
||||
{errorFieldMessage('nama')}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
import { JenisBelanjaList } from '@/services/referensi/jenisBelanja-service'
|
||||
import { JenisKegiatanList } from '@/services/referensi/jenisKegiatan-service'
|
||||
import { JenisTagihanList } from '@/services/referensi/jenisTagihan-service'
|
||||
import { PersyaratanCreate, PersyaratanUpdate } from '@/services/referensi/persyaratan-service'
|
||||
import { FieldArray, Formik } from 'formik'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Dialog } from 'primereact/dialog'
|
||||
import { Dropdown } from 'primereact/dropdown'
|
||||
import { InputText } from 'primereact/inputtext'
|
||||
import { InputTextarea } from 'primereact/inputtextarea'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
export default function FormPersyaratan({ dialogForm, setDialogForm, dataEdit, setDataEdit, refresh, toast }) {
|
||||
const [optionJenisTagihan, setOptionJenisTagihan] = useState([])
|
||||
const [optionJenisBelanja, setOptionJenisBelanja] = useState([])
|
||||
const [optionJenisKegiatan, setOptionJenisKegiatan] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
JenisTagihanList({ draw: 0 }).then((res) => setOptionJenisTagihan(res.data))
|
||||
JenisBelanjaList({ draw: 0 }).then((res) => setOptionJenisBelanja(res.data))
|
||||
JenisKegiatanList({ draw: 0 }).then((res) => setOptionJenisKegiatan(res.data))
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Formik
|
||||
initialValues={{
|
||||
syarat_id: dataEdit.syarat_id || '',
|
||||
jenis_tagihan: dataEdit.jenis_tagihan_id || '',
|
||||
jenis_belanja: dataEdit.jenis_belanja_id || '',
|
||||
jenis_kegiatan: dataEdit.jenis_kegiatan_id || '',
|
||||
syarat: [{}],
|
||||
}}
|
||||
validate={(data) => {
|
||||
let errors = {}
|
||||
if (!data.jenis_tagihan) errors.jenis_tagihan = 'Jenis Tagihan is required.'
|
||||
if (!data.jenis_belanja) errors.jenis_belanja = 'Jenis Belanja is required.'
|
||||
if (!data.jenis_kegiatan) errors.jenis_kegiatan = 'Jenis Kegiatan is required.'
|
||||
// if (!data.syarat) errors.syarat = 'Syarat Dokumen is required.'
|
||||
// if (!data.keterangan) errors.keterangan = 'Keterangan is required.'
|
||||
return errors
|
||||
}}
|
||||
onSubmit={(data) => {
|
||||
if (data.syarat_id) {
|
||||
PersyaratanUpdate(data).then((res) => {
|
||||
if (res.status === 'success') {
|
||||
refresh(Math.random)
|
||||
setDataEdit([])
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
PersyaratanCreate(data).then((res) => {
|
||||
if (res.status === 'success') {
|
||||
refresh(Math.random)
|
||||
setDataEdit([])
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}}
|
||||
validateOnChange={false}
|
||||
>
|
||||
{({ values, errors, handleChange, handleSubmit, isSubmitting, resetForm }) => {
|
||||
const dialogFooter = (
|
||||
<div className='flex justify-end'>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Cancel'
|
||||
className='p-button-sm p-button-secondary'
|
||||
onClick={() => {
|
||||
resetForm()
|
||||
setDialogForm(false)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Save'
|
||||
className='p-button-sm p-button-primary'
|
||||
onClick={handleSubmit}
|
||||
loading={isSubmitting}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
header={values.syarat_id ? 'Update Persyaratan' : 'Add Persyaratan'}
|
||||
className='p-dialog-form'
|
||||
visible={dialogForm}
|
||||
draggable={false}
|
||||
resizable={false}
|
||||
position={'center'}
|
||||
closable={false}
|
||||
style={{ width: '45vw' }}
|
||||
footer={dialogFooter}
|
||||
>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className='p-fluid grid gap-y-2'>
|
||||
<div className='field col-12 md:col-4'>
|
||||
<label className='block'>Jenis Tagihan</label>
|
||||
<Dropdown
|
||||
panelClassName='p-dropdown-form'
|
||||
name='jenis_tagihan'
|
||||
options={optionJenisTagihan}
|
||||
optionLabel='nama'
|
||||
optionValue='jenis_id'
|
||||
value={values.jenis_tagihan}
|
||||
onChange={handleChange}
|
||||
showClear={values.jenis_tagihan ? true : false}
|
||||
scrollHeight='200px'
|
||||
placeholder='Select Jenis Tagihan'
|
||||
/>
|
||||
{errors.jenis_tagihan && <div className='text-red-500 text-xs'>{errors.jenis_tagihan}</div>}
|
||||
</div>
|
||||
<div className='field col-12 md:col-4'>
|
||||
<label className='block'>Jenis Belanja</label>
|
||||
<Dropdown
|
||||
panelClassName='p-dropdown-form'
|
||||
name='jenis_belanja'
|
||||
options={optionJenisBelanja}
|
||||
optionLabel='nama'
|
||||
optionValue='jenis_id'
|
||||
value={values.jenis_belanja}
|
||||
onChange={handleChange}
|
||||
showClear={values.jenis_belanja ? true : false}
|
||||
scrollHeight='200px'
|
||||
placeholder='Select Jenis Belanja'
|
||||
/>
|
||||
{errors.jenis_belanja && <div className='text-red-500 text-xs'>{errors.jenis_belanja}</div>}
|
||||
</div>
|
||||
<div className='field col-12 md:col-4'>
|
||||
<label className='block'>Jenis Kegiatan</label>
|
||||
<Dropdown
|
||||
panelClassName='p-dropdown-form'
|
||||
name='jenis_kegiatan'
|
||||
options={optionJenisKegiatan}
|
||||
optionLabel='nama'
|
||||
optionValue='jenis_id'
|
||||
value={values.jenis_kegiatan}
|
||||
onChange={handleChange}
|
||||
showClear={values.jenis_kegiatan ? true : false}
|
||||
scrollHeight='200px'
|
||||
placeholder='Select Jenis Kegiatan'
|
||||
/>
|
||||
{errors.jenis_kegiatan && <div className='text-red-500 text-xs'>{errors.jenis_kegiatan}</div>}
|
||||
</div>
|
||||
<FieldArray
|
||||
name='syarat'
|
||||
render={(arrayHelpers) => (
|
||||
<div className=''>
|
||||
<label className='block'>Syarat</label>
|
||||
<div className='flex flex-col gap-1'>
|
||||
{values.syarat.map((syarat, index) => (
|
||||
<div key={index} className='field col-12 flex gap-2 items-center'>
|
||||
<div className='border p-3 rounded shadow my-2 w-full flex flex-col gap-2'>
|
||||
<div className='flex flex-col gap-1'>
|
||||
<label className='block'>Nama Syarat {index + 1}</label>
|
||||
<InputText
|
||||
className='p-inputtext-sm'
|
||||
name={`syarat.${index}.syarat`}
|
||||
value={values.syarat[index].syarat}
|
||||
onChange={handleChange}
|
||||
placeholder='Masukkan Syarat Dokumen'
|
||||
/>
|
||||
{errors.syarat && errors.syarat[index] && (
|
||||
<div className='text-red-500 text-xs'>{errors.syarat[index].syarat}</div>
|
||||
)}
|
||||
</div>
|
||||
<div className='flex flex-col gap-1'>
|
||||
<label className='block'>Keterangan Syarat {index + 1}</label>
|
||||
<InputTextarea
|
||||
name={`syarat.${index}.keterangan`}
|
||||
rows={3}
|
||||
cols={30}
|
||||
value={values.syarat[index].keterangan}
|
||||
onChange={handleChange}
|
||||
placeholder='Masukkan Keterangan'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{index > 0 && (
|
||||
<Button
|
||||
icon='pi pi-times'
|
||||
className='p-button-danger p-button-sm'
|
||||
onClick={() => arrayHelpers.remove(index)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<div className='w-fit relative left-1/2 -translate-x-1/2'>
|
||||
<Button
|
||||
type='button'
|
||||
icon='pi pi-plus'
|
||||
className='p-button-sm'
|
||||
label='Tambah Syarat'
|
||||
onClick={() => arrayHelpers.push({ syarat: '', keterangan: '' })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</Dialog>
|
||||
)
|
||||
}}
|
||||
</Formik>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
import { RoleCreate, RoleUpdate } from '@/services/manajemen/role-service'
|
||||
import { useFormik } from 'formik'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Dialog } from 'primereact/dialog'
|
||||
import { InputText } from 'primereact/inputtext'
|
||||
import { Tree } from 'primereact/tree'
|
||||
|
||||
export default function FormRole({ dialogForm, setDialogForm, dataEdit, setDataEdit, refresh, toast }) {
|
||||
const formik = useFormik({
|
||||
initialValues: {
|
||||
role_id: dataEdit.role_id || '',
|
||||
nama: dataEdit.nama || '',
|
||||
access: dataEdit.access || [],
|
||||
},
|
||||
validate: (data) => {
|
||||
let errors = {}
|
||||
if (!data.nama) errors.nama = 'Nama is required.'
|
||||
if (!data.access) errors.access = 'Nama is required.'
|
||||
return errors
|
||||
},
|
||||
onSubmit: (data) => {
|
||||
Object.assign(data, { access: JSON.stringify(data.access) })
|
||||
if (data.role_id) {
|
||||
RoleUpdate(data).then((res) => {
|
||||
if (res.status === 'success') {
|
||||
refresh(Math.random)
|
||||
formik.resetForm()
|
||||
setDataEdit([])
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
RoleCreate(data).then((res) => {
|
||||
if (res.status === 'success') {
|
||||
refresh(Math.random)
|
||||
formik.resetForm()
|
||||
setDataEdit([])
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const isFieldValid = (field) => !!(formik.touched[field] && formik.errors[field])
|
||||
const errorFieldMessage = (field) => {
|
||||
return (
|
||||
isFieldValid(field) && (
|
||||
<small className='p-error' style={{ fontSize: '11px' }}>
|
||||
{formik.errors[field]}
|
||||
</small>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const dialogFooter = (
|
||||
<div className='flex justify-end'>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Cancel'
|
||||
className='p-button-sm p-button-secondary'
|
||||
onClick={() => {
|
||||
formik.resetForm()
|
||||
setDialogForm(false)
|
||||
}}
|
||||
/>
|
||||
<Button type='submit' label='Save' className='p-button-sm p-button-primary' onClick={formik.handleSubmit} />
|
||||
</div>
|
||||
)
|
||||
|
||||
const data = [
|
||||
{
|
||||
key: 'dashboard',
|
||||
label: 'Dashboard',
|
||||
icon: 'pi pi-fw pi-home',
|
||||
},
|
||||
{
|
||||
key: 'referensi',
|
||||
label: 'Referensi',
|
||||
icon: 'pi pi-fw pi-database',
|
||||
children: [
|
||||
{ key: 'referensi/jenis-tagihan', label: 'Jenis Tagihan' },
|
||||
{ key: 'referensi/jenis-belanja', label: 'Jenis Belanja' },
|
||||
{ key: 'referensi/jenis-kegiatan', label: 'Jenis Kegiatan' },
|
||||
{ key: 'referensi/akun', label: 'Akun' },
|
||||
{ key: 'referensi/persyaratan', label: 'Persyaratan' },
|
||||
{ key: 'referensi/unit-kerja', label: 'Unit Kerja' },
|
||||
{ key: 'referensi/verifikator', label: 'Verifikator' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'pengajuan-tagihan',
|
||||
label: 'Pengajuan Tagihan',
|
||||
icon: 'pi pi-fw pi-dollar',
|
||||
},
|
||||
{
|
||||
key: 'laporan',
|
||||
label: 'Laporan',
|
||||
icon: 'pi pi-fw pi-chart-line',
|
||||
children: [
|
||||
{ key: 'laporan/per-user', label: 'Per User (Verifikator, kasub verif)' },
|
||||
// { key: 'laporan/approval', label: 'Approval (Kabag verif, bendahara, ppspm)' },
|
||||
{ key: 'laporan/rekap-bulanan-per-user', label: 'Rekapitulasi bulanan per user' },
|
||||
{ key: 'laporan/rekap-verif-per-bulan', label: 'Rekapitulasi hasil verifikasi per bulan' },
|
||||
{ key: 'laporan/rekap-per-unit-kerja', label: 'Rekapitulasi per unit kerja' },
|
||||
{ key: 'laporan/rekap-per-jenis-belanja', label: 'Rekapitulasi per jenis belanja (BAS)' },
|
||||
{ key: 'laporan/verif-per-bulan', label: 'Hasil verifikasi per bulan (detail)' },
|
||||
{
|
||||
key: 'laporan/pengendalian-waktu-penyelesaian',
|
||||
label: 'Pengendalian waktu penyelesaian tagihan per unit per bulan',
|
||||
},
|
||||
{
|
||||
key: 'laporan/pengendalian-waktu-penyelesaian-detail',
|
||||
label: 'Pengendalian waktu penyelesaian tagihan per unit per bulan (detail)',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'otorisasi',
|
||||
label: 'Otorisasi',
|
||||
icon: 'pi pi-fw pi-lock',
|
||||
children: [
|
||||
{ key: 'otorisasi/verifikasi', label: 'verifikasi' },
|
||||
{ key: 'otorisasi/rekomendasi', label: 'rekomendasi' },
|
||||
{ key: 'otorisasi/persetujuan', label: 'persetujuan' },
|
||||
{ key: 'otorisasi/pengesahan', label: 'pengesahan' },
|
||||
{ key: 'otorisasi/spm', label: 'spm' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'manajemen',
|
||||
label: 'Manajemen',
|
||||
icon: 'pi pi-fw pi-database',
|
||||
children: [
|
||||
{ key: 'manajemen/user', label: 'user' },
|
||||
{ key: 'manajemen/role', label: 'role' },
|
||||
{ key: 'manajemen/jam-kerja', label: 'jam kerja' },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
header={formik.values.role_id ? 'Update Role' : 'Add Role'}
|
||||
className='p-dialog-form'
|
||||
visible={dialogForm}
|
||||
draggable={false}
|
||||
resizable={false}
|
||||
position={'center'}
|
||||
closable={false}
|
||||
style={{ width: '60vw' }}
|
||||
footer={dialogFooter}
|
||||
>
|
||||
<form onSubmit={formik.handleSubmit}>
|
||||
<div className='grid grid-cols-2 gap-x-3'>
|
||||
{/* KIRI */}
|
||||
<div className='kiri'>
|
||||
<div className='p-fluid grid'>
|
||||
<div className='field col-12 md:col-12 mb-0'>
|
||||
<label className='block'>List menu</label>
|
||||
<Tree
|
||||
name='access'
|
||||
value={data}
|
||||
selectionMode='checkbox'
|
||||
selectionKeys={formik.values.access}
|
||||
onSelectionChange={(e) => {
|
||||
formik.setFieldValue('access', e.value)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* KANAN */}
|
||||
<div className='kanan'>
|
||||
<div className='p-fluid grid'>
|
||||
<div className='field col-12 md:col-12 mb-0'>
|
||||
<label className='block'>Nama Role</label>
|
||||
<InputText
|
||||
className='p-inputtext-sm'
|
||||
name='nama'
|
||||
value={formik.values.nama}
|
||||
onChange={formik.handleChange}
|
||||
placeholder='Masukkan nama role'
|
||||
/>
|
||||
{errorFieldMessage('nama')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
import { TagihanCreate, TagihanUpdate } from '@/services/pengajuan-tagihan/tagihan-service'
|
||||
import { useFormik } from 'formik'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Dialog } from 'primereact/dialog'
|
||||
import { InputText } from 'primereact/inputtext'
|
||||
|
||||
export default function FormTagihan({ dialogForm, setDialogForm, dataEdit, setDataEdit, refresh, toast }) {
|
||||
const formik = useFormik({
|
||||
initialValues: {
|
||||
ref_id: dataEdit.ref_id || '',
|
||||
nama: dataEdit.nama || '',
|
||||
},
|
||||
validate: (data) => {
|
||||
let errors = {}
|
||||
if (!data.nama) errors.nama = 'Nama is required.'
|
||||
return errors
|
||||
},
|
||||
onSubmit: (data) => {
|
||||
if (data.ref_id) {
|
||||
TagihanUpdate(data).then((res) => {
|
||||
if (res.status === 'success') {
|
||||
refresh(Math.random)
|
||||
formik.resetForm()
|
||||
setDataEdit([])
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
TagihanCreate(data).then((res) => {
|
||||
if (res.status === 'success') {
|
||||
refresh(Math.random)
|
||||
formik.resetForm()
|
||||
setDataEdit([])
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const isFieldValid = (field) => !!(formik.touched[field] && formik.errors[field])
|
||||
const errorFieldMessage = (field) => {
|
||||
return (
|
||||
isFieldValid(field) && (
|
||||
<small className='p-error' style={{ fontSize: '11px' }}>
|
||||
{formik.errors[field]}
|
||||
</small>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const dialogFooter = (
|
||||
<div className='flex justify-end'>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Cancel'
|
||||
className='p-button-sm p-button-secondary'
|
||||
onClick={() => {
|
||||
formik.resetForm()
|
||||
setDialogForm(false)
|
||||
}}
|
||||
/>
|
||||
<Button type='submit' label='Save' className='p-button-sm p-button-primary' onClick={formik.handleSubmit} />
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
header={formik.values.ref_id ? 'Update Tagihan' : 'Add Tagihan'}
|
||||
className='p-dialog-form'
|
||||
visible={dialogForm}
|
||||
draggable={false}
|
||||
resizable={false}
|
||||
position={'center'}
|
||||
closable={false}
|
||||
style={{ width: '30vw' }}
|
||||
footer={dialogFooter}
|
||||
>
|
||||
<form onSubmit={formik.handleSubmit}>
|
||||
<div className='p-fluid grid'>
|
||||
<div className='field col-12 md:col-12 mb-0'>
|
||||
<label className='block'>Nama</label>
|
||||
<InputText
|
||||
className='p-inputtext-sm'
|
||||
name='nama'
|
||||
value={formik.values.nama}
|
||||
onChange={formik.handleChange}
|
||||
placeholder='Masukkan nama jenis tagihan'
|
||||
/>
|
||||
{errorFieldMessage('nama')}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
import { UnitKerjaCreate, UnitKerjaUpdate } from '@/services/referensi/unitKerja-service'
|
||||
import { useFormik } from 'formik'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Dialog } from 'primereact/dialog'
|
||||
import { InputText } from 'primereact/inputtext'
|
||||
|
||||
export default function FormUnitKerja({ dialogForm, setDialogForm, dataEdit, setDataEdit, refresh, toast }) {
|
||||
const formik = useFormik({
|
||||
initialValues: {
|
||||
ref_id: dataEdit.ref_id || '',
|
||||
unit_id: dataEdit.unit_id || '',
|
||||
nama: dataEdit.nama || '',
|
||||
bobot: dataEdit.bobot || '',
|
||||
},
|
||||
validate: (data) => {
|
||||
let errors = {}
|
||||
if (!data.unit_id) errors.unit_id = 'Kode Unit is required.'
|
||||
if (!data.nama) errors.nama = 'Nama is required.'
|
||||
if (!data.bobot) errors.bobot = 'Bobot is required.'
|
||||
return errors
|
||||
},
|
||||
onSubmit: (data) => {
|
||||
if (data.ref_id) {
|
||||
UnitKerjaUpdate(data).then((res) => {
|
||||
if (res.status === 'success') {
|
||||
refresh(Math.random)
|
||||
formik.resetForm()
|
||||
setDataEdit([])
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
UnitKerjaCreate(data).then((res) => {
|
||||
if (res.status === 'success') {
|
||||
refresh(Math.random)
|
||||
formik.resetForm()
|
||||
setDataEdit([])
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const isFieldValid = (field) => !!(formik.errors[field] && formik.touched[field])
|
||||
const errorFieldMessage = (field) => {
|
||||
return (
|
||||
isFieldValid(field) && (
|
||||
<small className='p-error' style={{ fontSize: '11px' }}>
|
||||
{formik.errors[field]}
|
||||
</small>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const dialogFooter = (
|
||||
<div className='flex justify-end'>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Cancel'
|
||||
className='p-button-sm p-button-secondary'
|
||||
onClick={() => {
|
||||
formik.resetForm()
|
||||
setDialogForm(false)
|
||||
}}
|
||||
/>
|
||||
<Button type='submit' label='Save' className='p-button-sm p-button-primary' onClick={formik.handleSubmit} />
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
header={formik.values.unit_id ? 'Update Jenis Tagihan' : 'Add Jenis Tagihan'}
|
||||
className='p-dialog-form'
|
||||
visible={dialogForm}
|
||||
draggable={false}
|
||||
resizable={false}
|
||||
position={'center'}
|
||||
closable={false}
|
||||
style={{ width: '30vw' }}
|
||||
footer={dialogFooter}
|
||||
>
|
||||
<form onSubmit={formik.handleSubmit}>
|
||||
<div className='p-fluid grid gap-y-3'>
|
||||
<div className='field col-12 md:col-12'>
|
||||
<label className='block'>Kode Unit</label>
|
||||
<InputText
|
||||
className='p-inputtext-sm'
|
||||
name='unit_id'
|
||||
value={formik.values.unit_id}
|
||||
onChange={formik.handleChange}
|
||||
placeholder='Masukkan Kode Unit'
|
||||
/>
|
||||
{errorFieldMessage('unit_id')}
|
||||
</div>
|
||||
<div className='field col-12 md:col-12'>
|
||||
<label className='block'>Nama</label>
|
||||
<InputText
|
||||
className='p-inputtext-sm'
|
||||
name='nama'
|
||||
value={formik.values.nama}
|
||||
onChange={formik.handleChange}
|
||||
placeholder='Masukkan nama unit kerja'
|
||||
/>
|
||||
{errorFieldMessage('nama')}
|
||||
</div>
|
||||
<div className='field col-12 md:col-12'>
|
||||
<label className='block'>Bobot</label>
|
||||
<InputText
|
||||
className='p-inputtext-sm'
|
||||
name='bobot'
|
||||
value={formik.values.bobot}
|
||||
onChange={formik.handleChange}
|
||||
placeholder='Masukkan bobot'
|
||||
/>
|
||||
{errorFieldMessage('bobot')}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
import { RoleList } from '@/services/manajemen/role-service'
|
||||
import { UserCreate, UserUpdate } from '@/services/manajemen/user-service'
|
||||
import { UnitKerjaList } from '@/services/referensi/unitKerja-service'
|
||||
import { useFormik } from 'formik'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Dialog } from 'primereact/dialog'
|
||||
import { Dropdown } from 'primereact/dropdown'
|
||||
import { InputText } from 'primereact/inputtext'
|
||||
import { Password } from 'primereact/password'
|
||||
import { useEffect, useState } from 'react'
|
||||
import * as Yup from 'yup'
|
||||
|
||||
export default function FormUser({ dialogForm, setDialogForm, dataEdit, setDataEdit, refresh, toast }) {
|
||||
const [optionUnitKerja, setOptionUnitKerja] = useState([])
|
||||
const [optionRole, setOptionRole] = useState([])
|
||||
const [showUnitKerja, setShowUnitKerja] = useState(true)
|
||||
|
||||
const SignupSchema = Yup.object().shape({
|
||||
name: Yup.string().min(2, 'Too Short!').max(50, 'Too Long!').required('Nama is required'),
|
||||
username: Yup.string().min(2, 'Too Short!').max(50, 'Too Long!').required('Username is required'),
|
||||
email: Yup.string().email('Invalid email').required('Email is Required'),
|
||||
password: Yup.string().when('old_password', {
|
||||
is: true,
|
||||
then: Yup.string()
|
||||
.required('Password is required')
|
||||
.matches(
|
||||
/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#%&])(?=.{8,})/,
|
||||
'Must Contain 8 Characters, One Uppercase, One Lowercase, One Number and One Special Case Character'
|
||||
),
|
||||
}),
|
||||
password_retype: Yup.string().when('old_password', {
|
||||
is: true,
|
||||
then: Yup.string()
|
||||
.oneOf([Yup.ref('password'), null], 'Passwords must match')
|
||||
.required('Password confirm is required'),
|
||||
}),
|
||||
// unit: Yup.string().required('Unit kerja is required'),
|
||||
role: Yup.string().required('Role is required'),
|
||||
})
|
||||
|
||||
const formik = useFormik({
|
||||
initialValues: {
|
||||
user_id: dataEdit.user_id || '',
|
||||
name: dataEdit.nama || '',
|
||||
username: dataEdit.username || '',
|
||||
email: dataEdit.email || '',
|
||||
password: '',
|
||||
password_retype: '',
|
||||
unit: dataEdit.unit || '',
|
||||
role: dataEdit.user_role || '',
|
||||
},
|
||||
validationSchema: SignupSchema,
|
||||
onSubmit: (data) => {
|
||||
if (data.user_id) {
|
||||
UserUpdate(data).then((res) => {
|
||||
if (res.status === 'success') {
|
||||
refresh(Math.random)
|
||||
formik.resetForm()
|
||||
setDataEdit([])
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
UserCreate(data).then((res) => {
|
||||
if (res.status === 'success') {
|
||||
refresh(Math.random)
|
||||
formik.resetForm()
|
||||
setDataEdit([])
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
UnitKerjaList({ draw: 0 }).then((res) => setOptionUnitKerja(res.data))
|
||||
RoleList().then((res) => setOptionRole(res.data))
|
||||
}, [])
|
||||
|
||||
const isFieldValid = (field) => !!(formik.touched[field] && formik.errors[field])
|
||||
const errorFieldMessage = (field) => {
|
||||
return (
|
||||
isFieldValid(field) && (
|
||||
<small className='p-error' style={{ fontSize: '11px' }}>
|
||||
{formik.errors[field]}
|
||||
</small>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const dialogFooter = (
|
||||
<div className='flex justify-end'>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Cancel'
|
||||
className='p-button-sm p-button-secondary'
|
||||
onClick={() => {
|
||||
formik.resetForm()
|
||||
setDialogForm(false)
|
||||
}}
|
||||
/>
|
||||
<Button type='submit' label='Save' className='p-button-sm p-button-primary' onClick={formik.handleSubmit} />
|
||||
</div>
|
||||
)
|
||||
|
||||
const header = <h6>Pick a password</h6>
|
||||
const footer = (
|
||||
<>
|
||||
<p className='mt-3'>Suggestions</p>
|
||||
<ul className='pl-2 ml-2 mt-0' style={{ lineHeight: '1.5' }}>
|
||||
<li>At least one lowercase</li>
|
||||
<li>At least one uppercase</li>
|
||||
<li>At least one numeric</li>
|
||||
<li>At least one special case character</li>
|
||||
<li>Minimum 8 characters</li>
|
||||
<li>If edit password not required</li>
|
||||
</ul>
|
||||
</>
|
||||
)
|
||||
|
||||
const handleRole = (e) => {
|
||||
if (e.target.value === 'ROL-a713f345-9b9a-453a-8f77-33fd8335078f') {
|
||||
setShowUnitKerja(false)
|
||||
} else {
|
||||
setShowUnitKerja(true)
|
||||
}
|
||||
|
||||
formik.setFieldValue('role', e.target.value)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
header={formik.values.user_id ? 'Update User' : 'Add User'}
|
||||
className='p-dialog-form'
|
||||
visible={dialogForm}
|
||||
draggable={false}
|
||||
resizable={false}
|
||||
position={'center'}
|
||||
closable={false}
|
||||
style={{ width: '30vw' }}
|
||||
footer={dialogFooter}
|
||||
>
|
||||
<form onSubmit={formik.handleSubmit}>
|
||||
<div className='kanan'>
|
||||
<div className='p-fluid grid'>
|
||||
<div className='field col-12 md:col-12 mb-2'>
|
||||
<label className='block'>Nama</label>
|
||||
<InputText
|
||||
className='p-inputtext-sm'
|
||||
name='name'
|
||||
value={formik.values.name}
|
||||
onChange={formik.handleChange}
|
||||
placeholder='Masukkan nama'
|
||||
/>
|
||||
{errorFieldMessage('name')}
|
||||
</div>
|
||||
<div className='field col-12 md:col-12 mb-2'>
|
||||
<label className='block'>Email</label>
|
||||
<InputText
|
||||
className='p-inputtext-sm'
|
||||
name='email'
|
||||
value={formik.values.email}
|
||||
onChange={formik.handleChange}
|
||||
placeholder='Masukkan email'
|
||||
/>
|
||||
{errorFieldMessage('email')}
|
||||
</div>
|
||||
<div className='field col-12 md:col-12 mb-2'>
|
||||
<label className='block'>Username</label>
|
||||
<InputText
|
||||
className='p-inputtext-sm'
|
||||
name='username'
|
||||
value={formik.values.username}
|
||||
onChange={formik.handleChange}
|
||||
placeholder='Masukkan username'
|
||||
/>
|
||||
{errorFieldMessage('username')}
|
||||
</div>
|
||||
<div className='field col-12 md:col-12 mb-2'>
|
||||
<label className='block'>Password</label>
|
||||
<Password
|
||||
name='password'
|
||||
value={formik.values.password}
|
||||
onChange={formik.handleChange}
|
||||
header={header}
|
||||
footer={footer}
|
||||
toggleMask
|
||||
/>
|
||||
{errorFieldMessage('password')}
|
||||
</div>
|
||||
<div className='field col-12 md:col-12 mb-2'>
|
||||
<label className='block'>Password Confirm</label>
|
||||
<Password
|
||||
name='password_retype'
|
||||
value={formik.values.password_retype}
|
||||
onChange={formik.handleChange}
|
||||
feedback={false}
|
||||
toggleMask
|
||||
/>
|
||||
{errorFieldMessage('password_retype')}
|
||||
</div>
|
||||
<div className='field col-12 md:col-4'>
|
||||
<label className='block'>Role</label>
|
||||
<Dropdown
|
||||
panelClassName='p-dropdown-form'
|
||||
name='role'
|
||||
options={optionRole}
|
||||
optionLabel='nama'
|
||||
optionValue='role_id'
|
||||
value={formik.values.role}
|
||||
onChange={(e) => handleRole(e)}
|
||||
showClear={formik.values.role ? true : false}
|
||||
scrollHeight='200px'
|
||||
placeholder='Select role'
|
||||
/>
|
||||
{errorFieldMessage('role')}
|
||||
</div>
|
||||
<div className='field col-12 md:col-4'>
|
||||
<label className='block'>Unit Kerja</label>
|
||||
<Dropdown
|
||||
panelClassName='p-dropdown-form'
|
||||
name='unit'
|
||||
options={optionUnitKerja}
|
||||
optionLabel='nama'
|
||||
optionValue='unit_id'
|
||||
value={formik.values.unit}
|
||||
onChange={formik.handleChange}
|
||||
showClear={formik.values.unit ? true : false}
|
||||
scrollHeight='200px'
|
||||
placeholder='Select unit kerja'
|
||||
disabled={showUnitKerja}
|
||||
/>
|
||||
{errorFieldMessage('unit')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import { useFormik } from 'formik'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Dialog } from 'primereact/dialog'
|
||||
import { InputText } from 'primereact/inputtext'
|
||||
|
||||
export default function FormVerifikasi({ dialogForm, setDialogForm, dataEdit }) {
|
||||
const formik = useFormik({
|
||||
initialValues: {
|
||||
ref_id: dataEdit.ref_id || '',
|
||||
nama: dataEdit.nama || '',
|
||||
},
|
||||
validate: (data) => {
|
||||
let errors = {}
|
||||
if (!data.nama) errors.nama = 'Nama is required.'
|
||||
return errors
|
||||
},
|
||||
onSubmit: (data) => {
|
||||
console.log(data)
|
||||
},
|
||||
})
|
||||
|
||||
const isFieldValid = (field) => !!(formik.touched[field] && formik.errors[field])
|
||||
const errorFieldMessage = (field) => {
|
||||
return (
|
||||
isFieldValid(field) && (
|
||||
<small className='p-error' style={{ fontSize: '11px' }}>
|
||||
{formik.errors[field]}
|
||||
</small>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const dialogFooter = (
|
||||
<div className='flex justify-end'>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Cancel'
|
||||
className='p-button-sm p-button-secondary'
|
||||
onClick={() => {
|
||||
formik.resetForm()
|
||||
setDialogForm(false)
|
||||
}}
|
||||
/>
|
||||
<Button type='submit' label='Save' className='p-button-sm p-button-primary' onClick={formik.handleSubmit} />
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
header={formik.values.ref_id ? 'Update Verifikasi' : 'Add Verifikasi'}
|
||||
className='p-dialog-form'
|
||||
visible={dialogForm}
|
||||
draggable={false}
|
||||
resizable={false}
|
||||
position={'center'}
|
||||
closable={false}
|
||||
style={{ width: '30vw' }}
|
||||
footer={dialogFooter}
|
||||
>
|
||||
<form onSubmit={formik.handleSubmit}>
|
||||
<div className='p-fluid grid'>
|
||||
<div className='field col-12 md:col-12 mb-0'>
|
||||
<label className='block'>Nama</label>
|
||||
<InputText
|
||||
className='p-inputtext-sm'
|
||||
name='nama'
|
||||
value={formik.values.nama}
|
||||
onChange={formik.handleChange}
|
||||
placeholder='Masukkan nama jenis tagihan'
|
||||
/>
|
||||
{errorFieldMessage('nama')}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import { UserList } from '@/services/manajemen/user-service'
|
||||
import { UnitKerjaList } from '@/services/referensi/unitKerja-service'
|
||||
import { VerifikatorCreate } from '@/services/referensi/verifikator-service'
|
||||
import { useFormik } from 'formik'
|
||||
import { Button } from 'primereact/button'
|
||||
import { Dialog } from 'primereact/dialog'
|
||||
import { Dropdown } from 'primereact/dropdown'
|
||||
import { MultiSelect } from 'primereact/multiselect'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
export default function FormVerifikator({ dialogForm, setDialogForm, dataEdit, setDataEdit, refresh, toast }) {
|
||||
const [ddVerifikator, setDdVerifikator] = useState([])
|
||||
const [ddUserList, setDdUserList] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
UnitKerjaList({ draw: 0 }).then((res) => {
|
||||
setDdVerifikator(res.data)
|
||||
})
|
||||
UserList({ draw: 0 }).then((res) => {
|
||||
setDdUserList(res.data)
|
||||
})
|
||||
}, [])
|
||||
|
||||
const formik = useFormik({
|
||||
initialValues: {
|
||||
unit_id: '',
|
||||
user_id: dataEdit.user_id || '',
|
||||
},
|
||||
validate: (data) => {
|
||||
let errors = {}
|
||||
if (!data.unit_id) errors.unit_id = 'Kode Unit is required.'
|
||||
if (!data.user_id) errors.user_id = 'Nama is required.'
|
||||
return errors
|
||||
},
|
||||
onSubmit: (data) => {
|
||||
VerifikatorCreate(data).then((res) => {
|
||||
if (res.status === 'success') {
|
||||
refresh(Math.random)
|
||||
formik.resetForm()
|
||||
setDataEdit([])
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'success',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
} else {
|
||||
setDialogForm(false)
|
||||
toast.current.show({
|
||||
severity: 'error',
|
||||
detail: res.message,
|
||||
closable: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
const isFieldValid = (field) => !!(formik.errors[field] && formik.touched[field])
|
||||
const errorFieldMessage = (field) => {
|
||||
return (
|
||||
isFieldValid(field) && (
|
||||
<small className='p-error' style={{ fontSize: '11px' }}>
|
||||
{formik.errors[field]}
|
||||
</small>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const dialogFooter = (
|
||||
<div className='flex justify-end'>
|
||||
<Button
|
||||
type='submit'
|
||||
label='Cancel'
|
||||
className='p-button-sm p-button-secondary'
|
||||
onClick={() => {
|
||||
formik.resetForm()
|
||||
setDialogForm(false)
|
||||
}}
|
||||
/>
|
||||
<Button type='submit' label='Save' className='p-button-sm p-button-primary' onClick={formik.handleSubmit} />
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
header={formik.values.ref_id ? 'Update Jenis Tagihan' : 'Add Jenis Tagihan'}
|
||||
className='p-dialog-form'
|
||||
visible={dialogForm}
|
||||
draggable={false}
|
||||
resizable={false}
|
||||
position={'center'}
|
||||
closable={false}
|
||||
style={{ width: '30vw' }}
|
||||
footer={dialogFooter}
|
||||
>
|
||||
<form onSubmit={formik.handleSubmit}>
|
||||
<div className='p-fluid grid gap-y-3'>
|
||||
<div className='field col-12 md:col-12'>
|
||||
<label className='block'>Nama Verifikator</label>
|
||||
<Dropdown
|
||||
name='user_id'
|
||||
optionLabel='nama'
|
||||
optionValue='user_id'
|
||||
value={formik.values.user_id}
|
||||
options={ddUserList}
|
||||
onChange={formik.handleChange}
|
||||
placeholder='Select nama verifikator'
|
||||
/>
|
||||
{errorFieldMessage('nama')}
|
||||
</div>
|
||||
<div className='field col-12 md:col-12'>
|
||||
<label className='block'>Unit</label>
|
||||
<MultiSelect
|
||||
name='unit_id'
|
||||
optionLabel='nama'
|
||||
optionValue='unit_id'
|
||||
value={formik.values.unit_id}
|
||||
options={ddVerifikator}
|
||||
onChange={formik.handleChange}
|
||||
placeholder='Select unit'
|
||||
/>
|
||||
{errorFieldMessage('unit_id')}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
export const Label = ({ children, type }) => {
|
||||
let backgroundColor
|
||||
switch (type) {
|
||||
case 'success':
|
||||
backgroundColor = 'bg-green-400'
|
||||
break
|
||||
|
||||
case 'danger':
|
||||
backgroundColor = 'bg-red-400'
|
||||
break
|
||||
|
||||
case 'warning':
|
||||
backgroundColor = 'bg-yellow-400'
|
||||
break
|
||||
|
||||
default:
|
||||
backgroundColor = 'bg-green-400'
|
||||
break
|
||||
}
|
||||
|
||||
return <span className={`border rounded-md px-3 text-sm py-1 ${backgroundColor} animate-pulse`}>{children}</span>
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
import { useAuth } from '@/hooks/auth'
|
||||
import Image from 'next/image'
|
||||
import { useCallback } from 'react'
|
||||
import Particles from 'react-tsparticles'
|
||||
import { loadFull } from 'tsparticles'
|
||||
import packageJson from './../package.json'
|
||||
import { Menu } from './Menu'
|
||||
import { Navbar } from './Navbar'
|
||||
|
||||
export const Depan = ({ children }) => {
|
||||
useAuth({ middleware: 'guest', redirectIfAuthenticated: '/dashboard' })
|
||||
|
||||
/** PARTICLES */
|
||||
const particlesInit = useCallback(async (engine) => {
|
||||
await loadFull(engine)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className='flex h-screen'>
|
||||
<div className='kiri w-9/12 relative hidden md:block'>
|
||||
<div className='relative h-screen'>
|
||||
<Image src={'/img/bg-login.jpg'} alt='bg-login' layout='fill' objectFit='cover' priority />
|
||||
</div>
|
||||
<Particles
|
||||
id='tsparticles'
|
||||
init={particlesInit}
|
||||
options={{
|
||||
fpsLimit: 60,
|
||||
interactivity: {
|
||||
events: {
|
||||
onHover: {
|
||||
enable: true,
|
||||
mode: 'grab',
|
||||
},
|
||||
resize: true,
|
||||
},
|
||||
},
|
||||
particles: {
|
||||
color: {
|
||||
value: '#ffffff',
|
||||
},
|
||||
links: {
|
||||
color: '#ffffff',
|
||||
distance: 130,
|
||||
enable: true,
|
||||
opacity: 1,
|
||||
width: 1,
|
||||
},
|
||||
collisions: {
|
||||
enable: true,
|
||||
},
|
||||
move: {
|
||||
directions: 'none',
|
||||
enable: true,
|
||||
outModes: {
|
||||
default: 'bounce',
|
||||
},
|
||||
random: false,
|
||||
speed: 2,
|
||||
straight: false,
|
||||
},
|
||||
number: {
|
||||
density: {
|
||||
enable: true,
|
||||
area: 800,
|
||||
},
|
||||
value: 50,
|
||||
},
|
||||
opacity: {
|
||||
value: 0.5,
|
||||
},
|
||||
shape: {
|
||||
type: 'circle',
|
||||
},
|
||||
size: {
|
||||
value: { min: 1, max: 5 },
|
||||
},
|
||||
},
|
||||
detectRetina: true,
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className='absolute left-1/2 -translate-x-1/2 bottom-10 text-white flex gap-1 text-xs 2xl:text-base'>
|
||||
<p>Copyright © 2022</p>
|
||||
<p className=''>SISTEM APLIKASI VERIFIKASI ANGGARAN BKN RI</p>
|
||||
</div>
|
||||
<div className='absolute left-1/2 -translate-x-1/2 bottom-6 text-white flex gap-1 text-xs 2xl:text-base'>
|
||||
<p>V{packageJson.version}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='kanan bg-abu md:w-3/12 px-7 relative'>{children}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const Belakang = ({ children }) => {
|
||||
useAuth({ middleware: 'auth' })
|
||||
return (
|
||||
<div className=''>
|
||||
<Navbar />
|
||||
<Menu />
|
||||
<div className='content 2xl:mx-72 mx-20 my-10'>{children}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const LandingPageLayout = ({ children }) => {
|
||||
return <div className='md:grid md:grid-cols-1 items-center md:px-32 md:gap-3 gap-y-3 py-5 mx-3'>{children}</div>
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
export const Menu = () => {
|
||||
const router = useRouter()
|
||||
const { pathname } = router
|
||||
const [menus, setMenus] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
setMenus(JSON.parse(localStorage.getItem('menus')))
|
||||
}, [])
|
||||
|
||||
const renderMenu = (target) => {
|
||||
let ready = Object.keys(menus).find((data) => data === target)
|
||||
if (!ready) return false
|
||||
return true
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='navbar-detail 2xl:px-16 bg-biru2 h-12 flex items-center justify-center shadow'>
|
||||
<div className='menu flex 2xl:gap-x-14 gap-x-10 2xl:text-base text-sm'>
|
||||
<Link href={'/dashboard'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow cursor-pointer px-3 py-1 rounded ${
|
||||
pathname.startsWith('/dashboard') ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('dashboard')}
|
||||
>
|
||||
<i className='pi pi-home mr-1' />
|
||||
Dashboard
|
||||
</div>
|
||||
</Link>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow cursor-pointer px-3 py-1 rounded relative dropdown-toggle ${
|
||||
pathname.startsWith('/referensi') ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('referensi')}
|
||||
>
|
||||
<i className='pi pi-database mr-1' />
|
||||
Referensi
|
||||
<div className='absolute rounded bg-biru2 w-max min-w-full 2xl:top-8 top-7 -left-0 py-3 z-10 flex flex-col gap-y-1 shadow dropdown-menu'>
|
||||
<Link href={'/referensi/jenis-tagihan'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow px-3 py-1 ${
|
||||
pathname === '/referensi/jenis-tagihan' ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('referensi/jenis-tagihan')}
|
||||
>
|
||||
Jenis Tagihan
|
||||
</div>
|
||||
</Link>
|
||||
<Link href={'/referensi/jenis-belanja'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow px-3 py-1 ${
|
||||
pathname === '/referensi/jenis-belanja' ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('referensi/jenis-belanja')}
|
||||
>
|
||||
Jenis Belanja
|
||||
</div>
|
||||
</Link>
|
||||
<Link href={'/referensi/jenis-kegiatan'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow px-3 py-1 ${
|
||||
pathname === '/referensi/jenis-kegiatan' ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('referensi/jenis-kegiatan')}
|
||||
>
|
||||
Jenis Kegiatan
|
||||
</div>
|
||||
</Link>
|
||||
<Link href={'/referensi/akun'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow px-3 py-1 ${
|
||||
pathname === '/referensi/akun' ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('referensi/akun')}
|
||||
>
|
||||
Jenis Pembayaran Kegiatan (Akun)
|
||||
</div>
|
||||
</Link>
|
||||
<Link href={'/referensi/unit-kerja'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow px-3 py-1 ${
|
||||
pathname === '/referensi/unit-kerja' ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('referensi/unit-kerja')}
|
||||
>
|
||||
Unit Kerja
|
||||
</div>
|
||||
</Link>
|
||||
<Link href={'/referensi/persyaratan'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow px-3 py-1 ${
|
||||
pathname === '/referensi/persyaratan' ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('referensi/persyaratan')}
|
||||
>
|
||||
Persyaratan
|
||||
</div>
|
||||
</Link>
|
||||
<Link href={'/referensi/verifikator'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow px-3 py-1 ${
|
||||
pathname === '/referensi/verifikator' ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('referensi/verifikator')}
|
||||
>
|
||||
Verifikator
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<Link href={'/pengajuan-tagihan'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow cursor-pointer px-3 py-1 rounded ${
|
||||
pathname.startsWith('/pengajuan-tagihan') ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('pengajuan-tagihan')}
|
||||
>
|
||||
<i className='pi pi-dollar mr-1' />
|
||||
Pengajuan Tagihan
|
||||
</div>
|
||||
</Link>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow cursor-pointer px-3 py-1 rounded relative dropdown-toggle ${
|
||||
pathname.startsWith('/otorisasi') ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('otorisasi')}
|
||||
>
|
||||
<i className='pi pi-lock mr-1' />
|
||||
Otorisasi
|
||||
<div className='absolute rounded bg-biru2 w-max min-w-full 2xl:top-8 top-7 -left-0 py-3 z-10 flex flex-col gap-y-1 shadow dropdown-menu'>
|
||||
<Link href={'/otorisasi/verifikasi'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow px-3 py-1 ${
|
||||
pathname === '/otorisasi/verifikasi' ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('otorisasi/verifikasi')}
|
||||
>
|
||||
Verifikasi
|
||||
</div>
|
||||
</Link>
|
||||
<Link href={'/otorisasi/rekomendasi'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow px-3 py-1 ${
|
||||
pathname === '/otorisasi/rekomendasi' ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('otorisasi/rekomendasi')}
|
||||
>
|
||||
Rekomendasi
|
||||
</div>
|
||||
</Link>
|
||||
<Link href={'/otorisasi/persetujuan'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow px-3 py-1 ${
|
||||
pathname === '/otorisasi/persetujuan' ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('otorisasi/persetujuan')}
|
||||
>
|
||||
Persetujuan
|
||||
</div>
|
||||
</Link>
|
||||
<Link href={'/otorisasi/pengesahan'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow px-3 py-1 ${
|
||||
pathname === '/otorisasi/pengesahan' ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('otorisasi/pengesahan')}
|
||||
>
|
||||
Pengesahan
|
||||
</div>
|
||||
</Link>
|
||||
<Link href={'/otorisasi/spm'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow px-3 py-1 ${
|
||||
pathname === '/otorisasi/spm' ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('otorisasi/spm')}
|
||||
>
|
||||
SPM
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow cursor-pointer px-3 py-1 rounded relative dropdown-toggle ${
|
||||
pathname.startsWith('/laporan') ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('laporan')}
|
||||
>
|
||||
<i className='pi pi-chart-line mr-1' />
|
||||
Laporan
|
||||
<div className='absolute rounded bg-biru2 w-max min-w-full 2xl:top-8 top-7 -left-0 py-3 z-10 flex flex-col gap-y-1 shadow dropdown-menu'>
|
||||
<Link href={'/laporan/per-user'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow px-3 py-1 ${
|
||||
pathname === '/laporan/per-user' ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('laporan/per-user')}
|
||||
>
|
||||
Per User (Verifikator, kasub verif)
|
||||
</div>
|
||||
</Link>
|
||||
<Link href={'/laporan/approval'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow px-3 py-1 ${
|
||||
pathname === '/laporan/approval' ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('laporan/approval')}
|
||||
>
|
||||
Approval (Kabag verif, bendahara, ppspm)
|
||||
</div>
|
||||
</Link>
|
||||
<Link href={'/laporan/rekap-bulanan-per-user'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow px-3 py-1 ${
|
||||
pathname === '/laporan/rekap-bulanan-per-user' ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('laporan/rekap-bulanan-per-user')}
|
||||
>
|
||||
Rekapitulasi bulanan per user
|
||||
</div>
|
||||
</Link>
|
||||
<Link href={'/laporan/rekap-verif-per-bulan'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow px-3 py-1 ${
|
||||
pathname === '/laporan/rekap-verif-per-bulan' ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('laporan/rekap-verif-per-bulan')}
|
||||
>
|
||||
Rekapitulasi hasil verifikasi per bulan
|
||||
</div>
|
||||
</Link>
|
||||
<Link href={'/laporan/rekap-per-unit-kerja'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow px-3 py-1 ${
|
||||
pathname === '/laporan/rekap-per-unit-kerja' ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('laporan/rekap-per-unit-kerja')}
|
||||
>
|
||||
Rekapitulasi per unit kerja
|
||||
</div>
|
||||
</Link>
|
||||
<Link href={'/laporan/rekap-per-jenis-belanja'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow px-3 py-1 ${
|
||||
pathname === '/laporan/rekap-per-jenis-belanja' ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('laporan/rekap-per-jenis-belanja')}
|
||||
>
|
||||
Rekapitulasi per jenis belanja (BAS)
|
||||
</div>
|
||||
</Link>
|
||||
<Link href={'/laporan/verif-per-bulan'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow px-3 py-1 ${
|
||||
pathname === '/laporan/verif-per-bulan' ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('laporan/verif-per-bulan')}
|
||||
>
|
||||
Hasil verifikasi per bulan (detail)
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<Link href={'/peraturan'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow cursor-pointer px-3 py-1 rounded ${
|
||||
pathname.startsWith('/peraturan') ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('peraturan')}
|
||||
>
|
||||
<i className='pi pi-shield mr-1' />
|
||||
Peraturan
|
||||
</div>
|
||||
</Link>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow cursor-pointer px-3 py-1 rounded relative dropdown-toggle ${
|
||||
pathname.startsWith('/manajemen') ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('manajemen')}
|
||||
>
|
||||
<i className='pi pi-database mr-1' />
|
||||
Manajemen
|
||||
<div className='absolute rounded bg-biru2 w-max min-w-full 2xl:top-8 top-7 -left-0 py-3 z-10 flex flex-col gap-y-1 shadow dropdown-menu'>
|
||||
<Link href={'/manajemen/user'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow px-3 py-1 ${
|
||||
pathname === '/manajemen/user' ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('manajemen/user')}
|
||||
>
|
||||
User
|
||||
</div>
|
||||
</Link>
|
||||
<Link href={'/manajemen/role'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow px-3 py-1 ${
|
||||
pathname === '/manajemen/role' ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('manajemen/role')}
|
||||
>
|
||||
Role
|
||||
</div>
|
||||
</Link>
|
||||
<Link href={'/manajemen/jam-kerja'}>
|
||||
<div
|
||||
className={`hover:text-hitam hover:bg-yellow px-3 py-1 ${
|
||||
pathname === '/manajemen/jam-kerja' ? 'bg-yellow' : 'text-white'
|
||||
}`}
|
||||
hidden={!renderMenu('manajemen/jam-kerja')}
|
||||
>
|
||||
Jam Kerja
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { useAuth } from '@/hooks/auth'
|
||||
import { Avatar } from 'primereact/avatar'
|
||||
import { memo, useEffect, useState } from 'react'
|
||||
import { MdExpandMore } from 'react-icons/md'
|
||||
|
||||
function NavbarMemo() {
|
||||
const { logout } = useAuth({})
|
||||
const [user, setUser] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
setUser(JSON.parse(localStorage.getItem('user')))
|
||||
}, [])
|
||||
|
||||
const handleLogout = () => {
|
||||
logout()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='navbar 2xl:px-24 px-10 flex justify-end items-center border-cream h-[150px]'>
|
||||
<div className='logout z-10 py-3 px-5'>
|
||||
<div className='flex items-center gap-x-3'>
|
||||
<Avatar image={'/img/M.png'} shape='circle' />
|
||||
<div className='text-white'>
|
||||
<p className='font-semibold capitalize'>{user && user.nama_lengkap}</p>
|
||||
<p className='text-xs text-hijau/70'>{user && user.role}</p>
|
||||
</div>
|
||||
<MdExpandMore className='text-xl' />
|
||||
</div>
|
||||
<div
|
||||
className='absolute bg-white top-24 2xl:right-32 right-20 w-56 p-3 rounded flex flex-col shadow-lg gap-y-3 logout-pop'
|
||||
onClick={handleLogout}
|
||||
>
|
||||
<div className='flex items-center gap-x-3 shadow-md p-2'>
|
||||
<Avatar image={'/img/M.png'} shape='circle' className='shadow' />
|
||||
<p className='font-semibold capitalize'>{user && user.nama_lengkap}</p>
|
||||
</div>
|
||||
<div className='flex items-center gap-x-3 hover:bg-abu2 cursor-pointer p-2 rounded'>
|
||||
<i className='pi pi-sign-out rounded-full p-2' />
|
||||
<p className='text-sm'>Logout</p>
|
||||
</div>
|
||||
<div className='bg-overlay'></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const Navbar = memo(NavbarMemo)
|
||||
@@ -0,0 +1,15 @@
|
||||
export const Judul = ({ children }) => {
|
||||
return (
|
||||
<p className='judul-content 2xl:text-2xl text-xl border-l-4 border-l-pink pl-2 mb-3 font-bebas tracking-wide'>
|
||||
{children}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
export const SubJudul = ({ children, className = '' }) => {
|
||||
return <p className={`judul-content font-semibold ${className}`}>{children}</p>
|
||||
}
|
||||
|
||||
export const LabelInput = ({ children }) => {
|
||||
return <label className='font-semibold'>{children}</label>
|
||||
}
|
||||
Reference in New Issue
Block a user