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
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
const [locationSearchValue, setLocationSearchValue] = useState('');
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)
useEffect(() => {
// Only run auto-calculation if no field is being edited
if (formik.values.body_weights && editingAverageIndex === null) {
const updatedBodyWeights = formik.values.body_weights.map((weight, idx) => {
// Skip auto-calculation for the field being manually edited
if (idx === editingAverageIndex) {
// Skip the field that's being edited or has been manually edited
if (idx === editingAverageIndex || manuallyEditedRows.has(idx)) {
return weight;
}
@@ -234,7 +238,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
...weight,
average_weight:
weight.qty > 0 && weight.weight > 0
? Math.round(weight.weight / weight.qty)
? parseFloat((weight.weight / weight.qty).toFixed(2))
: 0,
};
});
@@ -243,11 +247,13 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
const hasChanges = updatedBodyWeights.some(
(updated, idx) =>
idx !== editingAverageIndex && // Skip the field being edited
!manuallyEditedRows.has(idx) && // Skip manually edited rows
updated.average_weight !==
(formik.values.body_weights[idx]?.average_weight || 0)
);
if (hasChanges) {
// Use false to prevent triggering validation and other side effects
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.qty),
editingAverageIndex, // Include editing index in dependencies
manuallyEditedRows, // Include manually edited rows in dependencies
]);
// EVENT HANDLERS - Body Weights
@@ -274,11 +281,18 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
const handleWeightChange = (idx: number, value: number) => {
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];
if (currentWeight) {
const qty = currentWeight.qty;
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);
} else {
formik.setFieldValue(`body_weights.${idx}.average_weight`, 0);
@@ -290,11 +304,18 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
const handleQtyChange = (idx: number, value: number) => {
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];
if (currentWeight) {
const weight = currentWeight.weight;
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);
} else {
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
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);
};
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);
};
@@ -333,11 +362,18 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
// Set focus state to prevent auto-calculation override
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);
};
const handleAverageWeightBlur = () => {
const handleAverageWeightBlur = (idx: number) => {
// Clear focus state when user leaves the field to re-enable auto-calculation
setEditingAverageIndex(null);
};
@@ -700,7 +736,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
value={bw.average_weight || 0}
onChange={handleAverageWeightChangeWrapper(idx)}
onBlur={(e) => {
handleAverageWeightBlur();
handleAverageWeightBlur(idx);
formik.handleBlur(e);
}}
maskType='weight'