From f011f5b7f9f5a64a9fe60bc7c86ac4d6d2d17162 Mon Sep 17 00:00:00 2001 From: ValdiANS Date: Thu, 6 Nov 2025 15:28:42 +0700 Subject: [PATCH] feat(FE-188,193): create ExpenseRequestKandangDetailExpense component --- .../ExpenseRequestKandangDetailExpense.tsx | 285 ++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 src/components/pages/expense/form/ExpenseRequestKandangDetailExpense.tsx diff --git a/src/components/pages/expense/form/ExpenseRequestKandangDetailExpense.tsx b/src/components/pages/expense/form/ExpenseRequestKandangDetailExpense.tsx new file mode 100644 index 00000000..6b670069 --- /dev/null +++ b/src/components/pages/expense/form/ExpenseRequestKandangDetailExpense.tsx @@ -0,0 +1,285 @@ +'use client'; + +import { FormikContextType } from 'formik'; + +import { Icon } from '@iconify/react'; +import Card from '@/components/Card'; +import SelectInput, { + OptionType, + useSelect, +} from '@/components/input/SelectInput'; +import NumberInput from '@/components/input/NumberInput'; +import TextInput from '@/components/input/TextInput'; +import Button from '@/components/Button'; + +import { ExpenseRequestFormValues } from '@/components/pages/expense/form/ExpenseRequestForm.schema'; +import { cn } from '@/lib/helper'; +import { NonstockApi } from '@/services/api/master-data'; +import { Nonstock } from '@/types/api/master-data/nonstock'; +import { removeArrayItemAndSync } from '@/lib/utils/formik'; + +interface ExpenseRequestKandangDetailExpenseProps { + type?: 'add' | 'edit' | 'detail'; + formik: FormikContextType; + className?: { + wrapper?: string; + }; +} + +const ExpenseRequestKandangDetailExpense: React.FC< + ExpenseRequestKandangDetailExpenseProps +> = ({ type, formik, className }) => { + const { + setInputValue: setNonstockInputValue, + options: nonstockOptions, + isLoadingOptions: isLoadingNonstockOptions, + } = useSelect(NonstockApi.basePath, 'id', 'name'); + + const nonstockChangeHandler = ( + kandangExpenseIdx: number, + expenseIdx: number, + val: OptionType | OptionType[] | null + ) => { + formik.setFieldTouched( + `kandangExpenses[${kandangExpenseIdx}].expenses[${expenseIdx}].nonstock`, + true + ); + formik.setFieldValue( + `kandangExpenses[${kandangExpenseIdx}].expenses[${expenseIdx}].nonstock`, + val + ); + }; + + const addExpenseItemHandler = (kandangExpenseIdx: number) => { + const newExpensesValue = [ + ...formik.values.kandangExpenses[kandangExpenseIdx].expenses, + { + nonstock: undefined, + totalExpense: undefined, + totalQuantity: undefined, + notes: '', + }, + ]; + + formik.setFieldValue( + `kandangExpenses[${kandangExpenseIdx}].expenses`, + newExpensesValue + ); + }; + + const deleteExpenseItemHandler = ( + kandangExpenseIdx: number, + expenseIdx: number + ) => { + const path = `kandangExpenses[${kandangExpenseIdx}].expenses`; + + // trims values, errors, and touched at expenseIdx + removeArrayItemAndSync(formik, path, expenseIdx); + }; + + const isExpenseRepeaterInputError = ( + column: 'nonstock' | 'totalQuantity' | 'totalExpense' | 'notes', + kandangExpenseIdx: number, + expenseIdx: number + ) => { + return ( + formik.touched.kandangExpenses?.[kandangExpenseIdx]?.expenses?.[ + expenseIdx + ]?.[column] && + Boolean( + formik.errors.kandangExpenses?.[kandangExpenseIdx] instanceof Object && + formik.errors.kandangExpenses?.[kandangExpenseIdx].expenses?.[ + expenseIdx + ] instanceof Object && + formik.errors.kandangExpenses?.[kandangExpenseIdx].expenses?.[ + expenseIdx + ]?.[column] + ) + ); + }; + + return ( + +
+

+ Rincian Pengajuan Biaya Operasional +

+
+ +
+ {formik.values.kandangExpenses.length === 0 && ( +
+

+ Pilih kandang terlebih dahulu! +

+
+ )} + + {formik.values.kandangExpenses.map( + (kandangExpense, kandangExpenseIdx) => { + const kandangName = formik.values.kandangs?.find( + (kandang) => kandang.id === kandangExpense.kandangId + ); + + return ( + kandangName?.name && ( +
+
+
+ Biaya {kandangName?.name} +
+ +
+ + + + + + + + {type !== 'detail' && } + + + + + {kandangExpense.expenses.map( + (expenseItem, expenseIdx) => ( + + + + + + + + + + {type !== 'detail' && ( + + )} + + ) + )} + +
NonstockTotal KuantitasTotal BiayaCatatanAksi
+ { + nonstockChangeHandler( + kandangExpenseIdx, + expenseIdx, + val + ); + }} + options={nonstockOptions} + isLoading={isLoadingNonstockOptions} + onInputChange={setNonstockInputValue} + className={{ wrapper: 'min-w-48' }} + /> + + + + + + + + +
+
+
+ + {type !== 'detail' && ( + + )} +
+ ) + ); + } + )} +
+
+ ); +}; + +export default ExpenseRequestKandangDetailExpense;