mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 21:41:57 +00:00
refactor(FE-64): integrate product and supplier selection with API data fetching in MovementForm
This commit is contained in:
@@ -37,24 +37,102 @@ interface MovementFormProps {
|
||||
}
|
||||
|
||||
const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
||||
// ===== STATE MANAGEMENT =====
|
||||
const [, setMovementFormErrorMessage] = useState('');
|
||||
const [
|
||||
productWarehouseSelectInputValue,
|
||||
setProductWarehouseSelectInputValue,
|
||||
] = useState('');
|
||||
const [productWarehouseSelectInputValue, setProductWarehouseSelectInputValue] = useState('');
|
||||
const [selectedProducts, setSelectedProducts] = useState<number[]>([]);
|
||||
const [selectedDeliveries, setSelectedDeliveries] = useState<number[]>([]);
|
||||
const [warehouseSelectInputValue, setWarehouseSelectInputValue] = useState('');
|
||||
const [supplierSelectInputValue, setSupplierSelectInputValue] = useState('');
|
||||
|
||||
// ===== FORM HANDLERS =====
|
||||
const {
|
||||
deleteModal,
|
||||
movementFormErrorMessage,
|
||||
isDeleteLoading,
|
||||
createMovementHandler,
|
||||
updateMovementHandler,
|
||||
deleteMovementClickHandler,
|
||||
confirmationModalDeleteClickHandler,
|
||||
} = useMovementFormHandlers(initialValues?.id);
|
||||
|
||||
// ===== INTERFACES =====
|
||||
interface WarehouseOptionType extends OptionType {
|
||||
area?: string;
|
||||
location?: string;
|
||||
}
|
||||
|
||||
interface ProductWarehouseOptionType extends OptionType {
|
||||
product_id: number;
|
||||
warehouse_id: number;
|
||||
warehouse_name: string;
|
||||
quantity: number;
|
||||
}
|
||||
|
||||
// ===== API DATA FETCHING =====
|
||||
const allProductWarehousesUrl = `${ProductWarehouseApi.basePath}`;
|
||||
const { data: allProductWarehouses } = useSWR(
|
||||
allProductWarehousesUrl,
|
||||
ProductWarehouseApi.getAllFetcher
|
||||
);
|
||||
|
||||
const warehousesUrl = `${WarehouseApi.basePath}?${new URLSearchParams({ search: warehouseSelectInputValue }).toString()}`;
|
||||
const { data: warehouses, isLoading: isLoadingWarehouses } = useSWR(
|
||||
warehousesUrl,
|
||||
WarehouseApi.getAllFetcher
|
||||
);
|
||||
|
||||
const suppliersUrl = `${SupplierApi.basePath}?${new URLSearchParams({ search: supplierSelectInputValue }).toString()}`;
|
||||
const { data: suppliers, isLoading: isLoadingSuppliers } = useSWR(
|
||||
suppliersUrl,
|
||||
SupplierApi.getAllFetcher
|
||||
);
|
||||
|
||||
// ===== DATA PROCESSING =====
|
||||
const warehouseStockMap = useMemo(() => {
|
||||
if (!isResponseSuccess(allProductWarehouses)) return new Map();
|
||||
|
||||
const stockMap = new Map<
|
||||
number,
|
||||
{ totalQty: number; productCount: number }
|
||||
>();
|
||||
|
||||
allProductWarehouses.data.forEach((pw) => {
|
||||
const warehouseId = pw.warehouse.id;
|
||||
const existing = stockMap.get(warehouseId) || {
|
||||
totalQty: 0,
|
||||
productCount: 0,
|
||||
};
|
||||
|
||||
stockMap.set(warehouseId, {
|
||||
totalQty: existing.totalQty + pw.quantity,
|
||||
productCount: existing.productCount + 1,
|
||||
});
|
||||
});
|
||||
|
||||
return stockMap;
|
||||
}, [allProductWarehouses]);
|
||||
|
||||
const warehouseOptions = isResponseSuccess(warehouses)
|
||||
? warehouses?.data.map((w) => {
|
||||
const stockInfo = warehouseStockMap.get(w.id);
|
||||
const stockLabel = stockInfo
|
||||
? ` (Stock: ${stockInfo.totalQty.toLocaleString('id-ID')} items, ${stockInfo.productCount} produk)`
|
||||
: ' (Kosong)';
|
||||
|
||||
return {
|
||||
value: w.id,
|
||||
label: `${w.name}${stockLabel}`,
|
||||
area: w.area?.name,
|
||||
location:
|
||||
'type' in w && (w.type === 'LOKASI' || w.type === 'KANDANG')
|
||||
? w.location?.name
|
||||
: undefined,
|
||||
};
|
||||
})
|
||||
: [];
|
||||
|
||||
const supplierOptions = isResponseSuccess(suppliers)
|
||||
? suppliers?.data.map((s) => ({ value: s.id, label: s.name }))
|
||||
: [];
|
||||
|
||||
// ===== FORM INITIALIZATION =====
|
||||
const formikInitialValues = useMemo<MovementFormValues>(
|
||||
() => getMovementFormInitialValues(initialValues),
|
||||
[initialValues]
|
||||
@@ -77,7 +155,6 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
||||
if (d.document && d.document instanceof File) {
|
||||
documents.push(d.document);
|
||||
documentIndex = documents.length - 1;
|
||||
} else {
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -122,91 +199,39 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
||||
},
|
||||
});
|
||||
|
||||
const addProduct = () => {
|
||||
const newProducts = [
|
||||
...(formik.values.products || []),
|
||||
{
|
||||
product: null,
|
||||
product_id: 0,
|
||||
product_qty: 0,
|
||||
},
|
||||
];
|
||||
formik.setFieldValue('products', newProducts);
|
||||
};
|
||||
// ===== PRODUCT WAREHOUSE FETCHING (after form initialization) =====
|
||||
const getProductWarehousesUrl = useCallback(() => {
|
||||
const productWarehouseParams = new URLSearchParams({
|
||||
search: productWarehouseSelectInputValue,
|
||||
});
|
||||
if (formik.values.source_warehouse_id) {
|
||||
productWarehouseParams.append(
|
||||
'warehouse_id',
|
||||
formik.values.source_warehouse_id.toString()
|
||||
);
|
||||
}
|
||||
return `${ProductWarehouseApi.basePath}?${productWarehouseParams.toString()}`;
|
||||
}, [formik.values.source_warehouse_id, productWarehouseSelectInputValue]);
|
||||
|
||||
const removeProduct = useCallback(
|
||||
(i: number) => {
|
||||
const updatedProducts =
|
||||
formik.values.products?.reduce((acc: ProductSchema[], item, index) => {
|
||||
if (index !== i) {
|
||||
acc.push(item);
|
||||
}
|
||||
return acc;
|
||||
}, []) ?? [];
|
||||
const productWarehousesUrl = getProductWarehousesUrl();
|
||||
const { data: productWarehouses, isLoading: isLoadingProductWarehouses } =
|
||||
useSWR(
|
||||
formik.values.source_warehouse_id ? productWarehousesUrl : null,
|
||||
ProductWarehouseApi.getAllFetcher
|
||||
);
|
||||
|
||||
formik.setFieldValue('products', updatedProducts);
|
||||
},
|
||||
[formik]
|
||||
);
|
||||
|
||||
const bulkRemoveProduct = useCallback(() => {
|
||||
const updatedProducts =
|
||||
formik.values.products?.filter(
|
||||
(_, idx) => !selectedProducts.includes(idx)
|
||||
) ?? [];
|
||||
formik.setFieldValue('products', updatedProducts);
|
||||
setSelectedProducts([]);
|
||||
}, [formik, selectedProducts]);
|
||||
|
||||
const addDelivery = () => {
|
||||
formik.setFieldValue('deliveries', [
|
||||
...(formik.values.deliveries || []),
|
||||
{
|
||||
delivery_cost: undefined,
|
||||
delivery_cost_per_item: undefined,
|
||||
document: null,
|
||||
driver_name: '',
|
||||
vehicle_plate: '',
|
||||
supplier: null,
|
||||
supplier_id: 0,
|
||||
products: [
|
||||
{
|
||||
product: null,
|
||||
product_id: 0,
|
||||
product_qty: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
const removeDelivery = useCallback(
|
||||
(i: number) => {
|
||||
const updatedDeliveries =
|
||||
formik.values.deliveries?.reduce(
|
||||
(acc: DeliverySchema[], item, index) => {
|
||||
if (index !== i) {
|
||||
acc.push(item);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
[]
|
||||
) ?? [];
|
||||
|
||||
formik.setFieldValue('deliveries', updatedDeliveries);
|
||||
},
|
||||
[formik]
|
||||
);
|
||||
|
||||
const bulkRemoveDelivery = useCallback(() => {
|
||||
const updatedDeliveries =
|
||||
formik.values.deliveries?.filter(
|
||||
(_, idx) => !selectedDeliveries.includes(idx)
|
||||
) ?? [];
|
||||
formik.setFieldValue('deliveries', updatedDeliveries);
|
||||
setSelectedDeliveries([]);
|
||||
}, [formik, selectedDeliveries]);
|
||||
const productWarehouseOptions = isResponseSuccess(productWarehouses)
|
||||
? productWarehouses?.data.map((pw) => ({
|
||||
value: pw.product.id,
|
||||
label: `${pw.product.name} - ${pw.warehouse.name} (Stock: ${pw.quantity.toLocaleString('id-ID')})`,
|
||||
product_id: pw.product.id,
|
||||
warehouse_id: pw.warehouse.id,
|
||||
warehouse_name: pw.warehouse.name,
|
||||
quantity: pw.quantity,
|
||||
}))
|
||||
: [];
|
||||
|
||||
// ===== HELPER FUNCTIONS =====
|
||||
const isRepeaterInputError = <T extends 'products' | 'deliveries'>(
|
||||
arrayName: T,
|
||||
column: T extends 'products' ? keyof ProductSchema : keyof DeliverySchema,
|
||||
@@ -263,118 +288,98 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
||||
};
|
||||
};
|
||||
|
||||
interface WarehouseOptionType extends OptionType {
|
||||
area?: string;
|
||||
location?: string;
|
||||
}
|
||||
// ===== EVENT HANDLERS =====
|
||||
// Product Handlers
|
||||
const addProduct = () => {
|
||||
const newProducts = [
|
||||
...(formik.values.products || []),
|
||||
{
|
||||
product: null,
|
||||
product_id: 0,
|
||||
product_qty: 0,
|
||||
},
|
||||
];
|
||||
formik.setFieldValue('products', newProducts);
|
||||
};
|
||||
|
||||
interface ProductWarehouseOptionType extends OptionType {
|
||||
product_id: number;
|
||||
warehouse_id: number;
|
||||
warehouse_name: string;
|
||||
quantity: number;
|
||||
}
|
||||
const removeProduct = useCallback(
|
||||
(i: number) => {
|
||||
const updatedProducts =
|
||||
formik.values.products?.reduce((acc: ProductSchema[], item, index) => {
|
||||
if (index !== i) {
|
||||
acc.push(item);
|
||||
}
|
||||
return acc;
|
||||
}, []) ?? [];
|
||||
|
||||
const allProductWarehousesUrl = `${ProductWarehouseApi.basePath}`;
|
||||
const { data: allProductWarehouses } = useSWR(
|
||||
allProductWarehousesUrl,
|
||||
ProductWarehouseApi.getAllFetcher
|
||||
formik.setFieldValue('products', updatedProducts);
|
||||
},
|
||||
[formik]
|
||||
);
|
||||
|
||||
const warehouseStockMap = useMemo(() => {
|
||||
if (!isResponseSuccess(allProductWarehouses)) return new Map();
|
||||
const bulkRemoveProduct = useCallback(() => {
|
||||
const updatedProducts =
|
||||
formik.values.products?.filter(
|
||||
(_, idx) => !selectedProducts.includes(idx)
|
||||
) ?? [];
|
||||
formik.setFieldValue('products', updatedProducts);
|
||||
setSelectedProducts([]);
|
||||
}, [formik, selectedProducts]);
|
||||
|
||||
const stockMap = new Map<
|
||||
number,
|
||||
{ totalQty: number; productCount: number }
|
||||
>();
|
||||
// Delivery Handlers
|
||||
const addDelivery = () => {
|
||||
formik.setFieldValue('deliveries', [
|
||||
...(formik.values.deliveries || []),
|
||||
{
|
||||
delivery_cost: undefined,
|
||||
delivery_cost_per_item: undefined,
|
||||
document: null,
|
||||
driver_name: '',
|
||||
vehicle_plate: '',
|
||||
supplier: null,
|
||||
supplier_id: 0,
|
||||
products: [
|
||||
{
|
||||
product: null,
|
||||
product_id: 0,
|
||||
product_qty: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
allProductWarehouses.data.forEach((pw) => {
|
||||
const warehouseId = pw.warehouse.id;
|
||||
const existing = stockMap.get(warehouseId) || {
|
||||
totalQty: 0,
|
||||
productCount: 0,
|
||||
};
|
||||
const removeDelivery = useCallback(
|
||||
(i: number) => {
|
||||
const updatedDeliveries =
|
||||
formik.values.deliveries?.reduce(
|
||||
(acc: DeliverySchema[], item, index) => {
|
||||
if (index !== i) {
|
||||
acc.push(item);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
[]
|
||||
) ?? [];
|
||||
|
||||
stockMap.set(warehouseId, {
|
||||
totalQty: existing.totalQty + pw.quantity,
|
||||
productCount: existing.productCount + 1,
|
||||
});
|
||||
});
|
||||
|
||||
return stockMap;
|
||||
}, [allProductWarehouses]);
|
||||
|
||||
// Warehouse selection
|
||||
const [warehouseSelectInputValue, setWarehouseSelectInputValue] =
|
||||
useState('');
|
||||
const warehousesUrl = `${WarehouseApi.basePath}?${new URLSearchParams({ search: warehouseSelectInputValue }).toString()}`;
|
||||
const { data: warehouses, isLoading: isLoadingWarehouses } = useSWR(
|
||||
warehousesUrl,
|
||||
WarehouseApi.getAllFetcher
|
||||
formik.setFieldValue('deliveries', updatedDeliveries);
|
||||
},
|
||||
[formik]
|
||||
);
|
||||
const warehouseOptions = isResponseSuccess(warehouses)
|
||||
? warehouses?.data.map((w) => {
|
||||
const stockInfo = warehouseStockMap.get(w.id);
|
||||
const stockLabel = stockInfo
|
||||
? ` (Stock: ${stockInfo.totalQty.toLocaleString('id-ID')} items, ${stockInfo.productCount} produk)`
|
||||
: ' (Kosong)';
|
||||
|
||||
return {
|
||||
value: w.id,
|
||||
label: `${w.name}${stockLabel}`,
|
||||
area: w.area?.name,
|
||||
location:
|
||||
'type' in w && (w.type === 'LOKASI' || w.type === 'KANDANG')
|
||||
? w.location?.name
|
||||
: undefined,
|
||||
};
|
||||
})
|
||||
: [];
|
||||
const bulkRemoveDelivery = useCallback(() => {
|
||||
const updatedDeliveries =
|
||||
formik.values.deliveries?.filter(
|
||||
(_, idx) => !selectedDeliveries.includes(idx)
|
||||
) ?? [];
|
||||
formik.setFieldValue('deliveries', updatedDeliveries);
|
||||
setSelectedDeliveries([]);
|
||||
}, [formik, selectedDeliveries]);
|
||||
|
||||
// Product Warehouse selection - Filter by source warehouse
|
||||
const productWarehouseParams = new URLSearchParams({
|
||||
search: productWarehouseSelectInputValue,
|
||||
});
|
||||
if (formik.values.source_warehouse_id) {
|
||||
productWarehouseParams.append(
|
||||
'warehouse_id',
|
||||
formik.values.source_warehouse_id.toString()
|
||||
);
|
||||
}
|
||||
const productWarehousesUrl = `${ProductWarehouseApi.basePath}?${productWarehouseParams.toString()}`;
|
||||
const { data: productWarehouses, isLoading: isLoadingProductWarehouses } =
|
||||
useSWR(
|
||||
formik.values.source_warehouse_id ? productWarehousesUrl : null,
|
||||
ProductWarehouseApi.getAllFetcher
|
||||
);
|
||||
const productWarehouseOptions = isResponseSuccess(productWarehouses)
|
||||
? productWarehouses?.data.map((pw) => ({
|
||||
value: pw.product.id,
|
||||
label: `${pw.product.name} - ${pw.warehouse.name} (Stock: ${pw.quantity.toLocaleString('id-ID')})`,
|
||||
product_id: pw.product.id,
|
||||
warehouse_id: pw.warehouse.id,
|
||||
warehouse_name: pw.warehouse.name,
|
||||
quantity: pw.quantity,
|
||||
}))
|
||||
: [];
|
||||
|
||||
// Supplier selection
|
||||
const [supplierSelectInputValue, setSupplierSelectInputValue] = useState('');
|
||||
const suppliersUrl = `${SupplierApi.basePath}?${new URLSearchParams({ search: supplierSelectInputValue }).toString()}`;
|
||||
const { data: suppliers, isLoading: isLoadingSuppliers } = useSWR(
|
||||
suppliersUrl,
|
||||
SupplierApi.getAllFetcher
|
||||
);
|
||||
const supplierOptions = isResponseSuccess(suppliers)
|
||||
? suppliers?.data.map((s) => ({ value: s.id, label: s.name }))
|
||||
: [];
|
||||
|
||||
// Handle cost calculation when delivery_cost changes
|
||||
// Cost Calculation Handlers
|
||||
const handleDeliveryCostChange = useCallback(
|
||||
(idx: number, value: string) => {
|
||||
const numValue = parseFloat(value) || 0;
|
||||
formik.setFieldValue(`deliveries.${idx}.delivery_cost`, numValue);
|
||||
(idx: number, value: number) => {
|
||||
formik.setFieldValue(`deliveries.${idx}.delivery_cost`, value);
|
||||
|
||||
const delivery = formik.values.deliveries?.[idx];
|
||||
if (delivery) {
|
||||
@@ -382,13 +387,13 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
||||
(sum, p) => sum + p.product_qty,
|
||||
0
|
||||
);
|
||||
if (productQty > 0 && numValue > 0) {
|
||||
const perItem = numValue / productQty;
|
||||
if (productQty > 0 && value > 0) {
|
||||
const perItem = value / productQty;
|
||||
formik.setFieldValue(
|
||||
`deliveries.${idx}.delivery_cost_per_item`,
|
||||
perItem
|
||||
);
|
||||
} else if (numValue === 0) {
|
||||
} else if (value === 0) {
|
||||
formik.setFieldValue(`deliveries.${idx}.delivery_cost_per_item`, 0);
|
||||
}
|
||||
}
|
||||
@@ -396,13 +401,11 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
||||
[formik]
|
||||
);
|
||||
|
||||
// Handle cost calculation when delivery_cost_per_item changes
|
||||
const handleDeliveryCostPerItemChange = useCallback(
|
||||
(idx: number, value: string) => {
|
||||
const numValue = parseFloat(value) || 0;
|
||||
(idx: number, value: number) => {
|
||||
formik.setFieldValue(
|
||||
`deliveries.${idx}.delivery_cost_per_item`,
|
||||
numValue
|
||||
value
|
||||
);
|
||||
|
||||
const delivery = formik.values.deliveries?.[idx];
|
||||
@@ -411,10 +414,10 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
||||
(sum, p) => sum + p.product_qty,
|
||||
0
|
||||
);
|
||||
if (productQty > 0 && numValue > 0) {
|
||||
const totalCost = numValue * productQty;
|
||||
if (productQty > 0 && value > 0) {
|
||||
const totalCost = value * productQty;
|
||||
formik.setFieldValue(`deliveries.${idx}.delivery_cost`, totalCost);
|
||||
} else if (numValue === 0) {
|
||||
} else if (value === 0) {
|
||||
formik.setFieldValue(`deliveries.${idx}.delivery_cost`, 0);
|
||||
}
|
||||
}
|
||||
@@ -422,57 +425,27 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
||||
[formik]
|
||||
);
|
||||
|
||||
// Auto-recalculate when product quantity changes
|
||||
useEffect(() => {
|
||||
formik.values.deliveries?.forEach((delivery, idx) => {
|
||||
const productQty = delivery.products.reduce(
|
||||
(sum, p) => sum + p.product_qty,
|
||||
0
|
||||
);
|
||||
const handleDeliveryCostChangeWrapper = useCallback(
|
||||
(idx: number) => (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const rawValue = e.target.value.replace(/[^\d,.-]/g, '');
|
||||
const normalizedValue = rawValue.replace(/,/g, '');
|
||||
const value = parseFloat(normalizedValue) || 0;
|
||||
handleDeliveryCostChange(idx, value);
|
||||
},
|
||||
[handleDeliveryCostChange]
|
||||
);
|
||||
|
||||
// If delivery_cost is set, recalculate delivery_cost_per_item
|
||||
if (
|
||||
delivery.delivery_cost &&
|
||||
delivery.delivery_cost > 0 &&
|
||||
productQty > 0
|
||||
) {
|
||||
const perItem = delivery.delivery_cost / productQty;
|
||||
if (Math.abs((delivery.delivery_cost_per_item || 0) - perItem) > 0.01) {
|
||||
formik.setFieldValue(
|
||||
`deliveries.${idx}.delivery_cost_per_item`,
|
||||
perItem
|
||||
);
|
||||
}
|
||||
}
|
||||
// If delivery_cost_per_item is set, recalculate delivery_cost
|
||||
else if (
|
||||
delivery.delivery_cost_per_item &&
|
||||
delivery.delivery_cost_per_item > 0 &&
|
||||
productQty > 0
|
||||
) {
|
||||
const totalCost = delivery.delivery_cost_per_item * productQty;
|
||||
if (Math.abs((delivery.delivery_cost || 0) - totalCost) > 0.01) {
|
||||
formik.setFieldValue(`deliveries.${idx}.delivery_cost`, totalCost);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [
|
||||
formik.values.deliveries
|
||||
?.map((d) => d.products.reduce((sum, p) => sum + p.product_qty, 0))
|
||||
.join(','),
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
formik.values.source_warehouse_id &&
|
||||
type !== 'edit' &&
|
||||
type !== 'detail'
|
||||
) {
|
||||
formik.setFieldValue('products', []);
|
||||
formik.setFieldValue('deliveries', []);
|
||||
}
|
||||
}, [formik.values.source_warehouse_id]);
|
||||
const handleDeliveryCostPerItemChangeWrapper = useCallback(
|
||||
(idx: number) => (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const rawValue = e.target.value.replace(/[^\d,.-]/g, '');
|
||||
const normalizedValue = rawValue.replace(/,/g, '');
|
||||
const value = parseFloat(normalizedValue) || 0;
|
||||
handleDeliveryCostPerItemChange(idx, value);
|
||||
},
|
||||
[handleDeliveryCostPerItemChange]
|
||||
);
|
||||
|
||||
// UTILITY FUNCTIONS
|
||||
const getFilteredProductWarehouseOptions = useCallback(() => {
|
||||
return (
|
||||
formik.values.products
|
||||
@@ -618,6 +591,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
||||
[formik.values.deliveries, formik.values.products, type]
|
||||
);
|
||||
|
||||
// ===== COMPUTED VALUES =====
|
||||
const invalidQtyRows = useMemo(
|
||||
() =>
|
||||
type === 'detail'
|
||||
@@ -650,6 +624,54 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
||||
);
|
||||
}, [formik.values.products, getProductQtyError, type]);
|
||||
|
||||
// ===== EFFECTS =====
|
||||
useEffect(() => {
|
||||
formik.values.deliveries?.forEach((delivery, idx) => {
|
||||
const productQty = delivery.products.reduce(
|
||||
(sum, p) => sum + p.product_qty,
|
||||
0
|
||||
);
|
||||
|
||||
if (
|
||||
delivery.delivery_cost &&
|
||||
delivery.delivery_cost > 0 &&
|
||||
productQty > 0
|
||||
) {
|
||||
const perItem = delivery.delivery_cost / productQty;
|
||||
if (Math.abs((delivery.delivery_cost_per_item || 0) - perItem) > 0.01) {
|
||||
formik.setFieldValue(
|
||||
`deliveries.${idx}.delivery_cost_per_item`,
|
||||
perItem
|
||||
);
|
||||
}
|
||||
} else if (
|
||||
delivery.delivery_cost_per_item &&
|
||||
delivery.delivery_cost_per_item > 0 &&
|
||||
productQty > 0
|
||||
) {
|
||||
const totalCost = delivery.delivery_cost_per_item * productQty;
|
||||
if (Math.abs((delivery.delivery_cost || 0) - totalCost) > 0.01) {
|
||||
formik.setFieldValue(`deliveries.${idx}.delivery_cost`, totalCost);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [
|
||||
formik.values.deliveries
|
||||
?.map((d) => d.products.reduce((sum, p) => sum + p.product_qty, 0))
|
||||
.join(','),
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
formik.values.source_warehouse_id &&
|
||||
type !== 'edit' &&
|
||||
type !== 'detail'
|
||||
) {
|
||||
formik.setFieldValue('products', []);
|
||||
formik.setFieldValue('deliveries', []);
|
||||
}
|
||||
}, [formik.values.source_warehouse_id]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className='w-full'>
|
||||
@@ -839,7 +861,6 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
||||
<tr>
|
||||
{type !== 'detail' && (
|
||||
<th>
|
||||
<div className='flex justify-center'>
|
||||
<CheckboxInput
|
||||
name='select-all-products'
|
||||
checked={
|
||||
@@ -865,7 +886,6 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
||||
checkbox: 'checkbox checkbox-sm',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</th>
|
||||
)}
|
||||
<th>
|
||||
@@ -893,31 +913,29 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
||||
{formik.values.products?.map((product, idx) => (
|
||||
<tr key={`product-row-${idx}-${product.product_id}`}>
|
||||
{type !== 'detail' && (
|
||||
<td>
|
||||
<div className='flex justify-center'>
|
||||
<CheckboxInput
|
||||
name={`product-${idx}`}
|
||||
checked={selectedProducts.includes(idx)}
|
||||
onChange={(
|
||||
e: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
if (e.target.checked) {
|
||||
setSelectedProducts([
|
||||
...selectedProducts,
|
||||
idx,
|
||||
]);
|
||||
} else {
|
||||
setSelectedProducts(
|
||||
selectedProducts.filter((i) => i !== idx)
|
||||
);
|
||||
}
|
||||
}}
|
||||
classNames={{
|
||||
wrapper: 'flex justify-center',
|
||||
checkbox: 'checkbox checkbox-sm',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<td className="!align-middle">
|
||||
<CheckboxInput
|
||||
name={`product-${idx}`}
|
||||
checked={selectedProducts.includes(idx)}
|
||||
onChange={(
|
||||
e: React.ChangeEvent<HTMLInputElement>,
|
||||
) => {
|
||||
if (e.target.checked) {
|
||||
setSelectedProducts([
|
||||
...selectedProducts,
|
||||
idx,
|
||||
]);
|
||||
} else {
|
||||
setSelectedProducts(
|
||||
selectedProducts.filter((i) => i !== idx),
|
||||
);
|
||||
}
|
||||
}}
|
||||
classNames={{
|
||||
wrapper: 'flex justify-center',
|
||||
checkbox: 'checkbox checkbox-sm',
|
||||
}}
|
||||
/>
|
||||
</td>
|
||||
)}
|
||||
<td>
|
||||
@@ -1061,7 +1079,6 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
||||
<tr>
|
||||
{type !== 'detail' && (
|
||||
<th>
|
||||
<div className='flex justify-center'>
|
||||
<CheckboxInput
|
||||
name='select-all-deliveries'
|
||||
checked={
|
||||
@@ -1087,7 +1104,6 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
||||
checkbox: 'checkbox checkbox-sm',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</th>
|
||||
)}
|
||||
<th>
|
||||
@@ -1161,33 +1177,31 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
||||
{formik.values.deliveries?.map((delivery, idx) => (
|
||||
<tr key={`delivery-row-${idx}`}>
|
||||
{type !== 'detail' && (
|
||||
<td>
|
||||
<div className='flex justify-center'>
|
||||
<CheckboxInput
|
||||
name={`delivery-${idx}`}
|
||||
checked={selectedDeliveries.includes(idx)}
|
||||
onChange={(
|
||||
e: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
if (e.target.checked) {
|
||||
setSelectedDeliveries([
|
||||
...selectedDeliveries,
|
||||
idx,
|
||||
]);
|
||||
} else {
|
||||
setSelectedDeliveries(
|
||||
selectedDeliveries.filter(
|
||||
(i) => i !== idx
|
||||
)
|
||||
);
|
||||
}
|
||||
}}
|
||||
classNames={{
|
||||
wrapper: 'flex justify-center',
|
||||
checkbox: 'checkbox checkbox-sm',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<td className="!align-middle">
|
||||
<CheckboxInput
|
||||
name={`delivery-${idx}`}
|
||||
checked={selectedDeliveries.includes(idx)}
|
||||
onChange={(
|
||||
e: React.ChangeEvent<HTMLInputElement>,
|
||||
) => {
|
||||
if (e.target.checked) {
|
||||
setSelectedDeliveries([
|
||||
...selectedDeliveries,
|
||||
idx,
|
||||
]);
|
||||
} else {
|
||||
setSelectedDeliveries(
|
||||
selectedDeliveries.filter(
|
||||
(i) => i !== idx,
|
||||
),
|
||||
);
|
||||
}
|
||||
}}
|
||||
classNames={{
|
||||
wrapper: 'flex justify-center',
|
||||
checkbox: 'checkbox checkbox-sm',
|
||||
}}
|
||||
/>
|
||||
</td>
|
||||
)}
|
||||
<td>
|
||||
@@ -1373,9 +1387,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
||||
required
|
||||
name={`deliveries.${idx}.delivery_cost`}
|
||||
value={delivery.delivery_cost || ''}
|
||||
onChange={(e) =>
|
||||
handleDeliveryCostChange(idx, e.target.value)
|
||||
}
|
||||
onChange={handleDeliveryCostChangeWrapper(idx)}
|
||||
onBlur={formik.handleBlur}
|
||||
maskType='currency'
|
||||
decimals={0}
|
||||
@@ -1397,12 +1409,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
||||
required
|
||||
name={`deliveries.${idx}.delivery_cost_per_item`}
|
||||
value={delivery.delivery_cost_per_item || ''}
|
||||
onChange={(e) =>
|
||||
handleDeliveryCostPerItemChange(
|
||||
idx,
|
||||
e.target.value
|
||||
)
|
||||
}
|
||||
onChange={handleDeliveryCostPerItemChangeWrapper(idx)}
|
||||
onBlur={formik.handleBlur}
|
||||
maskType='currency'
|
||||
decimals={0}
|
||||
|
||||
Reference in New Issue
Block a user