mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-24 23:35:45 +00:00
refactor(FE-435): Allow realizations without kandang
This commit is contained in:
@@ -12,7 +12,7 @@ type ExpenseRealizationFormSchemaType = {
|
|||||||
label: string;
|
label: string;
|
||||||
};
|
};
|
||||||
realization_date?: string;
|
realization_date?: string;
|
||||||
kandangs?: { id: number; name: string }[];
|
kandangs?: { id?: number; name?: string }[];
|
||||||
supplier?: {
|
supplier?: {
|
||||||
value: number;
|
value: number;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -20,7 +20,7 @@ type ExpenseRealizationFormSchemaType = {
|
|||||||
existing_documents?: { name: string; url: string }[];
|
existing_documents?: { name: string; url: string }[];
|
||||||
documents?: File[];
|
documents?: File[];
|
||||||
realizations: {
|
realizations: {
|
||||||
kandang_id: number;
|
kandang_id?: number;
|
||||||
cost_items: {
|
cost_items: {
|
||||||
nonstock?: {
|
nonstock?: {
|
||||||
value: number;
|
value: number;
|
||||||
@@ -49,12 +49,11 @@ export const ExpenseRealizationFormSchema: Yup.ObjectSchema<ExpenseRealizationFo
|
|||||||
kandangs: Yup.array()
|
kandangs: Yup.array()
|
||||||
.of(
|
.of(
|
||||||
Yup.object({
|
Yup.object({
|
||||||
id: Yup.number().required('Kandang wajib dipilih!'),
|
id: Yup.number().optional(),
|
||||||
name: Yup.string().required('Kandang wajib dipilih!'),
|
name: Yup.string().optional(),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.min(1, 'Kandang wajib dipilih!')
|
.optional(),
|
||||||
.required('Kandang wajib dipilih!'),
|
|
||||||
|
|
||||||
supplier: Yup.object({
|
supplier: Yup.object({
|
||||||
value: Yup.number().min(1).required(),
|
value: Yup.number().min(1).required(),
|
||||||
@@ -73,7 +72,7 @@ export const ExpenseRealizationFormSchema: Yup.ObjectSchema<ExpenseRealizationFo
|
|||||||
realizations: Yup.array()
|
realizations: Yup.array()
|
||||||
.of(
|
.of(
|
||||||
Yup.object({
|
Yup.object({
|
||||||
kandang_id: Yup.number().min(1, 'Wajib memilih kandang!').required(),
|
kandang_id: Yup.number().min(1, 'Wajib memilih kandang!').optional(),
|
||||||
cost_items: Yup.array()
|
cost_items: Yup.array()
|
||||||
.of(
|
.of(
|
||||||
Yup.object({
|
Yup.object({
|
||||||
@@ -86,12 +85,12 @@ export const ExpenseRealizationFormSchema: Yup.ObjectSchema<ExpenseRealizationFo
|
|||||||
notes: Yup.string(),
|
notes: Yup.string(),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.min(1, 'Kandang harus memiliki setidaknya 1 biaya!')
|
.min(1, 'Harus memiliki setidaknya 1 biaya!')
|
||||||
.required('Biaya kandang wajib diisi!'),
|
.required('Biaya wajib diisi!'),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.min(1, 'Biaya kandang wajib diisi!')
|
.min(1, 'Biaya wajib diisi!')
|
||||||
.required('Biaya kandang wajib diisi!'),
|
.required('Biaya wajib diisi!'),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const UpdateExpenseRealizationFormSchema = ExpenseRealizationFormSchema;
|
export const UpdateExpenseRealizationFormSchema = ExpenseRealizationFormSchema;
|
||||||
|
|||||||
@@ -150,29 +150,10 @@ const ExpenseRealizationForm = ({
|
|||||||
formik.setFieldValue('location', val);
|
formik.setFieldValue('location', val);
|
||||||
|
|
||||||
formik.setFieldValue('kandangs', []);
|
formik.setFieldValue('kandangs', []);
|
||||||
formik.setFieldValue('realizations', []);
|
|
||||||
};
|
|
||||||
|
|
||||||
const kandangsChangeHandler = (
|
// Auto-create realization item for location (without kandang)
|
||||||
kandangs: { id?: number; name?: string }[]
|
formik.setFieldValue('realizations', [
|
||||||
) => {
|
{
|
||||||
formik.setFieldTouched('kandangs', true);
|
|
||||||
formik.setFieldValue('kandangs', kandangs);
|
|
||||||
|
|
||||||
const newRealizations = [...(formik.values.realizations ?? [])];
|
|
||||||
|
|
||||||
// add new realizations
|
|
||||||
kandangs.forEach((kandangItem) => {
|
|
||||||
if (!kandangItem.id) return;
|
|
||||||
|
|
||||||
const isKandangExistInRealization = newRealizations.find(
|
|
||||||
(realizationItem) => realizationItem.kandang_id === kandangItem.id
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isKandangExistInRealization) return;
|
|
||||||
|
|
||||||
newRealizations.push({
|
|
||||||
kandang_id: kandangItem.id,
|
|
||||||
cost_items: [
|
cost_items: [
|
||||||
{
|
{
|
||||||
nonstock: undefined,
|
nonstock: undefined,
|
||||||
@@ -181,27 +162,55 @@ const ExpenseRealizationForm = ({
|
|||||||
notes: '',
|
notes: '',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
},
|
||||||
});
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
// prune realizations
|
const kandangsChangeHandler = (
|
||||||
const kandangIds = new Set(
|
kandangs: { id?: number; name?: string }[]
|
||||||
kandangs
|
) => {
|
||||||
.map((kandang) => kandang.id)
|
formik.setFieldTouched('kandangs', true);
|
||||||
.filter((id): id is number => id !== undefined)
|
formik.setFieldValue('kandangs', kandangs);
|
||||||
);
|
|
||||||
const deletedRealizationsIdx: number[] = [];
|
|
||||||
|
|
||||||
newRealizations.forEach((realization, idx) => {
|
// If no kandangs selected, create realization item for location
|
||||||
const isRealizationValid = kandangIds.has(realization.kandang_id);
|
if (kandangs.length === 0) {
|
||||||
|
formik.setFieldValue('realizations', [
|
||||||
if (!isRealizationValid) {
|
{
|
||||||
deletedRealizationsIdx.push(idx);
|
cost_items: [
|
||||||
|
{
|
||||||
|
nonstock: undefined,
|
||||||
|
quantity: undefined,
|
||||||
|
price: undefined,
|
||||||
|
notes: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
deletedRealizationsIdx.forEach((deletedRealizationIdx) => {
|
// Start with empty array when kandangs are selected
|
||||||
newRealizations.splice(deletedRealizationIdx, 1);
|
const newRealizations: typeof formik.values.realizations = [];
|
||||||
|
|
||||||
|
// add new realizations for each kandang
|
||||||
|
kandangs.forEach((kandangItem) => {
|
||||||
|
if (!kandangItem.id) return;
|
||||||
|
|
||||||
|
const existingRealization = formik.values.realizations?.find(
|
||||||
|
(realizationItem) => realizationItem.kandang_id === kandangItem.id
|
||||||
|
);
|
||||||
|
|
||||||
|
newRealizations.push({
|
||||||
|
kandang_id: kandangItem.id,
|
||||||
|
cost_items: existingRealization?.cost_items || [
|
||||||
|
{
|
||||||
|
nonstock: undefined,
|
||||||
|
quantity: undefined,
|
||||||
|
price: undefined,
|
||||||
|
notes: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
formik.setFieldValue('realizations', newRealizations);
|
formik.setFieldValue('realizations', newRealizations);
|
||||||
@@ -346,7 +355,10 @@ const ExpenseRealizationForm = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<ExpenseRealizationKandangDetailExpense
|
<ExpenseRealizationKandangDetailExpense
|
||||||
|
type={type}
|
||||||
formik={formik}
|
formik={formik}
|
||||||
|
supplierId={formik.values.supplier?.value as number}
|
||||||
|
location={formik.values.location}
|
||||||
className={{
|
className={{
|
||||||
wrapper: 'col-span-12',
|
wrapper: 'col-span-12',
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ import { Nonstock } from '@/types/api/master-data/nonstock';
|
|||||||
interface ExpenseRealizationKandangDetailExpenseProps {
|
interface ExpenseRealizationKandangDetailExpenseProps {
|
||||||
type?: 'add' | 'edit' | 'detail';
|
type?: 'add' | 'edit' | 'detail';
|
||||||
formik: FormikContextType<ExpenseRealizationFormValues>;
|
formik: FormikContextType<ExpenseRealizationFormValues>;
|
||||||
|
supplierId?: number;
|
||||||
|
location?: {
|
||||||
|
value: number;
|
||||||
|
label: string;
|
||||||
|
};
|
||||||
className?: {
|
className?: {
|
||||||
wrapper?: string;
|
wrapper?: string;
|
||||||
};
|
};
|
||||||
@@ -25,12 +30,18 @@ interface ExpenseRealizationKandangDetailExpenseProps {
|
|||||||
|
|
||||||
const ExpenseRealizationKandangDetailExpense: React.FC<
|
const ExpenseRealizationKandangDetailExpense: React.FC<
|
||||||
ExpenseRealizationKandangDetailExpenseProps
|
ExpenseRealizationKandangDetailExpenseProps
|
||||||
> = ({ type, formik, className }) => {
|
> = ({ type, formik, supplierId, location, className }) => {
|
||||||
const {
|
const {
|
||||||
setInputValue: setNonstockInputValue,
|
setInputValue: setNonstockInputValue,
|
||||||
options: nonstockOptions,
|
options: nonstockOptions,
|
||||||
isLoadingOptions: isLoadingNonstockOptions,
|
isLoadingOptions: isLoadingNonstockOptions,
|
||||||
} = useSelect<Nonstock>(NonstockApi.basePath, 'id', 'name');
|
} = useSelect<Nonstock>(
|
||||||
|
NonstockApi.basePath,
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'search',
|
||||||
|
supplierId ? { supplier_id: String(supplierId) } : undefined
|
||||||
|
);
|
||||||
|
|
||||||
const nonstockChangeHandler = (
|
const nonstockChangeHandler = (
|
||||||
kandangExpenseIdx: number,
|
kandangExpenseIdx: number,
|
||||||
@@ -82,28 +93,42 @@ const ExpenseRealizationKandangDetailExpense: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='w-full flex flex-col gap-6'>
|
<div className='w-full flex flex-col gap-6'>
|
||||||
{formik.values.realizations.length === 0 && (
|
{!formik.values.supplier?.value && (
|
||||||
<div>
|
<div>
|
||||||
<p className='text-sm text-gray-400 text-center'>
|
<p className='text-sm text-gray-400 text-center'>
|
||||||
Pilih kandang terlebih dahulu!
|
Pilih supplier terlebih dahulu!
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{formik.values.realizations.map((kandangExpense, kandangExpenseIdx) => {
|
{formik.values.realizations.length === 0 &&
|
||||||
const kandangName = formik.values.kandangs?.find(
|
formik.values.supplier?.value && (
|
||||||
|
<div>
|
||||||
|
<p className='text-sm text-gray-400 text-center'>
|
||||||
|
Belum ada item biaya. Silakan pilih lokasi terlebih dahulu.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{formik.values.realizations.length > 0 &&
|
||||||
|
formik.values.supplier?.value &&
|
||||||
|
formik.values.realizations.map(
|
||||||
|
(kandangExpense, kandangExpenseIdx) => {
|
||||||
|
const kandangName = kandangExpense.kandang_id
|
||||||
|
? formik.values.kandangs?.find(
|
||||||
(kandang) => kandang.id === kandangExpense.kandang_id
|
(kandang) => kandang.id === kandangExpense.kandang_id
|
||||||
);
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
kandangName?.name && (
|
(kandangName?.name || !kandangExpense.kandang_id) && (
|
||||||
<div
|
<div
|
||||||
key={`kandangExpense-${kandangExpenseIdx}`}
|
key={`kandangExpense-${kandangExpenseIdx}`}
|
||||||
className='w-full flex flex-col gap-4'
|
className='w-full flex flex-col gap-4'
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<h5 className='mb-2 text-lg font-bold text-center'>
|
<h5 className='mb-2 text-lg font-bold text-center'>
|
||||||
Biaya {kandangName?.name}
|
Biaya {kandangName?.name || location?.label || 'Umum'}
|
||||||
</h5>
|
</h5>
|
||||||
|
|
||||||
<div className='overflow-x-auto'>
|
<div className='overflow-x-auto'>
|
||||||
@@ -215,7 +240,8 @@ const ExpenseRealizationKandangDetailExpense: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
})}
|
}
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ type ExpenseFormSchemaType = {
|
|||||||
deleted_documents?: number[];
|
deleted_documents?: number[];
|
||||||
documents?: File[];
|
documents?: File[];
|
||||||
expense_nonstocks: {
|
expense_nonstocks: {
|
||||||
kandang_id?: number | null;
|
kandang_id?: number;
|
||||||
cost_items: {
|
cost_items: {
|
||||||
nonstock?: {
|
nonstock?: {
|
||||||
value: number;
|
value: number;
|
||||||
@@ -79,10 +79,7 @@ export const ExpenseRequestFormSchema: Yup.ObjectSchema<ExpenseFormSchemaType> =
|
|||||||
expense_nonstocks: Yup.array()
|
expense_nonstocks: Yup.array()
|
||||||
.of(
|
.of(
|
||||||
Yup.object({
|
Yup.object({
|
||||||
kandang_id: Yup.number()
|
kandang_id: Yup.number().min(1, 'Wajib memilih kandang!').optional(),
|
||||||
.min(1, 'Wajib memilih kandang!')
|
|
||||||
.nullable()
|
|
||||||
.optional(),
|
|
||||||
cost_items: Yup.array()
|
cost_items: Yup.array()
|
||||||
.of(
|
.of(
|
||||||
Yup.object({
|
Yup.object({
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ const ExpenseRequestForm = ({
|
|||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
|
||||||
return expenseNonstock.kandang_id !== null
|
return expenseNonstock.kandang_id
|
||||||
? { ...basePayload, kandang_id: expenseNonstock.kandang_id }
|
? { ...basePayload, kandang_id: expenseNonstock.kandang_id }
|
||||||
: basePayload;
|
: basePayload;
|
||||||
}),
|
}),
|
||||||
@@ -151,7 +151,7 @@ const ExpenseRequestForm = ({
|
|||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
|
||||||
return expenseNonstock.kandang_id !== null
|
return expenseNonstock.kandang_id
|
||||||
? { ...basePayload, kandang_id: expenseNonstock.kandang_id }
|
? { ...basePayload, kandang_id: expenseNonstock.kandang_id }
|
||||||
: basePayload;
|
: basePayload;
|
||||||
}
|
}
|
||||||
@@ -199,7 +199,6 @@ const ExpenseRequestForm = ({
|
|||||||
// Auto-create expense item for location (without kandang)
|
// Auto-create expense item for location (without kandang)
|
||||||
formik.setFieldValue('expense_nonstocks', [
|
formik.setFieldValue('expense_nonstocks', [
|
||||||
{
|
{
|
||||||
kandang_id: null,
|
|
||||||
cost_items: [
|
cost_items: [
|
||||||
{
|
{
|
||||||
nonstock: undefined,
|
nonstock: undefined,
|
||||||
@@ -222,7 +221,6 @@ const ExpenseRequestForm = ({
|
|||||||
if (kandangs.length === 0) {
|
if (kandangs.length === 0) {
|
||||||
formik.setFieldValue('expense_nonstocks', [
|
formik.setFieldValue('expense_nonstocks', [
|
||||||
{
|
{
|
||||||
kandang_id: null,
|
|
||||||
cost_items: [
|
cost_items: [
|
||||||
{
|
{
|
||||||
nonstock: undefined,
|
nonstock: undefined,
|
||||||
|
|||||||
Reference in New Issue
Block a user