'use client'; import { useState } from 'react'; import { useRouter } from 'next/navigation'; import { useFormik } from 'formik'; import toast from 'react-hot-toast'; import { Icon } from '@iconify/react'; import Link from 'next/link'; import Button from '@/components/Button'; import RealizationStatusBadge from '@/components/pages/expense/RealizationStatusBadge'; import ExpenseStatusBadge from '@/components/pages/expense/ExpenseStatusBadge'; import DropFileInput from '@/components/input/DropFileInput'; import ApprovalSteps, { useApprovalSteps, } from '@/components/pages/ApprovalSteps'; import { useModal } from '@/components/Modal'; import ConfirmationModal from '@/components/modal/ConfirmationModal'; import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes'; import ExpensePDFPreviewButton from '@/components/pages/expense//pdf/ExpensePDFButton'; import RequirePermission from '@/components/helper/RequirePermission'; import { Expense } from '@/types/api/expense'; import { formatCurrency, formatDate } from '@/lib/helper'; import { UploadRequestDocumentsFormSchema, UploadRequestDocumentsFormValues, } from '@/components/pages/expense/form/ExpenseRequestForm.schema'; import { ACCEPTED_FILE_TYPE, S3_PUBLIC_BASE_URL } from '@/config/constant'; import { ExpenseApi } from '@/services/api/expense'; import { isResponseSuccess } from '@/lib/api-helper'; import { EXPENSE_REQUEST_APPROVAL_LINE } from '@/config/approval-line'; import { BaseApiResponse } from '@/types/api/api-general'; interface ExpenseRequestContentProps { initialValues?: Expense; } const ExpenseRequestContent = ({ initialValues, }: ExpenseRequestContentProps) => { const router = useRouter(); const { approvals: approvalHistory, isLoading: isLoadingApprovalHistory } = useApprovalSteps({ latestApproval: initialValues?.latest_approval, approvalLines: EXPENSE_REQUEST_APPROVAL_LINE, moduleName: 'EXPENSES', moduleId: initialValues?.id.toString() ?? '', params: { page: 1, limit: 100, }, }); const isLatestApprovalRejected = initialValues?.latest_approval.action === 'REJECTED'; const isCurrentApprovalOnHeadArea = !isLatestApprovalRejected && initialValues?.latest_approval.step_number === 1; const isCurrentApprovalOnUnitVicePresident = !isLatestApprovalRejected && initialValues?.latest_approval.step_number === 2; const isCurrentApprovalOnFinance = !isLatestApprovalRejected && initialValues?.latest_approval.step_number === 3; const isCurrentApprovalOnRealization = !isLatestApprovalRejected && initialValues?.latest_approval.step_number === 5; const showEditButton = initialValues?.latest_approval.step_number !== 6 && (initialValues?.latest_approval.step_number === 1 || initialValues?.latest_approval.step_number === 2 || initialValues?.latest_approval.step_number === 3 || initialValues?.latest_approval.step_number === 4); const showRejectButton = !isLatestApprovalRejected && (initialValues?.latest_approval.step_number === 1 || initialValues?.latest_approval.step_number === 2 || initialValues?.latest_approval.step_number === 3); const isExpenseCanBeRealized = !isLatestApprovalRejected && initialValues?.latest_approval.step_number === 4; // Modal hooks const deleteModal = useModal(); const completeModal = useModal(); const approveModal = useModal(); const rejectModal = useModal(); // Modal loading state const [isDeleteLoading, setIsDeleteLoading] = useState(false); const [isCompleteLoading, setIsCompleteLoading] = useState(false); const [isApproveLoading, setIsApproveLoading] = useState(false); const [isRejectLoading, setIsRejectLoading] = useState(false); const [, setApprovalNotes] = useState(''); const formik = useFormik({ initialValues: { documents: [], }, validationSchema: UploadRequestDocumentsFormSchema, onSubmit: async (values) => { const addRequestDocumentsRes = await ExpenseApi.uploadRequestDocuments( initialValues?.id as number, values.documents ); if (isResponseSuccess(addRequestDocumentsRes)) { toast.success(addRequestDocumentsRes.message); window.location.reload(); } else { toast.error(String(addRequestDocumentsRes?.message)); } }, }); const deleteExpenseClickHandler = () => { deleteModal.openModal(); }; const completeExpenseClickHandler = () => { completeModal.openModal(); }; const approveClickHandler = () => { setApprovalNotes(''); approveModal.openModal(); }; const rejectClickHandler = () => { setApprovalNotes(''); rejectModal.openModal(); }; // Modal confirm click handler const confirmationModalDeleteClickHandler = async () => { setIsDeleteLoading(true); const deleteResponse = await ExpenseApi.delete(initialValues?.id as number); if (isResponseSuccess(deleteResponse)) { toast.success('Berhasil menghapus data biaya operasional!'); router.push('/expense'); } else { toast.error('Gagal menghapus data biaya operasional!'); } deleteModal.closeModal(); setIsDeleteLoading(false); }; const confirmationModalCompleteClickHandler = async () => { setIsCompleteLoading(true); const completeRes = await ExpenseApi.complete(initialValues?.id as number); if (isResponseSuccess(completeRes)) { toast.success(completeRes.message); router.push('/expense'); } else { toast.error(completeRes?.message as string); } completeModal.closeModal(); setIsCompleteLoading(false); }; const confirmationModalApproveClickHandler = async (notes: string) => { setIsApproveLoading(true); let approveResponse: BaseApiResponse | undefined = undefined; if (isCurrentApprovalOnHeadArea) { approveResponse = await ExpenseApi.approveHeadArea( initialValues.id, notes ); } if (isCurrentApprovalOnUnitVicePresident) { approveResponse = await ExpenseApi.approveUnitVicePresident( initialValues.id, notes ); } if (isCurrentApprovalOnFinance) { approveResponse = await ExpenseApi.approveFinance( initialValues.id, notes ); } if (isResponseSuccess(approveResponse)) { approveModal.closeModal(); toast.success(approveResponse?.message); setApprovalNotes(''); router.push('/expense'); } else { approveModal.closeModal(); toast.error(approveResponse?.message as string); } setIsApproveLoading(false); }; const confirmationModalRejectClickHandler = async (notes: string) => { setIsRejectLoading(true); let rejectResponse: BaseApiResponse | undefined = undefined; if (isCurrentApprovalOnHeadArea) { rejectResponse = await ExpenseApi.rejectHeadArea(initialValues.id, notes); } if (isCurrentApprovalOnUnitVicePresident) { rejectResponse = await ExpenseApi.rejectUnitVicePresident( initialValues.id, notes ); } if (isCurrentApprovalOnFinance) { rejectResponse = await ExpenseApi.rejectFinance(initialValues.id, notes); } if (isResponseSuccess(rejectResponse)) { rejectModal.closeModal(); toast.success(rejectResponse.message); setApprovalNotes(''); router.push('/expense'); } else { rejectModal.closeModal(); toast.error(rejectResponse?.message as string); } setIsRejectLoading(false); }; const requestDocumentsChangeHandler = (val: File[]) => { formik.setFieldTouched('documents', true); const invalidFiles = val.filter((file) => file.size > 5 * 1024 * 1024); if (invalidFiles.length > 0) { toast.error('Ukuran dokumen maksimal 5 MB!'); return; } formik.setFieldValue('documents', val); }; const requestDocumentsDeleteHandler = (deletedFileIdx: number) => { const newRequestDocuments = formik.values.documents; newRequestDocuments?.splice(deletedFileIdx, 1); formik.setFieldValue('documents', newRequestDocuments); }; return ( <>
{initialValues && !isLoadingApprovalHistory && approvalHistory && (
)}
{isCurrentApprovalOnHeadArea && ( )} {isCurrentApprovalOnUnitVicePresident && ( )} {isCurrentApprovalOnFinance && ( )} {isCurrentApprovalOnRealization && ( )} {showRejectButton && ( )} {isExpenseCanBeRealized && ( )}
{showEditButton && ( )}
Nomor PO : {!initialValues?.po_number && '-'} {initialValues?.po_number && ( )}
Nomor Referensi : {initialValues?.reference_number}
Kategori : {initialValues?.category === 'BOP' ? 'Biaya Operasional' : 'Non Biaya Operasional'}
Lokasi : {initialValues?.location.name}
Kandang : {initialValues?.kandangs && initialValues?.kandangs.some((k) => k.name) ? initialValues?.kandangs .filter((item) => item.name) .map((item) => item.name) .join(', ') : '-'}
Vendor : {initialValues?.supplier.name}
Tanggal Transaksi : {formatDate( initialValues?.transaction_date, 'DD MMMM YYYY' )}
Tanggal Realisasi : {initialValues?.realization_date ? formatDate( initialValues?.realization_date, 'DD MMMM YYYY' ) : '-'}
Nama Pengaju : {initialValues?.created_user.name}
Nominal Biaya : {formatCurrency( initialValues?.latest_approval.step_number === 5 || initialValues?.latest_approval.step_number === 6 ? (initialValues?.total_realisasi ?? 0) : (initialValues?.total_pengajuan ?? 0) )}
Status Pencairan :
Status Biaya :
Dokumen Pengajuan :
{!initialValues?.documents || (initialValues?.documents && initialValues?.documents.length === 0 && '-')} {initialValues?.documents && initialValues?.documents.length > 0 && (
    {initialValues?.documents.map( (requestDocument, requestDocumentIdx) => { const path = requestDocument.path.startsWith( '/' ) ? requestDocument.path.slice(1) : requestDocument.path; const documentUrl = `${S3_PUBLIC_BASE_URL}/${path}`; return (
  • {requestDocument.path}{' '}
  • ); } )}
)}
{formik.values.documents && formik.values.documents.length > 0 && ( )}

Rincian Pengajuan Biaya Operasional

{initialValues?.kandangs.map( (kandangExpense, kandangExpenseIdx) => { let expenseGrandTotal = 0; kandangExpense.pengajuans?.forEach( (item) => (expenseGrandTotal += item.qty * item.price) ); return (
{kandangExpense.pengajuans?.map( (pengajuanItem, pengajuanIdx) => ( ) )}
{kandangExpense.kandang_id && kandangExpense.name ? `Biaya ${kandangExpense.name}` : `Biaya ${initialValues?.location.name || 'Umum'}`}
Nonstock Total Kuantitas Harga Satuan Catatan
{pengajuanItem.nonstock.name} {pengajuanItem.qty} {formatCurrency(pengajuanItem.price)} {pengajuanItem.notes ?? '-'}
Total Biaya Keseluruhan: {formatCurrency(expenseGrandTotal)}
); } )}
{ setApprovalNotes(''); approveModal.closeModal(); }, }} primaryButton={{ text: 'Ya', color: 'success', isLoading: isApproveLoading, onClick: confirmationModalApproveClickHandler, }} /> { setApprovalNotes(''); rejectModal.closeModal(); }, }} primaryButton={{ text: 'Ya', color: 'error', isLoading: isRejectLoading, onClick: confirmationModalRejectClickHandler, }} /> ); }; export default ExpenseRequestContent;