mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
refactor(FE-208,212): update PurchaseOrderForm and PurchaseOrderStaffApprovalForm for improved validation and dynamic item handling
This commit is contained in:
@@ -90,15 +90,16 @@ const PurchaseStaffApprovalItemObjectSchema: Yup.ObjectSchema<PurchaseStaffAppro
|
||||
purchase_item: Yup.object({
|
||||
value: Yup.number().min(1).required(),
|
||||
label: Yup.string().required(),
|
||||
}).nullable(),
|
||||
})
|
||||
.nullable()
|
||||
.optional(),
|
||||
purchase_item_id: Yup.number()
|
||||
.min(1, 'Purchase item is required!')
|
||||
.required('Purchase item is required!')
|
||||
.test(
|
||||
'is-valid-purchase-item',
|
||||
'Purchase item must be selected!',
|
||||
'is-valid-purchase-item-id',
|
||||
'Purchase item ID must be valid!',
|
||||
function (value) {
|
||||
if (!this.parent.purchase_item) return true;
|
||||
return Boolean(value && value > 0);
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useFormik } from 'formik';
|
||||
import { Icon } from '@iconify/react';
|
||||
import { toast } from 'react-hot-toast';
|
||||
@@ -8,7 +8,7 @@ import { useSearchParams } from 'next/navigation';
|
||||
import Button from '@/components/Button';
|
||||
import TextInput from '@/components/input/TextInput';
|
||||
import NumberInput from '@/components/input/NumberInput';
|
||||
import SelectInput, { OptionType } from '@/components/input/SelectInput';
|
||||
import { OptionType } from '@/components/input/SelectInput';
|
||||
|
||||
import {
|
||||
PurchaseRequestStaffApprovalFormDefaultValues,
|
||||
@@ -56,7 +56,7 @@ const PurchaseOrderStaffApprovalForm = ({
|
||||
// ===== UTILITY FUNCTIONS =====
|
||||
const getPurchaseItemError = (
|
||||
idx: number,
|
||||
field: 'purchase_item_id' | 'price' | 'total_price'
|
||||
field: 'price' | 'total_price'
|
||||
): { isError: boolean; errorMessage: string } => {
|
||||
const touchedItem = formik.touched.items?.[idx];
|
||||
const errorItem = formik.errors.items?.[idx] as
|
||||
@@ -135,20 +135,20 @@ const PurchaseOrderStaffApprovalForm = ({
|
||||
onSubmit: async (values) => {
|
||||
const payload: CreateStaffApprovalRequestPayload = {
|
||||
notes: values.notes || '',
|
||||
items: (values.items || []).map((item) => ({
|
||||
purchase_item_id:
|
||||
typeof item.purchase_item_id === 'string'
|
||||
? parseInt(item.purchase_item_id) || 0
|
||||
: item.purchase_item_id || 0,
|
||||
price:
|
||||
typeof item.price === 'string'
|
||||
? parseFloat(item.price) || 0
|
||||
: item.price || 0,
|
||||
total_price:
|
||||
typeof item.total_price === 'string'
|
||||
? parseFloat(item.total_price) || 0
|
||||
: item.total_price || 0,
|
||||
})),
|
||||
items: purchaseItems.map((purchaseItem, idx) => {
|
||||
const formItem = values.items?.[idx];
|
||||
return {
|
||||
purchase_item_id: purchaseItem.value,
|
||||
price:
|
||||
typeof formItem?.price === 'string'
|
||||
? parseFloat(formItem.price) || 0
|
||||
: formItem?.price || 0,
|
||||
total_price:
|
||||
typeof formItem?.total_price === 'string'
|
||||
? parseFloat(formItem.total_price) || 0
|
||||
: formItem?.total_price || 0,
|
||||
};
|
||||
}),
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
@@ -170,9 +170,9 @@ const PurchaseOrderStaffApprovalForm = ({
|
||||
if (initialValues?.items) {
|
||||
return initialValues.items.map((item) => ({
|
||||
value: item.id,
|
||||
label: `${item.product.name} ${item.quantity}`,
|
||||
label: `${item.product.name} ${item.sub_qty}`,
|
||||
id: item.id,
|
||||
quantity: item.quantity,
|
||||
quantity: item.sub_qty,
|
||||
product: {
|
||||
name: item.product.name,
|
||||
product_category: item.product.product_category,
|
||||
@@ -187,25 +187,18 @@ const PurchaseOrderStaffApprovalForm = ({
|
||||
return [];
|
||||
}, [initialValues?.items]);
|
||||
|
||||
const getPurchaseItemOptions = useCallback(() => {
|
||||
return purchaseItems;
|
||||
// Ensure form values are properly set when purchaseItems change
|
||||
useEffect(() => {
|
||||
if (purchaseItems.length > 0) {
|
||||
const updatedItems = purchaseItems.map((purchaseItem) => ({
|
||||
purchase_item_id: purchaseItem.value,
|
||||
price: '',
|
||||
total_price: '',
|
||||
}));
|
||||
formik.setFieldValue('items', updatedItems);
|
||||
}
|
||||
}, [purchaseItems]);
|
||||
|
||||
// ===== FIELD CHANGE HANDLERS =====
|
||||
const purchaseItemChangeHandler = (
|
||||
idx: number,
|
||||
val: OptionType | OptionType[] | null
|
||||
) => {
|
||||
const purchaseItem = val as PurchaseItemOptionType | null;
|
||||
formik.setFieldTouched(`items.${idx}.purchase_item`, true);
|
||||
formik.setFieldValue(`items.${idx}.purchase_item`, purchaseItem);
|
||||
formik.setFieldTouched(`items.${idx}.purchase_item_id`, true);
|
||||
formik.setFieldValue(
|
||||
`items.${idx}.purchase_item_id`,
|
||||
(purchaseItem as OptionType)?.value || 0
|
||||
);
|
||||
};
|
||||
|
||||
// ===== PURCHASE ITEM OPERATIONS =====
|
||||
const handlePurchaseItemChange = (
|
||||
idx: number,
|
||||
@@ -217,29 +210,26 @@ const PurchaseOrderStaffApprovalForm = ({
|
||||
typeof value === 'string' ? parseFloat(value) || 0 : value;
|
||||
formik.setFieldValue(`items.${idx}.${field}`, numValue);
|
||||
|
||||
if (field === 'price') {
|
||||
const selectedItem = purchaseItems.find(
|
||||
(p) => p.value === formik.values.items?.[idx]?.purchase_item_id
|
||||
);
|
||||
if (selectedItem && selectedItem.quantity && numValue > 0) {
|
||||
const calculatedTotal = numValue * selectedItem.quantity;
|
||||
formik.setFieldValue(`items.${idx}.total_price`, calculatedTotal);
|
||||
}
|
||||
const selectedItem = purchaseItems[idx];
|
||||
|
||||
if (
|
||||
field === 'price' &&
|
||||
selectedItem &&
|
||||
selectedItem.quantity > 0 &&
|
||||
numValue >= 0
|
||||
) {
|
||||
const calculatedTotal = numValue * selectedItem.quantity;
|
||||
formik.setFieldValue(`items.${idx}.total_price`, calculatedTotal);
|
||||
}
|
||||
|
||||
if (field === 'total_price') {
|
||||
const selectedItem = purchaseItems.find(
|
||||
(p) => p.value === formik.values.items?.[idx]?.purchase_item_id
|
||||
);
|
||||
if (
|
||||
selectedItem &&
|
||||
selectedItem.quantity &&
|
||||
selectedItem.quantity > 0 &&
|
||||
numValue > 0
|
||||
) {
|
||||
const calculatedPrice = numValue / selectedItem.quantity;
|
||||
formik.setFieldValue(`items.${idx}.price`, calculatedPrice);
|
||||
}
|
||||
if (
|
||||
field === 'total_price' &&
|
||||
selectedItem &&
|
||||
selectedItem.quantity > 0 &&
|
||||
numValue >= 0
|
||||
) {
|
||||
const calculatedPrice = numValue / selectedItem.quantity;
|
||||
formik.setFieldValue(`items.${idx}.price`, calculatedPrice);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -260,10 +250,6 @@ const PurchaseOrderStaffApprovalForm = ({
|
||||
<table className='table'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Item
|
||||
<span className='text-error'>*</span>
|
||||
</th>
|
||||
<th>Gudang</th>
|
||||
<th>Produk</th>
|
||||
<th>Jenis Produk</th>
|
||||
@@ -280,43 +266,16 @@ const PurchaseOrderStaffApprovalForm = ({
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{formik.values.items?.map((item, idx) => {
|
||||
const selectedPurchaseItem = purchaseItems.find(
|
||||
(p) => p.value === item.purchase_item_id
|
||||
);
|
||||
{purchaseItems?.map((purchaseItem, idx) => {
|
||||
const formItem = formik.values.items?.[idx];
|
||||
return (
|
||||
<tr key={`purchase-item-${idx}`}>
|
||||
<td>
|
||||
<SelectInput
|
||||
required
|
||||
isClearable={true}
|
||||
value={item.purchase_item}
|
||||
key={`purchase-item-${idx}`}
|
||||
onChange={(val) =>
|
||||
purchaseItemChangeHandler(idx, val)
|
||||
}
|
||||
options={getPurchaseItemOptions()}
|
||||
isError={
|
||||
getPurchaseItemError(idx, 'purchase_item_id')
|
||||
.isError
|
||||
}
|
||||
errorMessage={
|
||||
getPurchaseItemError(idx, 'purchase_item_id')
|
||||
.errorMessage
|
||||
}
|
||||
placeholder='Pilih Item...'
|
||||
className={{
|
||||
wrapper: 'min-w-52 md:min-w-72 lg:min-w-80',
|
||||
}}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<TextInput
|
||||
name={`items.${idx}.warehouse`}
|
||||
type='text'
|
||||
value={selectedPurchaseItem?.warehouse?.name || ''}
|
||||
value={purchaseItem?.warehouse?.name || ''}
|
||||
readOnly={true}
|
||||
placeholder='Pilih item terlebih dahulu'
|
||||
className={{
|
||||
wrapper: 'min-w-40 md:min-w-52 lg:min-w-64',
|
||||
}}
|
||||
@@ -327,9 +286,8 @@ const PurchaseOrderStaffApprovalForm = ({
|
||||
<TextInput
|
||||
name={`items.${idx}.product_name`}
|
||||
type='text'
|
||||
value={selectedPurchaseItem?.product?.name || ''}
|
||||
value={purchaseItem?.product?.name || ''}
|
||||
readOnly={true}
|
||||
placeholder='Pilih item terlebih dahulu'
|
||||
className={{
|
||||
wrapper: 'min-w-52 md:min-w-72 lg:min-w-80',
|
||||
}}
|
||||
@@ -341,14 +299,13 @@ const PurchaseOrderStaffApprovalForm = ({
|
||||
name={`items.${idx}.product_category`}
|
||||
type='text'
|
||||
value={
|
||||
typeof selectedPurchaseItem?.product
|
||||
?.product_category === 'string'
|
||||
? selectedPurchaseItem.product.product_category
|
||||
: selectedPurchaseItem?.product?.product_category
|
||||
?.name || ''
|
||||
typeof purchaseItem?.product?.product_category ===
|
||||
'string'
|
||||
? purchaseItem.product.product_category
|
||||
: purchaseItem?.product?.product_category?.name ||
|
||||
''
|
||||
}
|
||||
readOnly={true}
|
||||
placeholder='Pilih item terlebih dahulu'
|
||||
className={{
|
||||
wrapper: 'min-w-40 md:min-w-52 lg:min-w-64',
|
||||
}}
|
||||
@@ -360,14 +317,11 @@ const PurchaseOrderStaffApprovalForm = ({
|
||||
name={`items.${idx}.quantity`}
|
||||
type='text'
|
||||
value={
|
||||
selectedPurchaseItem?.quantity
|
||||
? selectedPurchaseItem.quantity.toLocaleString(
|
||||
'id-ID'
|
||||
)
|
||||
purchaseItem?.quantity
|
||||
? purchaseItem.quantity.toLocaleString('id-ID')
|
||||
: ''
|
||||
}
|
||||
readOnly={true}
|
||||
placeholder='Pilih item terlebih dahulu'
|
||||
className={{
|
||||
wrapper: 'min-w-24',
|
||||
}}
|
||||
@@ -378,9 +332,8 @@ const PurchaseOrderStaffApprovalForm = ({
|
||||
<TextInput
|
||||
name={`items.${idx}.uom`}
|
||||
type='text'
|
||||
value={selectedPurchaseItem?.product?.uom?.name || ''}
|
||||
value={purchaseItem?.product?.uom?.name || ''}
|
||||
readOnly={true}
|
||||
placeholder='Pilih item terlebih dahulu'
|
||||
className={{
|
||||
wrapper: 'min-w-24',
|
||||
}}
|
||||
@@ -391,7 +344,7 @@ const PurchaseOrderStaffApprovalForm = ({
|
||||
<NumberInput
|
||||
required
|
||||
name={`items.${idx}.price`}
|
||||
value={item.price || ''}
|
||||
value={formItem?.price || ''}
|
||||
onChange={(e) =>
|
||||
handlePurchaseItemChange(
|
||||
idx,
|
||||
@@ -419,7 +372,7 @@ const PurchaseOrderStaffApprovalForm = ({
|
||||
<NumberInput
|
||||
required
|
||||
name={`items.${idx}.total_price`}
|
||||
value={item.total_price || ''}
|
||||
value={formItem?.total_price || ''}
|
||||
onChange={(e) =>
|
||||
handlePurchaseItemChange(
|
||||
idx,
|
||||
|
||||
Reference in New Issue
Block a user