refactor(FE-114): enhance body weight calculations in RecordingForm with auto-update for average weight

This commit is contained in:
rstubryan
2025-10-21 09:54:28 +07:00
parent 1ecdff855e
commit fb29cea8d2
@@ -1,6 +1,7 @@
'use client'; 'use client';
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { ChangeEvent } from 'react';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { Icon } from '@iconify/react'; import { Icon } from '@iconify/react';
import Button from '@/components/Button'; import Button from '@/components/Button';
@@ -77,22 +78,46 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
: '', : '',
feed_data: (values.feed_data ?? []).map((p) => ({ feed_data: (values.feed_data ?? []).map((p) => ({
feed_id: p.feed_id, feed_id: p.feed_id,
feed_qty: typeof p.feed_qty === 'number' ? p.feed_qty : parseFloat(String(p.feed_qty)) || 0, feed_qty:
feed_stock: typeof p.feed_stock === 'number' ? p.feed_stock : parseFloat(String(p.feed_stock)) || 0, typeof p.feed_qty === 'number'
? p.feed_qty
: parseFloat(String(p.feed_qty)) || 0,
feed_stock:
typeof p.feed_stock === 'number'
? p.feed_stock
: parseFloat(String(p.feed_stock)) || 0,
})), })),
body_weight: (values.body_weight ?? []).map((b) => ({ body_weight: (values.body_weight ?? []).map((b) => ({
chicken_weight: typeof b.chicken_weight === 'number' ? b.chicken_weight : parseFloat(String(b.chicken_weight)) || 0, chicken_weight:
chicken_count: typeof b.chicken_count === 'number' ? b.chicken_count : parseFloat(String(b.chicken_count)) || 0, typeof b.chicken_weight === 'number'
average_chicken_weight: typeof b.average_chicken_weight === 'number' ? b.average_chicken_weight : parseFloat(String(b.average_chicken_weight)) || 0, ? b.chicken_weight
: parseFloat(String(b.chicken_weight)) || 0,
chicken_count:
typeof b.chicken_count === 'number'
? b.chicken_count
: parseFloat(String(b.chicken_count)) || 0,
average_chicken_weight:
typeof b.average_chicken_weight === 'number'
? b.average_chicken_weight
: parseFloat(String(b.average_chicken_weight)) || 0,
})), })),
vaccination: (values.vaccination ?? []).map((v) => ({ vaccination: (values.vaccination ?? []).map((v) => ({
vaccine_id: v.vaccine_id, vaccine_id: v.vaccine_id,
total_stock: typeof v.total_stock === 'number' ? v.total_stock : parseFloat(String(v.total_stock)) || 0, total_stock:
used_stock: typeof v.used_stock === 'number' ? v.used_stock : parseFloat(String(v.used_stock)) || 0, typeof v.total_stock === 'number'
? v.total_stock
: parseFloat(String(v.total_stock)) || 0,
used_stock:
typeof v.used_stock === 'number'
? v.used_stock
: parseFloat(String(v.used_stock)) || 0,
})), })),
mortality: (values.mortality ?? []).map((m) => ({ mortality: (values.mortality ?? []).map((m) => ({
condition: m.condition, condition: m.condition,
count: typeof m.count === 'number' ? m.count : parseFloat(String(m.count)) || 0, count:
typeof m.count === 'number'
? m.count
: parseFloat(String(m.count)) || 0,
})), })),
}; };
@@ -283,6 +308,33 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
} }
}, [initialValues, projectFlocks]); }, [initialValues, projectFlocks]);
// Auto-calculate average weight when chicken weight or count changes
useEffect(() => {
if (formik.values.body_weight) {
const updatedBodyWeight = formik.values.body_weight.map((weight) => ({
...weight,
average_chicken_weight:
weight.chicken_count > 0
? Math.round(weight.chicken_weight / weight.chicken_count)
: 0,
}));
// Only update if values are different to avoid infinite loops
const hasChanges = updatedBodyWeight.some(
(updated, idx) =>
updated.average_chicken_weight !==
formik.values.body_weight[idx]?.average_chicken_weight
);
if (hasChanges) {
formik.setFieldValue('body_weight', updatedBodyWeight);
}
}
}, [
formik.values.body_weight?.map((w) => w.chicken_weight),
formik.values.body_weight?.map((w) => w.chicken_count),
]);
// EVENT HANDLERS - Select Inputs // EVENT HANDLERS - Select Inputs
const locationChangeHandler = (val: OptionType | OptionType[] | null) => { const locationChangeHandler = (val: OptionType | OptionType[] | null) => {
const locationValue = (val as OptionType)?.value; const locationValue = (val as OptionType)?.value;
@@ -363,6 +415,28 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
formik.setFieldValue('body_weight', newBodyWeight); formik.setFieldValue('body_weight', newBodyWeight);
}; };
const handleBodyWeightChange =
(fieldName: string, idx: number) =>
(e: React.ChangeEvent<HTMLInputElement>) => {
formik.handleChange(e);
setTimeout(() => {
const currentWeight = formik.values.body_weight?.[idx];
if (currentWeight) {
const newAverage =
currentWeight.chicken_count > 0
? Math.round(
currentWeight.chicken_weight / currentWeight.chicken_count
)
: 0;
formik.setFieldValue(
`body_weight.${idx}.average_chicken_weight`,
newAverage
);
}
}, 0);
};
const removeBodyWeight = (idx: number) => { const removeBodyWeight = (idx: number) => {
const updatedBodyWeight = formik.values.body_weight?.filter( const updatedBodyWeight = formik.values.body_weight?.filter(
(_, i) => i !== idx (_, i) => i !== idx
@@ -865,7 +939,10 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
required required
name={`body_weight.${idx}.chicken_weight`} name={`body_weight.${idx}.chicken_weight`}
value={weight.chicken_weight} value={weight.chicken_weight}
onChange={formik.handleChange} onChange={handleBodyWeightChange(
'chicken_weight',
idx
)}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
maskType='weight' maskType='weight'
weightUnit='gram' weightUnit='gram'
@@ -896,7 +973,10 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
required required
name={`body_weight.${idx}.chicken_count`} name={`body_weight.${idx}.chicken_count`}
value={weight.chicken_count} value={weight.chicken_count}
onChange={formik.handleChange} onChange={handleBodyWeightChange(
'chicken_count',
idx
)}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
maskType='number' maskType='number'
decimals={0} decimals={0}
@@ -922,35 +1002,46 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
/> />
</td> </td>
<td> <td>
<NumberInput <div className='relative'>
required <NumberInput
name={`body_weight.${idx}.average_chicken_weight`} required
value={weight.average_chicken_weight} name={`body_weight.${idx}.average_chicken_weight`}
onChange={formik.handleChange} value={
onBlur={formik.handleBlur} weight.chicken_count > 0
maskType='weight' ? Math.round(
weightUnit='gram' weight.chicken_weight /
decimals={0} weight.chicken_count
min={0} )
isError={ : 0
isRepeaterInputError( }
'body_weight', onChange={formik.handleChange}
'average_chicken_weight', onBlur={formik.handleBlur}
idx maskType='weight'
).isError weightUnit='gram'
} decimals={0}
errorMessage={ min={0}
isRepeaterInputError( isError={
'body_weight', isRepeaterInputError(
'average_chicken_weight', 'body_weight',
idx 'average_chicken_weight',
).errorMessage idx
} ).isError
readOnly={type === 'detail'} }
className={{ errorMessage={
wrapper: 'w-full min-w-24', isRepeaterInputError(
}} 'body_weight',
/> 'average_chicken_weight',
idx
).errorMessage
}
readOnly={true}
disabled={true}
className={{
wrapper: 'w-full min-w-24',
}}
placeholder='0'
/>
</div>
</td> </td>
{type !== 'detail' && ( {type !== 'detail' && (
<td> <td>