feat(US-114): enhance auto-calculation logic in RecordingForm to handle manual edits

This commit is contained in:
rstubryan
2025-10-24 11:00:14 +07:00
parent 9cbc703a63
commit adc995dbe7
@@ -45,6 +45,9 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
// Track which average weight field is being edited to prevent auto-calculation override // Track which average weight field is being edited to prevent auto-calculation override
const [editingAverageIndex, setEditingAverageIndex] = useState<number | null>(null); const [editingAverageIndex, setEditingAverageIndex] = useState<number | null>(null);
// Track which rows have been manually edited to prevent auto-calculation override
const [manuallyEditedRows, setManuallyEditedRows] = useState<Set<number>>(new Set());
// State for Location search and selection // State for Location search and selection
const [locationSearchValue, setLocationSearchValue] = useState(''); const [locationSearchValue, setLocationSearchValue] = useState('');
const [selectedLocation, setSelectedLocation] = useState<OptionType | null>(null); const [selectedLocation, setSelectedLocation] = useState<OptionType | null>(null);
@@ -223,10 +226,11 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
// Auto-calculate average weight when weight or qty changes (but not when editing average weight manually) // Auto-calculate average weight when weight or qty changes (but not when editing average weight manually)
useEffect(() => { useEffect(() => {
// Only run auto-calculation if no field is being edited
if (formik.values.body_weights && editingAverageIndex === null) { if (formik.values.body_weights && editingAverageIndex === null) {
const updatedBodyWeights = formik.values.body_weights.map((weight, idx) => { const updatedBodyWeights = formik.values.body_weights.map((weight, idx) => {
// Skip auto-calculation for the field being manually edited // Skip the field that's being edited or has been manually edited
if (idx === editingAverageIndex) { if (idx === editingAverageIndex || manuallyEditedRows.has(idx)) {
return weight; return weight;
} }
@@ -234,7 +238,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
...weight, ...weight,
average_weight: average_weight:
weight.qty > 0 && weight.weight > 0 weight.qty > 0 && weight.weight > 0
? Math.round(weight.weight / weight.qty) ? parseFloat((weight.weight / weight.qty).toFixed(2))
: 0, : 0,
}; };
}); });
@@ -243,11 +247,13 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
const hasChanges = updatedBodyWeights.some( const hasChanges = updatedBodyWeights.some(
(updated, idx) => (updated, idx) =>
idx !== editingAverageIndex && // Skip the field being edited idx !== editingAverageIndex && // Skip the field being edited
!manuallyEditedRows.has(idx) && // Skip manually edited rows
updated.average_weight !== updated.average_weight !==
(formik.values.body_weights[idx]?.average_weight || 0) (formik.values.body_weights[idx]?.average_weight || 0)
); );
if (hasChanges) { if (hasChanges) {
// Use false to prevent triggering validation and other side effects
formik.setFieldValue('body_weights', updatedBodyWeights, false); formik.setFieldValue('body_weights', updatedBodyWeights, false);
} }
} }
@@ -255,6 +261,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
formik.values.body_weights?.map((w) => w.weight), formik.values.body_weights?.map((w) => w.weight),
formik.values.body_weights?.map((w) => w.qty), formik.values.body_weights?.map((w) => w.qty),
editingAverageIndex, // Include editing index in dependencies editingAverageIndex, // Include editing index in dependencies
manuallyEditedRows, // Include manually edited rows in dependencies
]); ]);
// EVENT HANDLERS - Body Weights // EVENT HANDLERS - Body Weights
@@ -274,11 +281,18 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
const handleWeightChange = (idx: number, value: number) => { const handleWeightChange = (idx: number, value: number) => {
formik.setFieldValue(`body_weights.${idx}.weight`, value); formik.setFieldValue(`body_weights.${idx}.weight`, value);
// Reset manual edit flag when weight changes (user wants auto-calculation)
setManuallyEditedRows(prev => {
const newSet = new Set(prev);
newSet.delete(idx);
return newSet;
});
const currentWeight = formik.values.body_weights?.[idx]; const currentWeight = formik.values.body_weights?.[idx];
if (currentWeight) { if (currentWeight) {
const qty = currentWeight.qty; const qty = currentWeight.qty;
if (qty > 0 && value > 0) { if (qty > 0 && value > 0) {
const averageWeight = Math.round(value / qty); const averageWeight = parseFloat((value / qty).toFixed(2));
formik.setFieldValue(`body_weights.${idx}.average_weight`, averageWeight); formik.setFieldValue(`body_weights.${idx}.average_weight`, averageWeight);
} else { } else {
formik.setFieldValue(`body_weights.${idx}.average_weight`, 0); formik.setFieldValue(`body_weights.${idx}.average_weight`, 0);
@@ -290,11 +304,18 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
const handleQtyChange = (idx: number, value: number) => { const handleQtyChange = (idx: number, value: number) => {
formik.setFieldValue(`body_weights.${idx}.qty`, value); formik.setFieldValue(`body_weights.${idx}.qty`, value);
// Reset manual edit flag when qty changes (user wants auto-calculation)
setManuallyEditedRows(prev => {
const newSet = new Set(prev);
newSet.delete(idx);
return newSet;
});
const currentWeight = formik.values.body_weights?.[idx]; const currentWeight = formik.values.body_weights?.[idx];
if (currentWeight) { if (currentWeight) {
const weight = currentWeight.weight; const weight = currentWeight.weight;
if (value > 0 && weight > 0) { if (value > 0 && weight > 0) {
const averageWeight = Math.round(weight / value); const averageWeight = parseFloat((weight / value).toFixed(2));
formik.setFieldValue(`body_weights.${idx}.average_weight`, averageWeight); formik.setFieldValue(`body_weights.${idx}.average_weight`, averageWeight);
} else { } else {
formik.setFieldValue(`body_weights.${idx}.average_weight`, 0); formik.setFieldValue(`body_weights.${idx}.average_weight`, 0);
@@ -320,12 +341,20 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
// Create wrapper handlers that match NumberInput's onChange signature // Create wrapper handlers that match NumberInput's onChange signature
const handleWeightChangeWrapper = (idx: number) => (e: React.ChangeEvent<HTMLInputElement>) => { const handleWeightChangeWrapper = (idx: number) => (e: React.ChangeEvent<HTMLInputElement>) => {
const value = parseFloat(e.target.value.replace(/[^\d,.-]/g, '').replace(/,/g, '')) || 0; // Parse the value more carefully to handle decimal numbers properly
const rawValue = e.target.value.replace(/[^\d,.-]/g, '');
// Convert comma thousand separator to nothing, but keep decimal point
const normalizedValue = rawValue.replace(/,/g, '');
const value = parseFloat(normalizedValue) || 0;
handleWeightChange(idx, value); handleWeightChange(idx, value);
}; };
const handleQtyChangeWrapper = (idx: number) => (e: React.ChangeEvent<HTMLInputElement>) => { const handleQtyChangeWrapper = (idx: number) => (e: React.ChangeEvent<HTMLInputElement>) => {
const value = parseFloat(e.target.value.replace(/[^\d,.-]/g, '').replace(/,/g, '')) || 0; // Parse the value more carefully to handle decimal numbers properly
const rawValue = e.target.value.replace(/[^\d,.-]/g, '');
// Convert comma thousand separator to nothing, but keep decimal point
const normalizedValue = rawValue.replace(/,/g, '');
const value = parseFloat(normalizedValue) || 0;
handleQtyChange(idx, value); handleQtyChange(idx, value);
}; };
@@ -333,11 +362,18 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
// Set focus state to prevent auto-calculation override // Set focus state to prevent auto-calculation override
setEditingAverageIndex(idx); setEditingAverageIndex(idx);
const value = parseFloat(e.target.value.replace(/[^\d,.-]/g, '').replace(/,/g, '')) || 0; // Mark this row as manually edited
setManuallyEditedRows(prev => new Set(prev).add(idx));
// Parse the value more carefully to handle decimal numbers properly
const rawValue = e.target.value.replace(/[^\d,.-]/g, '');
// Convert comma thousand separator to nothing, but keep decimal point
const normalizedValue = rawValue.replace(/,/g, '');
const value = parseFloat(normalizedValue) || 0;
handleAverageWeightChange(idx, value); handleAverageWeightChange(idx, value);
}; };
const handleAverageWeightBlur = () => { const handleAverageWeightBlur = (idx: number) => {
// Clear focus state when user leaves the field to re-enable auto-calculation // Clear focus state when user leaves the field to re-enable auto-calculation
setEditingAverageIndex(null); setEditingAverageIndex(null);
}; };
@@ -700,7 +736,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
value={bw.average_weight || 0} value={bw.average_weight || 0}
onChange={handleAverageWeightChangeWrapper(idx)} onChange={handleAverageWeightChangeWrapper(idx)}
onBlur={(e) => { onBlur={(e) => {
handleAverageWeightBlur(); handleAverageWeightBlur(idx);
formik.handleBlur(e); formik.handleBlur(e);
}} }}
maskType='weight' maskType='weight'