mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
332 lines
12 KiB
TypeScript
332 lines
12 KiB
TypeScript
import { useFormik } from 'formik';
|
|
import toast from 'react-hot-toast';
|
|
|
|
import Link from 'next/link';
|
|
import { Icon } from '@iconify/react';
|
|
import Button from '@/components/Button';
|
|
import RequirePermission from '@/components/helper/RequirePermission';
|
|
import Card from '@/components/Card';
|
|
import DropFileInput from '@/components/input/DropFileInput';
|
|
|
|
import { Expense } from '@/types/api/expense';
|
|
import { formatCurrency, formatDate } from '@/lib/helper';
|
|
import {
|
|
UploadRequestDocumentsFormSchema,
|
|
UploadRequestDocumentsFormValues,
|
|
} from '@/components/pages/expense/form/ExpenseRequestForm.schema';
|
|
import { ExpenseApi } from '@/services/api/expense';
|
|
import { isResponseSuccess } from '@/lib/api-helper';
|
|
import { ACCEPTED_FILE_TYPE } from '@/config/constant';
|
|
|
|
interface ExpenseRealizationContentProps {
|
|
initialValues?: Expense;
|
|
}
|
|
|
|
const ExpenseRealizationContent = ({
|
|
initialValues,
|
|
}: ExpenseRealizationContentProps) => {
|
|
const formik = useFormik<UploadRequestDocumentsFormValues>({
|
|
initialValues: {
|
|
documents: [],
|
|
},
|
|
validationSchema: UploadRequestDocumentsFormSchema,
|
|
onSubmit: async (values) => {
|
|
const addRealizationDocumentsRes =
|
|
await ExpenseApi.uploadRealizationDocuments(
|
|
initialValues?.id as number,
|
|
values.documents
|
|
);
|
|
|
|
if (isResponseSuccess(addRealizationDocumentsRes)) {
|
|
toast.success(addRealizationDocumentsRes.message);
|
|
window.location.reload();
|
|
} else {
|
|
toast.error(String(addRealizationDocumentsRes?.message));
|
|
}
|
|
},
|
|
});
|
|
|
|
const realizationDocumentsChangeHandler = (val: File[]) => {
|
|
formik.setFieldTouched('documents', true);
|
|
formik.setFieldValue('documents', val);
|
|
};
|
|
|
|
const realizationDocumentsDeleteHandler = (deletedFileIdx: number) => {
|
|
const newRealizationDocuments = formik.values.documents;
|
|
|
|
newRealizationDocuments?.splice(deletedFileIdx, 1);
|
|
|
|
formik.setFieldValue('documents', newRealizationDocuments);
|
|
};
|
|
|
|
return (
|
|
<div>
|
|
<div className='w-full max-w-5xl mx-auto flex flex-col sm:flex-row justify-end gap-2'>
|
|
<div className='w-full sm:w-fit sm:ml-2 flex flex-row gap-2 items-center'>
|
|
<RequirePermission permissions='lti.expense.update.realization'>
|
|
<Button
|
|
type='button'
|
|
color='warning'
|
|
href={`/expense/realization/edit/?expenseId=${initialValues?.id}`}
|
|
className='px-4 grow sm:grow-0'
|
|
>
|
|
<Icon icon='mdi:pencil-outline' width={24} height={24} />
|
|
Edit Realisasi
|
|
</Button>
|
|
</RequirePermission>
|
|
</div>
|
|
</div>
|
|
|
|
<div className='overflow-x-auto w-full max-w-5xl mx-auto'>
|
|
<table className='table table-sm table-zebra'>
|
|
<tbody>
|
|
<tr>
|
|
<th>Tanggal Realisasi</th>
|
|
<th>:</th>
|
|
<td>
|
|
{initialValues?.realization_date
|
|
? formatDate(initialValues?.realization_date, 'DD MMMM YYYY')
|
|
: '-'}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Dokumen Realisasi</th>
|
|
<th>:</th>
|
|
<td>
|
|
<div>
|
|
{!initialValues?.realization_docs ||
|
|
(initialValues?.realization_docs &&
|
|
initialValues?.realization_docs.length === 0 &&
|
|
'-')}
|
|
|
|
{initialValues?.realization_docs &&
|
|
initialValues?.realization_docs.length > 0 && (
|
|
<ul className='list-disc'>
|
|
{initialValues?.realization_docs.map(
|
|
(realizationDocument, realizationDocumentIdx) => (
|
|
<li key={realizationDocumentIdx}>
|
|
<Link
|
|
href={realizationDocument.path}
|
|
target='_blank'
|
|
rel='noopener noreferrer'
|
|
className='text-blue-500 underline'
|
|
>
|
|
{realizationDocument.path}{' '}
|
|
<Icon
|
|
icon='cuida:open-in-new-tab-outline'
|
|
width={12}
|
|
height={12}
|
|
className='inline'
|
|
/>
|
|
</Link>
|
|
</li>
|
|
)
|
|
)}
|
|
</ul>
|
|
)}
|
|
</div>
|
|
|
|
<RequirePermission permissions='lti.expense.document.realization'>
|
|
<div className='flex flex-col gap-2'>
|
|
<DropFileInput
|
|
name='documents'
|
|
values={formik.values.documents}
|
|
onChange={realizationDocumentsChangeHandler}
|
|
onDelete={realizationDocumentsDeleteHandler}
|
|
accept={{
|
|
...ACCEPTED_FILE_TYPE.PDF,
|
|
...ACCEPTED_FILE_TYPE.IMAGE,
|
|
}}
|
|
maxFiles={10}
|
|
className={{
|
|
wrapper: 'mt-2',
|
|
inputWrapper: 'flex items-center',
|
|
}}
|
|
/>
|
|
|
|
{formik.values.documents &&
|
|
formik.values.documents.length > 0 && (
|
|
<Button
|
|
onClick={formik.submitForm}
|
|
disabled={formik.isSubmitting}
|
|
isLoading={formik.isSubmitting}
|
|
className='w-fit self-end'
|
|
>
|
|
<Icon icon='ic:round-plus' width={24} height={24} />
|
|
Tambah
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</RequirePermission>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div className='w-full max-w-5xl mt-8 mx-auto'>
|
|
<div className='flex flex-row gap-4'>
|
|
<Card variant='bordered' size='sm' className={{ wrapper: 'grow' }}>
|
|
<div className='w-full flex flex-col gap-2'>
|
|
<h3 className='text-sm'>Nominal Pengajuan</h3>
|
|
|
|
<span className='text-xl'>
|
|
{formatCurrency(initialValues?.total_pengajuan as number)}
|
|
</span>
|
|
|
|
<span className='text-sm'>
|
|
Terbayar{' '}
|
|
{formatCurrency(initialValues?.total_realisasi as number)}
|
|
</span>
|
|
</div>
|
|
</Card>
|
|
|
|
<Card variant='bordered' size='sm' className={{ wrapper: 'grow' }}>
|
|
<div className='w-full flex flex-col gap-2'>
|
|
<h3 className='text-sm'>Nominal Realisasi</h3>
|
|
|
|
<span className='text-xl'>
|
|
{formatCurrency(initialValues?.total_realisasi as number)}
|
|
</span>
|
|
|
|
<span className='text-sm'>
|
|
Selisih{' '}
|
|
{formatCurrency(
|
|
(initialValues?.total_realisasi as number) -
|
|
(initialValues?.total_pengajuan as number)
|
|
)}
|
|
</span>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
|
|
<div className='w-full max-w-5xl mt-8 mx-auto'>
|
|
<h2 className='font-bold text-xl text-center'>
|
|
Rincian Pengajuan Biaya Operasional
|
|
</h2>
|
|
|
|
<div className='w-full mt-2 flex flex-col gap-4'>
|
|
{initialValues?.kandangs.map((kandangExpense, kandangExpenseIdx) => {
|
|
let expenseGrandTotal = 0;
|
|
|
|
kandangExpense.pengajuans?.forEach(
|
|
(item) => (expenseGrandTotal += item.price)
|
|
);
|
|
|
|
return (
|
|
<div
|
|
key={kandangExpenseIdx}
|
|
className='overflow-x-auto w-full mx-auto'
|
|
>
|
|
<table className='table table-sm table-zebra'>
|
|
<thead>
|
|
<tr>
|
|
<th
|
|
colSpan={5}
|
|
className='font-bold text-center text-base-content text-lg'
|
|
>
|
|
Biaya {kandangExpense.name}
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<th>Nonstock</th>
|
|
<th>Total Kuantitas</th>
|
|
<th>Total Biaya</th>
|
|
<th>Catatan</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{kandangExpense.pengajuans?.map(
|
|
(pengajuanItem, pengajuanIdx) => (
|
|
<tr key={pengajuanIdx}>
|
|
<td>{pengajuanItem.nonstock.name}</td>
|
|
<td>{pengajuanItem.qty}</td>
|
|
<td>{formatCurrency(pengajuanItem.price)}</td>
|
|
<td className='w-xs'>{pengajuanItem.note ?? '-'}</td>
|
|
</tr>
|
|
)
|
|
)}
|
|
</tbody>
|
|
<tfoot>
|
|
<tr className='border-y'>
|
|
<th colSpan={2} className='text-right'>
|
|
Total Biaya Keseluruhan:
|
|
</th>
|
|
<th colSpan={2}>{formatCurrency(expenseGrandTotal)}</th>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
|
|
<div className='w-full max-w-5xl mt-8 mx-auto'>
|
|
<h2 className='font-bold text-xl text-center'>
|
|
Rincian Realisasi Biaya Operasional
|
|
</h2>
|
|
|
|
<div className='w-full mt-2 flex flex-col gap-4'>
|
|
{initialValues?.kandangs.map((kandangExpense, kandangExpenseIdx) => {
|
|
let expenseGrandTotal = 0;
|
|
|
|
kandangExpense.realisasi?.forEach(
|
|
(item) => (expenseGrandTotal += item.price)
|
|
);
|
|
|
|
return (
|
|
<div
|
|
key={kandangExpenseIdx}
|
|
className='overflow-x-auto w-full mx-auto'
|
|
>
|
|
<table className='table table-sm table-zebra'>
|
|
<thead>
|
|
<tr>
|
|
<th
|
|
colSpan={5}
|
|
className='font-bold text-center text-base-content text-lg'
|
|
>
|
|
Biaya {kandangExpense.name}
|
|
</th>
|
|
</tr>
|
|
<tr>
|
|
<th>Nonstock</th>
|
|
<th>Total Kuantitas</th>
|
|
<th>Total Biaya</th>
|
|
<th>Catatan</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{kandangExpense.realisasi?.map(
|
|
(realisasiItem, realisasiIdx) => (
|
|
<tr key={realisasiIdx}>
|
|
<td>{realisasiItem.nonstock.name}</td>
|
|
<td>{realisasiItem.qty}</td>
|
|
<td>{formatCurrency(realisasiItem.price)}</td>
|
|
<td className='w-xs'>{realisasiItem.note ?? '-'}</td>
|
|
</tr>
|
|
)
|
|
)}
|
|
</tbody>
|
|
<tfoot>
|
|
<tr className='border-y'>
|
|
<th colSpan={2} className='text-right'>
|
|
Total Biaya Keseluruhan:
|
|
</th>
|
|
<th colSpan={2}>{formatCurrency(expenseGrandTotal)}</th>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ExpenseRealizationContent;
|