feat(FE-208): implement conditional item deletion in PurchaseOrderDetail and update form handling in PurchaseOrderStaffApprovalForm

This commit is contained in:
rstubryan
2025-11-21 13:26:34 +07:00
parent f6fe2d4eb1
commit c74733430b
2 changed files with 59 additions and 61 deletions
@@ -252,7 +252,7 @@ const PurchaseOrderStaffApprovalForm = ({
(item) => item.id === purchaseItem.id (item) => item.id === purchaseItem.id
); );
return { return {
purchase_item_id: type === 'edit' ? purchaseItem.value : undefined, purchase_item_id: purchaseItem.id,
product_id: purchaseItem.product_id || 0, product_id: purchaseItem.product_id || 0,
warehouse_id: purchaseItem.warehouse_id || 0, warehouse_id: purchaseItem.warehouse_id || 0,
qty: purchaseItem.quantity || 0, qty: purchaseItem.quantity || 0,
@@ -266,36 +266,27 @@ const PurchaseOrderStaffApprovalForm = ({
}, [purchaseItems, type, initialValues]); }, [purchaseItems, type, initialValues]);
// ===== PURCHASE ITEM OPERATIONS ===== // ===== PURCHASE ITEM OPERATIONS =====
const findItemIndex = (purchaseItemId: number) => {
return purchaseItems.findIndex((item) => item.id === purchaseItemId);
};
const handlePurchaseItemChange = ( const handlePurchaseItemChange = (
purchaseItemId: number, purchaseItemId: number,
field: 'price' | 'total_price', field: 'price' | 'total_price',
value: string | number value: string | number
) => { ) => {
const itemIndex = findItemIndex(purchaseItemId);
const formItemIndex = formik.values.items?.findIndex( const formItemIndex = formik.values.items?.findIndex(
(item) => item.purchase_item_id === purchaseItemId (item) => item.purchase_item_id === purchaseItemId
); );
const purchaseItem = purchaseItems.find(
(item) => item.id === purchaseItemId
);
if (itemIndex === -1 || formItemIndex === -1) return; if (formItemIndex === -1 || !purchaseItem) return;
if (field === 'price' || field === 'total_price') { if (field === 'price' || field === 'total_price') {
const numValue = const numValue =
typeof value === 'string' ? parseFloat(value) || 0 : value; typeof value === 'string' ? parseFloat(value) || 0 : value;
formik.setFieldValue(`items.${formItemIndex}.${field}`, numValue); formik.setFieldValue(`items.${formItemIndex}.${field}`, numValue);
const selectedItem = purchaseItems[itemIndex]; if (field === 'price' && purchaseItem.quantity > 0 && numValue >= 0) {
const calculatedTotal = numValue * purchaseItem.quantity;
if (
field === 'price' &&
selectedItem &&
selectedItem.quantity > 0 &&
numValue >= 0
) {
const calculatedTotal = numValue * selectedItem.quantity;
formik.setFieldValue( formik.setFieldValue(
`items.${formItemIndex}.total_price`, `items.${formItemIndex}.total_price`,
calculatedTotal calculatedTotal
@@ -304,11 +295,10 @@ const PurchaseOrderStaffApprovalForm = ({
if ( if (
field === 'total_price' && field === 'total_price' &&
selectedItem && purchaseItem.quantity > 0 &&
selectedItem.quantity > 0 &&
numValue >= 0 numValue >= 0
) { ) {
const calculatedPrice = numValue / selectedItem.quantity; const calculatedPrice = numValue / purchaseItem.quantity;
formik.setFieldValue(`items.${formItemIndex}.price`, calculatedPrice); formik.setFieldValue(`items.${formItemIndex}.price`, calculatedPrice);
} }
} }
@@ -362,6 +352,11 @@ const PurchaseOrderStaffApprovalForm = ({
(item) => (item) =>
item.purchase_item_id === purchaseItem.id item.purchase_item_id === purchaseItem.id
); );
const formItemIndex =
formik.values.items?.findIndex(
(item) =>
item.purchase_item_id === purchaseItem.id
);
return ( return (
<tr key={`purchase-item-${purchaseItem.id}`}> <tr key={`purchase-item-${purchaseItem.id}`}>
<td> <td>
@@ -383,7 +378,7 @@ const PurchaseOrderStaffApprovalForm = ({
</td> </td>
<td> <td>
<TextInput <TextInput
name={`items.${purchaseItem.id}.product_category`} name={`items.${formItemIndex}.product_category`}
type='text' type='text'
value={ value={
typeof purchaseItem?.product typeof purchaseItem?.product
@@ -403,7 +398,7 @@ const PurchaseOrderStaffApprovalForm = ({
</td> </td>
<td> <td>
<TextInput <TextInput
name={`items.${purchaseItem.id}.quantity`} name={`items.${formItemIndex}.quantity`}
type='text' type='text'
value={ value={
purchaseItem?.quantity purchaseItem?.quantity
@@ -421,7 +416,7 @@ const PurchaseOrderStaffApprovalForm = ({
</td> </td>
<td> <td>
<TextInput <TextInput
name={`items.${purchaseItem.id}.uom`} name={`items.${formItemIndex}.uom`}
type='text' type='text'
value={ value={
purchaseItem?.product?.uom?.name || '' purchaseItem?.product?.uom?.name || ''
@@ -436,7 +431,7 @@ const PurchaseOrderStaffApprovalForm = ({
<td> <td>
<NumberInput <NumberInput
required required
name={`items.${purchaseItem.id}.price`} name={`items.${formItemIndex}.price`}
value={formItem?.price || ''} value={formItem?.price || ''}
onChange={(e) => onChange={(e) =>
handlePurchaseItemChange( handlePurchaseItemChange(
@@ -473,7 +468,7 @@ const PurchaseOrderStaffApprovalForm = ({
<td> <td>
<NumberInput <NumberInput
required required
name={`items.${purchaseItem.id}.total_price`} name={`items.${formItemIndex}.total_price`}
value={formItem?.total_price || ''} value={formItem?.total_price || ''}
onChange={(e) => onChange={(e) =>
handlePurchaseItemChange( handlePurchaseItemChange(
@@ -1,7 +1,7 @@
'use client'; 'use client';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { ColumnDef, SortingState } from '@tanstack/react-table'; import { ColumnDef, SortingState, Row, Table as TableType } from '@tanstack/react-table';
import ApprovalSteps, { import ApprovalSteps, {
useApprovalSteps, useApprovalSteps,
@@ -156,6 +156,11 @@ const PurchaseOrderDetail = ({
const showApprovalButton = const showApprovalButton =
approvalStep !== null && approvalStep >= 1 && approvalStep <= 3; approvalStep !== null && approvalStep >= 1 && approvalStep <= 3;
const canDeleteItems = useMemo(() => {
if (!initialValues?.approval) return false;
return initialValues.approval.step_number >= 3;
}, [initialValues?.approval]);
const handleApprovalClick = () => { const handleApprovalClick = () => {
if (!approvalStep) return; if (!approvalStep) return;
@@ -186,12 +191,6 @@ const PurchaseOrderDetail = ({
} }
}; };
useMemo(() => {
if (!initialValues?.approval) return false;
const currentStep = initialValues.approval.step_number;
return currentStep >= 4;
}, [initialValues?.approval]);
const canShowPurchaseOrderInvoice = useMemo(() => { const canShowPurchaseOrderInvoice = useMemo(() => {
if (!initialValues?.approval) return false; if (!initialValues?.approval) return false;
@@ -345,32 +344,36 @@ const PurchaseOrderDetail = ({
const purchaseData = initialValues; const purchaseData = initialValues;
const purchaseOrderColumns: ColumnDef<PurchaseItem>[] = [ const purchaseOrderColumns: ColumnDef<PurchaseItem>[] = [
{ ...(canDeleteItems
id: 'select', ? [
header: ({ table }) => ( {
<div className='w-full flex flex-row justify-center'> id: 'select',
<CheckboxInput header: ({ table }: { table: TableType<PurchaseItem> }) => (
name='allRow' <div className='w-full flex flex-row justify-center'>
checked={table.getIsAllRowsSelected()} <CheckboxInput
indeterminate={table.getIsSomeRowsSelected()} name='allRow'
onChange={table.getToggleAllRowsSelectedHandler()} checked={table.getIsAllRowsSelected()}
/> indeterminate={table.getIsSomeRowsSelected()}
</div> onChange={table.getToggleAllRowsSelectedHandler()}
), />
cell: ({ row }) => { </div>
return ( ),
<div> cell: ({ row }: { row: Row<PurchaseItem> }) => {
<CheckboxInput return (
name='row' <div>
checked={row.getIsSelected()} <CheckboxInput
disabled={!row.getCanSelect()} name='row'
indeterminate={row.getIsSomeSelected()} checked={row.getIsSelected()}
onChange={row.getToggleSelectedHandler()} disabled={!row.getCanSelect()}
/> indeterminate={row.getIsSomeSelected()}
</div> onChange={row.getToggleSelectedHandler()}
); />
}, </div>
}, );
},
},
]
: []),
{ {
header: 'No', header: 'No',
cell: (props) => props.row.index + 1, cell: (props) => props.row.index + 1,
@@ -426,7 +429,7 @@ const PurchaseOrderDetail = ({
deleteModal.openModal(); deleteModal.openModal();
}; };
return ( return canDeleteItems ? (
<Button <Button
type='button' type='button'
color='error' color='error'
@@ -435,7 +438,7 @@ const PurchaseOrderDetail = ({
> >
<Icon icon='mdi:trash-can' width={16} height={16} /> <Icon icon='mdi:trash-can' width={16} height={16} />
</Button> </Button>
); ) : null;
}, },
}, },
]; ];
@@ -738,7 +741,7 @@ const PurchaseOrderDetail = ({
setSorting={setSorting} setSorting={setSorting}
rowSelection={rowSelection} rowSelection={rowSelection}
setRowSelection={setRowSelection} setRowSelection={setRowSelection}
enableRowSelection={() => true} enableRowSelection={() => canDeleteItems}
className={{ className={{
containerClassName: 'm-0', containerClassName: 'm-0',
tableWrapperClassName: 'overflow-x-auto', tableWrapperClassName: 'overflow-x-auto',
@@ -755,7 +758,7 @@ const PurchaseOrderDetail = ({
</div> </div>
{/* Bulk Action Buttons */} {/* Bulk Action Buttons */}
{selectedRowIds.length > 0 && ( {selectedRowIds.length > 0 && canDeleteItems && (
<div className='flex justify-center items-center mt-4 gap-4 px-6 py-4 bg-gray-50 border-t border-gray-200'> <div className='flex justify-center items-center mt-4 gap-4 px-6 py-4 bg-gray-50 border-t border-gray-200'>
<Button <Button
type='button' type='button'