mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
feat(FE-188,193): create ExpenseRequestKandangDetailExpense component
This commit is contained in:
@@ -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<ExpenseRequestFormValues>;
|
||||
className?: {
|
||||
wrapper?: string;
|
||||
};
|
||||
}
|
||||
|
||||
const ExpenseRequestKandangDetailExpense: React.FC<
|
||||
ExpenseRequestKandangDetailExpenseProps
|
||||
> = ({ type, formik, className }) => {
|
||||
const {
|
||||
setInputValue: setNonstockInputValue,
|
||||
options: nonstockOptions,
|
||||
isLoadingOptions: isLoadingNonstockOptions,
|
||||
} = useSelect<Nonstock>(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 (
|
||||
<Card
|
||||
className={{
|
||||
wrapper: cn('w-full', className?.wrapper),
|
||||
body: 'p-4 shadow',
|
||||
}}
|
||||
>
|
||||
<div className='mb-4 text-center'>
|
||||
<h4 className='font-bold text-xl'>
|
||||
Rincian Pengajuan Biaya Operasional
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div className='w-full flex flex-col gap-6'>
|
||||
{formik.values.kandangExpenses.length === 0 && (
|
||||
<div>
|
||||
<p className='text-sm text-gray-400 text-center'>
|
||||
Pilih kandang terlebih dahulu!
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{formik.values.kandangExpenses.map(
|
||||
(kandangExpense, kandangExpenseIdx) => {
|
||||
const kandangName = formik.values.kandangs?.find(
|
||||
(kandang) => kandang.id === kandangExpense.kandangId
|
||||
);
|
||||
|
||||
return (
|
||||
kandangName?.name && (
|
||||
<div
|
||||
key={`kandangExpense-${kandangExpenseIdx}`}
|
||||
className='w-full flex flex-col gap-4'
|
||||
>
|
||||
<div>
|
||||
<h5 className='mb-2 text-lg font-bold text-center'>
|
||||
Biaya {kandangName?.name}
|
||||
</h5>
|
||||
|
||||
<div className='overflow-x-auto'>
|
||||
<table className='table'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nonstock</th>
|
||||
<th>Total Kuantitas</th>
|
||||
<th>Total Biaya</th>
|
||||
<th>Catatan</th>
|
||||
{type !== 'detail' && <th>Aksi</th>}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{kandangExpense.expenses.map(
|
||||
(expenseItem, expenseIdx) => (
|
||||
<tr key={`expense-${expenseIdx}`}>
|
||||
<td className='p-2'>
|
||||
<SelectInput
|
||||
placeholder='Pilih Nonstock'
|
||||
value={expenseItem.nonstock}
|
||||
onChange={(val) => {
|
||||
nonstockChangeHandler(
|
||||
kandangExpenseIdx,
|
||||
expenseIdx,
|
||||
val
|
||||
);
|
||||
}}
|
||||
options={nonstockOptions}
|
||||
isLoading={isLoadingNonstockOptions}
|
||||
onInputChange={setNonstockInputValue}
|
||||
className={{ wrapper: 'min-w-48' }}
|
||||
/>
|
||||
</td>
|
||||
|
||||
<td className='p-2'>
|
||||
<NumberInput
|
||||
required
|
||||
name={`kandangExpenses[${kandangExpenseIdx}].expenses[${expenseIdx}].totalQuantity`}
|
||||
placeholder='Masukkan Total Kuantitas'
|
||||
value={
|
||||
formik.values.kandangExpenses[
|
||||
kandangExpenseIdx
|
||||
].expenses[expenseIdx].totalQuantity ?? ''
|
||||
}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
isError={isExpenseRepeaterInputError(
|
||||
'totalQuantity',
|
||||
kandangExpenseIdx,
|
||||
expenseIdx
|
||||
)}
|
||||
className={{ wrapper: 'min-w-24' }}
|
||||
/>
|
||||
</td>
|
||||
|
||||
<td className='p-2'>
|
||||
<NumberInput
|
||||
name={`kandangExpenses[${kandangExpenseIdx}].expenses[${expenseIdx}].totalExpense`}
|
||||
placeholder='Masukkan Total Biaya'
|
||||
value={
|
||||
formik.values.kandangExpenses[
|
||||
kandangExpenseIdx
|
||||
].expenses[expenseIdx].totalExpense ?? ''
|
||||
}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
isError={isExpenseRepeaterInputError(
|
||||
'totalExpense',
|
||||
kandangExpenseIdx,
|
||||
expenseIdx
|
||||
)}
|
||||
className={{ wrapper: 'min-w-24' }}
|
||||
/>
|
||||
</td>
|
||||
|
||||
<td className='p-2'>
|
||||
<TextInput
|
||||
name={`kandangExpenses[${kandangExpenseIdx}].expenses[${expenseIdx}].notes`}
|
||||
placeholder='Tuliskan catatan'
|
||||
value={
|
||||
formik.values.kandangExpenses[
|
||||
kandangExpenseIdx
|
||||
].expenses[expenseIdx].notes
|
||||
}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
isError={isExpenseRepeaterInputError(
|
||||
'notes',
|
||||
kandangExpenseIdx,
|
||||
expenseIdx
|
||||
)}
|
||||
className={{ wrapper: 'min-w-24' }}
|
||||
/>
|
||||
</td>
|
||||
|
||||
{type !== 'detail' && (
|
||||
<td>
|
||||
<Button
|
||||
type='button'
|
||||
color='error'
|
||||
onClick={() =>
|
||||
deleteExpenseItemHandler(
|
||||
kandangExpenseIdx,
|
||||
expenseIdx
|
||||
)
|
||||
}
|
||||
>
|
||||
<Icon
|
||||
icon='material-symbols:delete-outline-rounded'
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
</Button>
|
||||
</td>
|
||||
)}
|
||||
</tr>
|
||||
)
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{type !== 'detail' && (
|
||||
<Button
|
||||
type='button'
|
||||
variant='outline'
|
||||
color='success'
|
||||
onClick={() => addExpenseItemHandler(kandangExpenseIdx)}
|
||||
className='w-fit mx-auto'
|
||||
>
|
||||
<Icon icon='ic:round-plus' width={24} height={24} />{' '}
|
||||
Tambah
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExpenseRequestKandangDetailExpense;
|
||||
Reference in New Issue
Block a user