refactor(FE-212): update PurchaseRequestForm schema and validation, streamline warehouse handling and add sub quantity field

This commit is contained in:
rstubryan
2025-11-03 11:04:07 +07:00
parent 1d79e8de1d
commit b4a9c86c2a
4 changed files with 111 additions and 118 deletions
@@ -1,12 +1,15 @@
'use client';
import { useMemo, useState } from 'react';
import { useCallback, useMemo, useState } from 'react';
import { useFormik } from 'formik';
import useSWR from 'swr';
import { useRouter } from 'next/navigation';
import { Icon } from '@iconify/react';
import { toast } from 'react-hot-toast';
import Button from '@/components/Button';
import TextInput from '@/components/input/TextInput';
import ConfirmationModal from '@/components/modal/ConfirmationModal';
import { useModal } from '@/components/Modal';
import { FormHeader } from '@/components/helper/form/FormHeader';
import { FormActions } from '@/components/helper/form/FormActions';
@@ -18,8 +21,8 @@ import {
} from './PurchaseRequestForm.schema';
import { SupplierApi } from '@/services/api/master-data';
import { WarehouseApi } from '@/services/api/master-data';
import { isResponseSuccess } from '@/lib/api-helper';
import { usePurchaseRequestFormHandlers } from './usePurchaseRequestFormHandlers';
import { isResponseSuccess, isResponseError } from '@/lib/api-helper';
import { PurchaseApi } from '@/services/api/purchasing';
import Card from '@/components/Card';
import {
@@ -36,19 +39,61 @@ const PurchaseRequestForm = ({
type = 'add',
initialValues,
}: PurchaseRequestFormProps) => {
const router = useRouter();
const deleteModal = useModal();
const [selectedPurchaseItems, setSelectedPurchaseItems] = useState<number[]>(
[]
);
const [purchaseRequestFormErrorMessage, setPurchaseRequestFormErrorMessage] =
useState('');
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const {
deleteModal,
purchaseRequestFormErrorMessage,
isDeleteLoading,
createPurchaseRequestHandler,
updatePurchaseRequestHandler,
deletePurchaseRequestClickHandler,
confirmationModalDeleteClickHandler,
} = usePurchaseRequestFormHandlers(initialValues?.id);
// ===== FORM HANDLERS =====
const createPurchaseRequestHandler = useCallback(
async (payload: CreatePurchaseRequestPayload) => {
const res = await PurchaseApi.create(payload);
if (isResponseError(res)) {
setPurchaseRequestFormErrorMessage(res.message);
return;
}
toast.success(res?.message as string);
router.push('/purchase');
},
[router]
);
const updatePurchaseRequestHandler = useCallback(
async (
purchaseRequestId: number,
payload: CreatePurchaseRequestPayload
) => {
const res = await PurchaseApi.update(purchaseRequestId, payload);
if (isResponseError(res)) {
setPurchaseRequestFormErrorMessage(res.message);
return;
}
toast.success(res?.message as string);
router.refresh();
router.push('/purchase');
},
[router]
);
const deletePurchaseRequestClickHandler = useCallback(() => {
deleteModal.openModal();
}, [deleteModal]);
const confirmationModalDeleteClickHandler = useCallback(async () => {
if (!initialValues?.id) return;
setIsDeleteLoading(true);
await PurchaseApi.delete(initialValues.id);
deleteModal.closeModal();
toast.success('Successfully delete Purchase Request!');
setIsDeleteLoading(false);
router.push('/purchase');
}, [deleteModal, initialValues?.id, router]);
// ===== API DATA FETCHING =====
const { data: suppliers, isLoading: isLoadingSuppliers } = useSWR(
@@ -101,9 +146,10 @@ const PurchaseRequestForm = ({
credit_term: values.credit_term || 0,
notes: values.notes || '',
purchase_items: (values.purchase_items || []).map((item) => ({
warehouse_ids: item.warehouse_ids || 0,
warehouse_id: item.warehouse_id || 0,
product_id: item.product_id || 0,
product_warehouse_id: item.product_warehouse_id || 0,
product_warehouse_id: item.product_warehouse_id || undefined,
sub_qty: item.sub_qty || 0,
total_qty: item.total_qty || 0,
price:
typeof item.price === 'number'
@@ -147,11 +193,12 @@ const PurchaseRequestForm = ({
...(formik.values.purchase_items || []),
{
warehouse: null,
warehouse_ids: 0,
warehouse_id: 0,
product: null,
product_id: 0,
product_warehouse: null,
product_warehouse_id: 0,
product_warehouse_id: null,
sub_qty: 0,
total_qty: 0,
price: 0,
},
@@ -180,12 +227,12 @@ const PurchaseRequestForm = ({
value: string | number
) => {
const integerFields = [
'warehouse_ids',
'warehouse_id',
'product_id',
'product_warehouse_id',
'total_qty',
];
const floatFields = ['price'];
const floatFields = ['price', 'sub_qty'];
if (integerFields.includes(field)) {
const numValue = typeof value === 'string' ? parseInt(value) || 0 : value;
@@ -320,6 +367,10 @@ const PurchaseRequestForm = ({
Product Warehouse ID
<span className='text-error'>*</span>
</th>
<th>
Sub Qty
<span className='text-error'>*</span>
</th>
<th>
Total Qty
<span className='text-error'>*</span>
@@ -358,18 +409,18 @@ const PurchaseRequestForm = ({
<td>
<TextInput
required
name={`purchase_items.${idx}.warehouse_ids`}
value={item.warehouse_ids}
name={`purchase_items.${idx}.warehouse_id`}
value={item.warehouse_id}
onChange={(e) =>
handlePurchaseItemChange(
idx,
'warehouse_ids',
'warehouse_id',
e.target.value
)
}
onBlur={formik.handleBlur}
type='number'
placeholder='Warehouse IDs'
placeholder='Warehouse ID'
readOnly={type === 'detail'}
className={{
wrapper: 'min-w-24',
@@ -399,7 +450,6 @@ const PurchaseRequestForm = ({
</td>
<td>
<TextInput
required
name={`purchase_items.${idx}.product_warehouse_id`}
value={item.product_warehouse_id || ''}
onChange={(e) =>
@@ -418,6 +468,27 @@ const PurchaseRequestForm = ({
}}
/>
</td>
<td>
<TextInput
required
name={`purchase_items.${idx}.sub_qty`}
value={item.sub_qty}
onChange={(e) =>
handlePurchaseItemChange(
idx,
'sub_qty',
e.target.value
)
}
onBlur={formik.handleBlur}
type='number'
placeholder='Sub Qty'
readOnly={type === 'detail'}
className={{
wrapper: 'min-w-24',
}}
/>
</td>
<td>
<TextInput
required