'use client'; import { useCallback, useEffect, useState } from 'react'; import { useRouter } from 'next/navigation'; 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 SelectInput, { OptionType, useSelect, } from '@/components/input/SelectInput'; import DateInput from '@/components/input/DateInput'; import DropFileInput from '@/components/input/DropFileInput'; import ExpenseKandangsTable from '@/components/pages/expense/form/ExpenseKandangsTable'; import ExpenseRealizationKandangDetailExpense from '@/components/pages/expense/form/ExpenseRealizationKandangDetailExpense'; import { CreateExpenseRealizationPayload, Expense, UpdateExpenseRealizationPayload, } from '@/types/api/expense'; import { ExpenseRealizationFormSchema, ExpenseRealizationFormValues, getExpenseRealizationFormInitialValues, UpdateExpenseRealizationFormSchema, } from '@/components/pages/expense/form/ExpenseRealizationForm.schema'; import { ExpenseApi } from '@/services/api/expense'; import { isResponseError } from '@/lib/api-helper'; import { LocationApi, SupplierApi } from '@/services/api/master-data'; import { Supplier } from '@/types/api/master-data/supplier'; import { ACCEPTED_FILE_TYPE } from '@/config/constant'; import { cn } from '@/lib/helper'; interface ExpenseRealizationFormProps { type?: 'add' | 'edit' | 'detail'; initialValues?: Expense; } const ExpenseRealizationForm = ({ type = 'add', initialValues, }: ExpenseRealizationFormProps) => { const router = useRouter(); const [expenseFormErrorMessage, setExpenseFormErrorMessage] = useState(''); const createExpenseHandler = useCallback( async (payload: CreateExpenseRealizationPayload) => { const createExpenseRes = await ExpenseApi.createRealization( initialValues?.id as number, ExpenseApi.convertExpenseRealizationPayloadToFormData(payload) ); if (isResponseError(createExpenseRes)) { setExpenseFormErrorMessage(createExpenseRes.message); return; } toast.success(createExpenseRes?.message as string); router.push('/expense'); }, [router] ); const updateExpenseHandler = useCallback( async (expenseId: number, payload: UpdateExpenseRealizationPayload) => { const updateExpenseRes = await ExpenseApi.updateRealization( expenseId, ExpenseApi.convertExpenseRealizationPayloadToFormData(payload) ); if (updateExpenseRes?.status === 'error') { setExpenseFormErrorMessage(updateExpenseRes.message); return; } toast.success(updateExpenseRes?.message as string); router.refresh(); router.push('/expense'); }, [router] ); const formik = useFormik({ initialValues: getExpenseRealizationFormInitialValues(initialValues), validationSchema: type === 'edit' ? UpdateExpenseRealizationFormSchema : ExpenseRealizationFormSchema, onSubmit: async (values) => { setExpenseFormErrorMessage(''); const realizations: CreateExpenseRealizationPayload['realizations'] = []; values.realizations.forEach((realization) => { realization.cost_items.forEach((costItem) => { const unitPrice = parseFloat(String(costItem.total_cost)) / parseFloat(String(costItem.quantity)); const realizationItem = { expense_nonstock_id: costItem.nonstock?.value as number, qty: parseFloat(String(costItem.quantity)) as number, unit_price: unitPrice, total_price: parseFloat(String(costItem.total_cost)) as number, notes: costItem.notes ?? '', }; realizations.push(realizationItem); }); }); const expensePayload: CreateExpenseRealizationPayload = { realization_date: values.realization_date as string, documents: values.documents as File[], realizations, }; switch (type) { case 'add': await createExpenseHandler(expensePayload); break; case 'edit': await updateExpenseHandler( initialValues?.id as number, expensePayload ); break; } }, }); const { setValues: formikSetValues } = formik; const { setInputValue: setLocationInputValue, options: locationOptions, isLoadingOptions: isLoadingLocationOptions, } = useSelect(LocationApi.basePath, 'id', 'name'); const { setInputValue: setVendorInputValue, options: vendorOptions, isLoadingOptions: isLoadingVendorOptions, } = useSelect(SupplierApi.basePath, 'id', 'name'); const locationChangeHandler = (val: OptionType | OptionType[] | null) => { formik.setFieldTouched('location', true); formik.setFieldValue('location', val); formik.setFieldValue('kandangs', []); formik.setFieldValue('realizations', []); }; const kandangsChangeHandler = (kandangs: { id: number; name: string }[]) => { formik.setFieldTouched('kandangs', true); formik.setFieldValue('kandangs', kandangs); const newRealizations = [...(formik.values.realizations ?? [])]; // add new realizations kandangs.forEach((kandangItem) => { const isKandangExistInRealization = newRealizations.find( (realizationItem) => realizationItem.kandang_id === kandangItem.id ); if (isKandangExistInRealization) return; newRealizations.push({ kandang_id: kandangItem.id, cost_items: [ { nonstock: undefined, quantity: undefined, total_cost: undefined, notes: '', }, ], }); }); // prune realizations const kandangIds = new Set(kandangs.map((kandang) => kandang.id)); const deletedRealizationsIdx: number[] = []; newRealizations.forEach((realization, idx) => { const isRealizationValid = kandangIds.has(realization.kandang_id); if (!isRealizationValid) { deletedRealizationsIdx.push(idx); } }); deletedRealizationsIdx.forEach((deletedRealizationIdx) => { newRealizations.splice(deletedRealizationIdx, 1); }); formik.setFieldValue('realizations', newRealizations); }; const vendorChangeHandler = (val: OptionType | OptionType[] | null) => { formik.setFieldTouched('vendor', true); formik.setFieldValue('vendor', val); }; const realizationDocumentsChangeHandler = (val: File[]) => { formik.setFieldTouched('documents', true); formik.setFieldValue('documents', val); }; const realizationDocumentsDeleteHandler = (deletedFileIdx: number) => { const newRequestDocuments = formik.values.documents; newRequestDocuments?.splice(deletedFileIdx, 1); formik.setFieldValue('documents', newRequestDocuments); }; useEffect(() => { formikSetValues(getExpenseRealizationFormInitialValues(initialValues)); }, [formikSetValues, getExpenseRealizationFormInitialValues, initialValues]); return (

Realisasi Biaya Operasional

{formik.values.existing_documents && formik.values.existing_documents.length > 0 && (
    {formik.values.existing_documents.map( (existingDocument, existingDocumentIdx) => (
  • {existingDocument.name}{' '}
  • ) )}
)}
{expenseFormErrorMessage && (
{expenseFormErrorMessage}
)}
{type !== 'add' && (
{type !== 'edit' && ( )}
)} {type !== 'detail' && (
)}
); }; export default ExpenseRealizationForm;