refactor(FE): Refactor form field change handlers for real-time

calculations
This commit is contained in:
rstubryan
2026-02-23 15:30:54 +07:00
parent 81e4e1fc6a
commit 35b09b514f
4 changed files with 158 additions and 72 deletions
@@ -44,13 +44,29 @@ export const DeliveryOrderProductSchema: Yup.ObjectSchema<DeliveryOrderProductSc
.min(1, 'Harga Satuan wajib diisi!')
.required('Harga Satuan wajib diisi!'),
total_weight: Yup.number()
.transform((value) =>
value === '' || value === null ? 0 : Number(value)
)
.min(0, 'Total Bobot wajib diisi!')
.test(
'is-greater-than-zero',
'Total Bobot harus lebih dari 0!',
(value) => value !== undefined && value > 0
)
.required('Total Bobot wajib diisi!'),
qty: Yup.number()
.min(1, 'Kuantitas wajib diisi!')
.required('Kuantitas wajib diisi!'),
avg_weight: Yup.number()
.transform((value) =>
value === '' || value === null ? 0 : Number(value)
)
.min(0, 'Avg. Bobot wajib diisi!')
.test(
'is-greater-than-zero',
'Avg. Bobot harus lebih dari 0!',
(value) => value !== undefined && value > 0
)
.required('Avg. Bobot wajib diisi!'),
total_price: Yup.number()
.min(1, 'Total Penjualan wajib diisi!')
@@ -236,6 +236,25 @@ const DeliveryOrderProductForm = ({
});
};
// Handler untuk onChange - auto calculation real-time untuk field yang mempengaruhi total_price (total_peti, weight_per_convertion, price_per_convertion, sisa_berat, price_sisa_berat, price_per_qty, qty)
const handleFieldChange = (
field: string,
value: number | string,
callback?: () => void
) => {
formik.setFieldValue(field, value);
setTimeout(() => {
handleMarketingCalculation(field, {
values: { ...formik.values, [field]: value },
setFieldValue: formik.setFieldValue,
hasSisaBerat,
});
}, 0);
if (callback) callback();
};
// Handler khusus untuk toggle sisa berat - langsung pakai nilai baru
const handleSisaBeratToggle = (newHasSisaBerat: boolean) => {
setHasSisaBerat(newHasSisaBerat);
@@ -520,13 +539,11 @@ const DeliveryOrderProductForm = ({
} per ${formik.values.convertion_unit?.value}`}
value={formik.values.weight_per_convertion ?? ''}
onChange={(e) => {
formik.setFieldValue(
'weight_per_convertion',
Number(e.target.value)
const value = Number(e.target.value);
handleFieldChange('weight_per_convertion', value, () =>
setCurrentInput(e.target.name)
);
setCurrentInput(e.target.name);
}}
onBlur={() => handleBlurField('weight_per_convertion')}
/>
</div>
</div>
@@ -564,10 +581,11 @@ const DeliveryOrderProductForm = ({
name='total_peti'
value={formik.values.total_peti ?? undefined}
onChange={(e) => {
formik.handleChange(e);
setCurrentInput(e.target.name);
const value = Number(e.target.value);
handleFieldChange('total_peti', value, () =>
setCurrentInput(e.target.name)
);
}}
onBlur={() => handleBlurField('total_peti')}
isError={
formik.touched.total_peti && Boolean(formik.errors.total_peti)
}
@@ -592,10 +610,11 @@ const DeliveryOrderProductForm = ({
name='avg_weight'
value={formik.values.avg_weight}
onChange={(e) => {
formik.handleChange(e);
setCurrentInput(e.target.name);
const value = Number(e.target.value);
handleFieldChange('avg_weight', value, () =>
setCurrentInput('avg_weight')
);
}}
onBlur={() => handleBlurField('avg_weight')}
isError={
formik.touched.avg_weight &&
Boolean(formik.errors.avg_weight)
@@ -613,10 +632,11 @@ const DeliveryOrderProductForm = ({
name='total_weight'
value={formik.values.total_weight}
onChange={(e) => {
formik.handleChange(e);
setCurrentInput(e.target.name);
const value = Number(e.target.value);
handleFieldChange('total_weight', value, () =>
setCurrentInput('total_weight')
);
}}
onBlur={() => handleBlurField('total_weight')}
isError={
formik.touched.total_weight &&
Boolean(formik.errors.total_weight)
@@ -638,10 +658,11 @@ const DeliveryOrderProductForm = ({
name='qty'
value={formik.values.qty}
onChange={(e) => {
formik.handleChange(e);
setCurrentInput(e.target.name);
const value = Number(e.target.value);
handleFieldChange('qty', value, () =>
setCurrentInput(e.target.name)
);
}}
onBlur={() => handleBlurField('qty')}
isError={Boolean(formik.errors.qty)}
errorMessage={formik.errors.qty}
placeholder='Masukan Kuantitas'
@@ -677,10 +698,11 @@ const DeliveryOrderProductForm = ({
name='price_per_convertion'
value={formik.values.price_per_convertion ?? undefined}
onChange={(e) => {
formik.handleChange(e);
setCurrentInput(e.target.name);
const value = Number(e.target.value);
handleFieldChange('price_per_convertion', value, () =>
setCurrentInput(e.target.name)
);
}}
onBlur={() => handleBlurField('price_per_convertion')}
isError={
formik.touched.price_per_convertion &&
Boolean(formik.errors.price_per_convertion)
@@ -699,10 +721,11 @@ const DeliveryOrderProductForm = ({
name='price_per_qty'
value={formik.values.price_per_qty ?? undefined}
onChange={(e) => {
formik.setFieldValue('price_per_qty', Number(e.target.value));
setCurrentInput('price_per_qty');
const value = Number(e.target.value);
handleFieldChange('price_per_qty', value, () =>
setCurrentInput('price_per_qty')
);
}}
onBlur={() => handleBlurField('price_per_qty')}
isError={
formik.touched.price_per_qty &&
Boolean(formik.errors.price_per_qty)
@@ -721,10 +744,11 @@ const DeliveryOrderProductForm = ({
name='unit_price'
value={formik.values.unit_price}
onChange={(e) => {
formik.handleChange(e);
setCurrentInput(e.target.name);
const value = Number(e.target.value);
handleFieldChange('unit_price', value, () =>
setCurrentInput(e.target.name)
);
}}
onBlur={() => handleBlurField('unit_price')}
isError={Boolean(formik.errors.unit_price)}
errorMessage={formik.errors.unit_price}
placeholder='Masukan Harga Satuan'
@@ -760,10 +784,11 @@ const DeliveryOrderProductForm = ({
name='sisa_berat'
value={formik.values.sisa_berat ?? undefined}
onChange={(e) => {
formik.handleChange(e);
setCurrentInput(e.target.name);
const value = Number(e.target.value);
handleFieldChange('sisa_berat', value, () =>
setCurrentInput(e.target.name)
);
}}
onBlur={() => handleBlurField('sisa_berat')}
isError={
formik.touched.sisa_berat && Boolean(formik.errors.sisa_berat)
}
@@ -776,10 +801,11 @@ const DeliveryOrderProductForm = ({
name='price_sisa_berat'
value={formik.values.price_sisa_berat ?? undefined}
onChange={(e) => {
formik.handleChange(e);
setCurrentInput(e.target.name);
const value = Number(e.target.value);
handleFieldChange('price_sisa_berat', value, () =>
setCurrentInput(e.target.name)
);
}}
onBlur={() => handleBlurField('price_sisa_berat')}
isError={
formik.touched.price_sisa_berat &&
Boolean(formik.errors.price_sisa_berat)
@@ -797,10 +823,11 @@ const DeliveryOrderProductForm = ({
name='total_price'
value={formik.values.total_price}
onChange={(e) => {
formik.handleChange(e);
setCurrentInput(e.target.name);
const value = Number(e.target.value);
handleFieldChange('total_price', value, () =>
setCurrentInput('total_price')
);
}}
onBlur={() => handleBlurField('total_price')}
isError={
formik.touched.total_price && Boolean(formik.errors.total_price)
}
@@ -70,13 +70,29 @@ export const SalesOrderProductSchema: Yup.ObjectSchema<SalesOrderProductSchemaTy
.min(1, 'Harga Satuan wajib diisi!')
.required('Harga Satuan wajib diisi!'),
total_weight: Yup.number()
.min(1, 'Total Bobot wajib diisi!')
.transform((value) =>
value === '' || value === null ? 0 : Number(value)
)
.min(0, 'Total Bobot tidak boleh negatif!')
.test(
'is-greater-than-zero',
'Total Bobot harus lebih dari 0!',
(value) => value !== undefined && value > 0
)
.required('Total Bobot wajib diisi!'),
qty: Yup.number()
.min(1, 'Kuantitas wajib diisi!')
.required('Kuantitas wajib diisi!'),
avg_weight: Yup.number()
.min(1, 'Avg. Bobot wajib diisi!')
.transform((value) =>
value === '' || value === null ? 0 : Number(value)
)
.min(0, 'Avg. Bobot wajib diisi!')
.test(
'is-greater-than-zero',
'Avg. Bobot harus lebih dari 0!',
(value) => value !== undefined && value > 0
)
.required('Avg. Bobot wajib diisi!'),
total_price: Yup.number()
.min(1, 'Total Penjualan wajib diisi!')
@@ -250,6 +250,25 @@ const SalesOrderProductForm = ({
});
};
// Handler untuk onChange - auto calculation real-time untuk field yang mempengaruhi total_price (total_peti, weight_per_convertion, price_per_convertion, sisa_berat, price_sisa_berat, price_per_qty, qty)
const handleFieldChange = (
field: string,
value: number | string,
callback?: () => void
) => {
formik.setFieldValue(field, value);
setTimeout(() => {
handleMarketingCalculation(field, {
values: { ...formik.values, [field]: value },
setFieldValue: formik.setFieldValue,
hasSisaBerat,
});
}, 0);
if (callback) callback();
};
// Handler khusus untuk toggle sisa berat - langsung pakai nilai baru
const handleSisaBeratToggle = (newHasSisaBerat: boolean) => {
setHasSisaBerat(newHasSisaBerat);
@@ -475,13 +494,11 @@ const SalesOrderProductForm = ({
} per ${formik.values.convertion_unit?.value}`}
value={formik.values.weight_per_convertion ?? ''}
onChange={(e) => {
formik.setFieldValue(
'weight_per_convertion',
Number(e.target.value)
const value = Number(e.target.value);
handleFieldChange('weight_per_convertion', value, () =>
setCurrentInput(e.target.name)
);
setCurrentInput(e.target.name);
}}
onBlur={() => handleBlurField('weight_per_convertion')}
/>
</div>
</div>
@@ -519,10 +536,11 @@ const SalesOrderProductForm = ({
name='total_peti'
value={formik.values.total_peti ?? undefined}
onChange={(e) => {
formik.handleChange(e);
setCurrentInput(e.target.name);
const value = Number(e.target.value);
handleFieldChange('total_peti', value, () =>
setCurrentInput(e.target.name)
);
}}
onBlur={() => handleBlurField('total_peti')}
isError={
formik.touched.total_peti && Boolean(formik.errors.total_peti)
}
@@ -547,10 +565,11 @@ const SalesOrderProductForm = ({
name='avg_weight'
value={formik.values.avg_weight}
onChange={(e) => {
formik.handleChange(e);
setCurrentInput(e.target.name);
const value = Number(e.target.value);
handleFieldChange('avg_weight', value, () =>
setCurrentInput('avg_weight')
);
}}
onBlur={() => handleBlurField('avg_weight')}
isError={
formik.touched.avg_weight &&
Boolean(formik.errors.avg_weight)
@@ -568,10 +587,11 @@ const SalesOrderProductForm = ({
name='total_weight'
value={formik.values.total_weight}
onChange={(e) => {
formik.handleChange(e);
setCurrentInput(e.target.name);
const value = Number(e.target.value);
handleFieldChange('total_weight', value, () =>
setCurrentInput('total_weight')
);
}}
onBlur={() => handleBlurField('total_weight')}
isError={
formik.touched.total_weight &&
Boolean(formik.errors.total_weight)
@@ -593,10 +613,11 @@ const SalesOrderProductForm = ({
name='qty'
value={formik.values.qty}
onChange={(e) => {
formik.handleChange(e);
setCurrentInput(e.target.name);
const value = Number(e.target.value);
handleFieldChange('qty', value, () =>
setCurrentInput(e.target.name)
);
}}
onBlur={() => handleBlurField('qty')}
isError={formik.touched.qty && Boolean(formik.errors.qty)}
errorMessage={formik.errors.qty}
placeholder='Masukan Kuantitas'
@@ -630,10 +651,11 @@ const SalesOrderProductForm = ({
name='price_per_convertion'
value={formik.values.price_per_convertion ?? undefined}
onChange={(e) => {
formik.handleChange(e);
setCurrentInput(e.target.name);
const value = Number(e.target.value);
handleFieldChange('price_per_convertion', value, () =>
setCurrentInput(e.target.name)
);
}}
onBlur={() => handleBlurField('price_per_convertion')}
isError={
formik.touched.price_per_convertion &&
Boolean(formik.errors.price_per_convertion)
@@ -652,10 +674,11 @@ const SalesOrderProductForm = ({
name='price_per_qty'
value={formik.values.price_per_qty ?? undefined}
onChange={(e) => {
formik.setFieldValue('price_per_qty', Number(e.target.value));
setCurrentInput('price_per_qty');
const value = Number(e.target.value);
handleFieldChange('price_per_qty', value, () =>
setCurrentInput('price_per_qty')
);
}}
onBlur={() => handleBlurField('price_per_qty')}
isError={
formik.touched.price_per_qty &&
Boolean(formik.errors.price_per_qty)
@@ -674,10 +697,11 @@ const SalesOrderProductForm = ({
name='unit_price'
value={formik.values.unit_price}
onChange={(e) => {
formik.handleChange(e);
setCurrentInput(e.target.name);
const value = Number(e.target.value);
handleFieldChange('unit_price', value, () =>
setCurrentInput(e.target.name)
);
}}
onBlur={() => handleBlurField('unit_price')}
isError={
formik.touched.unit_price && Boolean(formik.errors.unit_price)
}
@@ -715,10 +739,11 @@ const SalesOrderProductForm = ({
name='sisa_berat'
value={formik.values.sisa_berat ?? undefined}
onChange={(e) => {
formik.handleChange(e);
setCurrentInput(e.target.name);
const value = Number(e.target.value);
handleFieldChange('sisa_berat', value, () =>
setCurrentInput(e.target.name)
);
}}
onBlur={() => handleBlurField('sisa_berat')}
isError={
formik.touched.sisa_berat && Boolean(formik.errors.sisa_berat)
}
@@ -731,10 +756,11 @@ const SalesOrderProductForm = ({
name='price_sisa_berat'
value={formik.values.price_sisa_berat ?? undefined}
onChange={(e) => {
formik.handleChange(e);
setCurrentInput(e.target.name);
const value = Number(e.target.value);
handleFieldChange('price_sisa_berat', value, () =>
setCurrentInput(e.target.name)
);
}}
onBlur={() => handleBlurField('price_sisa_berat')}
isError={
formik.touched.price_sisa_berat &&
Boolean(formik.errors.price_sisa_berat)
@@ -752,10 +778,11 @@ const SalesOrderProductForm = ({
name='total_price'
value={formik.values.total_price}
onChange={(e) => {
formik.handleChange(e);
setCurrentInput(e.target.name);
const value = Number(e.target.value);
handleFieldChange('total_price', value, () =>
setCurrentInput('total_price')
);
}}
onBlur={() => handleBlurField('total_price')}
isError={
formik.touched.total_price && Boolean(formik.errors.total_price)
}