feat(FE): adding edit and delete per product row sales order

This commit is contained in:
randy-ar
2026-01-20 10:43:13 +07:00
parent 5e7f55000a
commit bbb9c5f190
4 changed files with 71 additions and 22 deletions
@@ -361,6 +361,8 @@ const MarketingForm = ({
},
});
const memoSalesOrder = formik.values.sales_order;
// ================== FORM REPEATER HANDLER ==================
const createMarketingHandler = async (values: CreateSalesOrderPayload) => {
setIsLoading(true);
@@ -471,13 +473,25 @@ const MarketingForm = ({
}, [deleteModal]);
// ================== SALES ORDER HANDLER ==================
const handleDeleteSO = useCallback((id: number) => {
const currentProducts = formik.values.sales_order;
formik.setFieldValue(
'sales_order',
currentProducts.filter((p) => p.id != id)
);
}, []);
const handleDeleteSO = useCallback(
(id: number) => {
const currentProducts = formik.values.sales_order;
formik.setFieldValue(
'sales_order',
currentProducts.filter((p) => p.id != id)
);
},
[memoSalesOrder]
);
const handleEditSO = useCallback(
(id: number) => {
const currentProducts = formik.values.sales_order;
const selectedProduct = currentProducts.find((p) => p.id == id);
setSelectedMarketingProduct(selectedProduct ?? null);
addSOModal.openModal();
},
[memoSalesOrder]
);
const handleBulkDeleteSO = useCallback(() => {
const currentProducts = formik.values.sales_order;
formik.setFieldValue(
@@ -487,7 +501,7 @@ const MarketingForm = ({
)
);
setRowSOSelection({});
}, [selectedRowSOIds]);
}, [selectedRowSOIds, memoSalesOrder]);
const handleAddSOClick = useCallback(() => {
setSelectedMarketingProduct(null);
addSOModal.openModal();
@@ -523,7 +537,7 @@ const MarketingForm = ({
addSOModal.closeModal();
},
[addSOModal]
[addSOModal, memoSalesOrder]
);
// ================== DELIVERY ORDER HANDLER ==================
@@ -569,8 +583,6 @@ const MarketingForm = ({
[addDOModal]
);
const memoSalesOrder = formik.values.sales_order;
useEffect(() => {
formik.setFieldValue('delivery_order', deliveryOrderValues);
}, [deliveryOrderValues, initialValues]);
@@ -652,6 +664,7 @@ const MarketingForm = ({
setRowSelection={setRowSOSelection}
selectedRowIds={selectedRowSOIds}
onDelete={handleDeleteSO}
onEdit={handleEditSO}
onBulkDelete={handleBulkDeleteSO}
onAddProductClick={handleAddSOClick}
/>
@@ -18,6 +18,7 @@ type SalesOrderProductSchemaType = {
avg_weight: string | number | undefined;
total_price: string | number | undefined;
vehicle_number?: string | undefined;
uom?: string | null | undefined;
};
export const SalesOrderProductSchema: Yup.ObjectSchema<SalesOrderProductSchemaType> =
@@ -57,6 +58,7 @@ export const SalesOrderProductSchema: Yup.ObjectSchema<SalesOrderProductSchemaTy
total_price: Yup.number()
.min(1, 'Total Penjualan wajib diisi!')
.required('Total Penjualan wajib diisi!'),
uom: Yup.string().nullable().optional().notRequired(),
});
export type SalesOrderProductFormValues = Yup.InferType<
@@ -61,16 +61,17 @@ const SalesOrderProductForm = ({
const formik = useFormik<SalesOrderProductFormValues>({
enableReinitialize: true,
initialValues: {
vehicle_number: initialValues?.vehicle_number || undefined,
vehicle_number: initialValues?.vehicle_number || '',
kandang_id: initialValues?.kandang_id || undefined,
kandang: initialValues?.kandang || undefined,
product_warehouse: initialValues?.product_warehouse || undefined,
kandang: initialValues?.kandang || null,
product_warehouse: initialValues?.product_warehouse || null,
product_warehouse_id: initialValues?.product_warehouse_id || undefined,
unit_price: initialValues?.unit_price || undefined,
total_weight: initialValues?.total_weight || undefined,
qty: initialValues?.qty || undefined,
avg_weight: initialValues?.avg_weight || undefined,
total_price: initialValues?.total_price || undefined,
unit_price: initialValues?.unit_price || '',
total_weight: initialValues?.total_weight || '',
qty: initialValues?.qty || '',
avg_weight: initialValues?.avg_weight || '',
total_price: initialValues?.total_price || '',
uom: initialValues?.uom || '',
},
validationSchema: SalesOrderProductSchema,
onSubmit: async (values) => {
@@ -220,7 +221,19 @@ const SalesOrderProductForm = ({
};
// ===== Formik Error List =====
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(formik);
const { formErrorList, close, handleFormSubmit } = useFormikErrorList(
formik,
{
onBeforeSubmit(e) {
e.preventDefault();
handleBlurField(currentInput);
formik.setFieldValue(
'uom',
isResponseSuccess(productData) ? productData?.data?.uom.name : ''
);
},
}
);
return (
<>
@@ -23,6 +23,7 @@ type SalesOrderProductTableProps = {
>;
selectedRowIds: number[];
onDelete: (id: number) => void;
onEdit: (id: number) => void;
onBulkDelete: () => void;
onAddProductClick: () => void;
};
@@ -34,11 +35,14 @@ const SalesOrderProductTable = ({
setRowSelection,
selectedRowIds,
onDelete,
onEdit,
onBulkDelete,
onAddProductClick,
}: SalesOrderProductTableProps) => {
const onDeleteRef = useRef(onDelete);
onDeleteRef.current = onDelete;
const onEditRef = useRef(onEdit);
onEditRef.current = onEdit;
const columns = useMemo(
() => [
@@ -92,17 +96,26 @@ const SalesOrderProductTable = ({
},
{
accessorFn: (row: SalesOrderProductFormValues) =>
formatNumber(parseFloat(row.total_weight as string)),
formatNumber(parseFloat(row.total_weight as string), undefined, 0, 5),
header: 'Total Bobot (Kg)',
},
{
accessorFn: (row: SalesOrderProductFormValues) =>
formatNumber(parseFloat(row.qty as string)),
header: 'Kuantitas',
cell: ({ row }: { row: TanStack.Row<SalesOrderProductFormValues> }) =>
formatNumber(
parseFloat(row.original.qty as string),
undefined,
0,
5
) +
' ' +
row.original.uom,
},
{
accessorFn: (row: SalesOrderProductFormValues) =>
formatNumber(parseFloat(row.avg_weight as string)),
formatNumber(parseFloat(row.avg_weight as string), undefined, 0, 5),
header: 'Avg. Bobot (Kg)',
},
{
@@ -116,6 +129,14 @@ const SalesOrderProductTable = ({
props: TanStack.CellContext<SalesOrderProductFormValues, unknown>
) => (
<div className='flex flex-row gap-1 items-center justify-end h-full mt-2'>
<Button
color='warning'
className='p-1'
onClick={() => onEditRef.current(props.row.original.id as number)}
type='button'
>
<Icon icon='mdi:pencil' width={16} height={16} />
</Button>
<Button
color='error'
className='p-1'