feat(FE-62): enhance MovementForm with delivery product input error handling and validation

This commit is contained in:
rstubryan
2025-10-15 12:30:59 +07:00
parent 06dc869b84
commit aa21088e99
@@ -214,6 +214,32 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
}; };
}; };
const isDeliveryProductInputError = (
deliveryIdx: number,
productIdx: number,
column: keyof DeliverySchema['products'][number]
) => {
const touchedDelivery = formik.touched.deliveries?.[deliveryIdx];
const errorDelivery = formik.errors.deliveries?.[deliveryIdx] as
| { products: Array<Record<string, string>> }
| undefined;
if (!touchedDelivery?.products || !errorDelivery?.products) {
return {
isError: false,
errorMessage: undefined,
};
}
const touchedField = touchedDelivery.products[productIdx]?.[column];
const errorField = errorDelivery.products[productIdx]?.[column];
return {
isError: Boolean(touchedField && errorField),
errorMessage: touchedField ? errorField : undefined,
};
};
interface WarehouseOptionType extends OptionType { interface WarehouseOptionType extends OptionType {
area?: string; area?: string;
location?: string; location?: string;
@@ -305,11 +331,11 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
if (!relatedProduct) return true; if (!relatedProduct) return true;
const totalQtyUsed = const totalQtyUsed =
formik.values.deliveries?.reduce((total, d) => { formik.values.deliveries?.reduce((total, d, dIdx) => {
const productQty = d.products.reduce((sum, p, pIdx) => { const productQty = d.products.reduce((sum, p, pIdx) => {
if ( if (
p.product_id === productId && p.product_id === productId &&
!(d === delivery && pIdx === deliveryProductIdx) !(dIdx === deliveryIdx && pIdx === deliveryProductIdx)
) { ) {
return sum + (Number(p.product_qty) || 0); return sum + (Number(p.product_qty) || 0);
} }
@@ -323,6 +349,47 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
[formik.values.deliveries, formik.values.products] [formik.values.deliveries, formik.values.products]
); );
const getDeliveryQtyError = useCallback(
(deliveryIdx: number, deliveryProductIdx: number) => {
const delivery = formik.values.deliveries?.[deliveryIdx];
if (!delivery) return null;
const deliveryProduct = delivery.products[deliveryProductIdx];
if (!deliveryProduct || !deliveryProduct.product_id) return null;
const qty = Number(deliveryProduct.product_qty) || 0;
const productId = deliveryProduct.product_id;
const relatedProduct = formik.values.products?.find(
(p) => p.product_id === productId
);
if (!relatedProduct) return null;
const totalQtyUsed =
formik.values.deliveries?.reduce((total, d, dIdx) => {
const productQty = d.products.reduce((sum, p, pIdx) => {
if (
p.product_id === productId &&
!(dIdx === deliveryIdx && pIdx === deliveryProductIdx)
) {
return sum + (Number(p.product_qty) || 0);
}
return sum;
}, 0);
return total + productQty;
}, 0) || 0;
const availableQty = Number(relatedProduct.product_qty) - totalQtyUsed;
if (totalQtyUsed + qty > Number(relatedProduct.product_qty)) {
return `Qty melebihi stok produk! Tersedia: ${availableQty}, Total digunakan: ${totalQtyUsed + qty}`;
}
return null;
},
[formik.values.deliveries, formik.values.products]
);
const invalidQtyRows = useMemo( const invalidQtyRows = useMemo(
() => () =>
formik.values.deliveries?.flatMap((delivery, deliveryIdx) => formik.values.deliveries?.flatMap((delivery, deliveryIdx) =>
@@ -334,7 +401,10 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
[formik.values.deliveries, formik.values.products, validateDeliveryQty] [formik.values.deliveries, formik.values.products, validateDeliveryQty]
); );
const hasInvalidQty = invalidQtyRows.some(Boolean); const hasInvalidQty = useMemo(
() => invalidQtyRows.some(Boolean),
[invalidQtyRows]
);
return ( return (
<> <>
@@ -762,6 +832,16 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
value={delivery.products[0]?.product_qty ?? ''} value={delivery.products[0]?.product_qty ?? ''}
onChange={formik.handleChange} onChange={formik.handleChange}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
isError={
isDeliveryProductInputError(idx, 0, 'product_qty')
.isError || Boolean(getDeliveryQtyError(idx, 0))
}
errorMessage={
isDeliveryProductInputError(idx, 0, 'product_qty')
.errorMessage ||
getDeliveryQtyError(idx, 0) ||
undefined
}
readOnly={type === 'detail'} readOnly={type === 'detail'}
className={{ className={{
wrapper: 'w-full min-w-24', wrapper: 'w-full min-w-24',