refactor(FE-114): streamline cost field validation messages and enhance layout with FieldMessage component

This commit is contained in:
rstubryan
2025-10-20 18:54:31 +07:00
parent 1bcfd9bbb4
commit e0ce571000
2 changed files with 85 additions and 79 deletions
@@ -61,32 +61,24 @@ const DeliveryObjectSchema: Yup.ObjectSchema<DeliverySchema> = Yup.object({
.transform((value) => (isNaN(value) || value === 0 ? undefined : value)) .transform((value) => (isNaN(value) || value === 0 ? undefined : value))
.min(1, 'Biaya minimal 1!') .min(1, 'Biaya minimal 1!')
.typeError('Biaya harus berupa angka!') .typeError('Biaya harus berupa angka!')
.test( .test('one-of-cost-fields', 'Wajib diisi salah satu!', function (value) {
'one-of-cost-fields', const { delivery_cost_per_item } = this.parent;
'Biaya pengiriman atau biaya per item wajib diisi!', return (
function (value) { (value !== undefined && value > 0) ||
const { delivery_cost_per_item } = this.parent; (delivery_cost_per_item !== undefined && delivery_cost_per_item > 0)
return ( );
(value !== undefined && value > 0) || }),
(delivery_cost_per_item !== undefined && delivery_cost_per_item > 0)
);
}
),
delivery_cost_per_item: Yup.number() delivery_cost_per_item: Yup.number()
.transform((value) => (isNaN(value) || value === 0 ? undefined : value)) .transform((value) => (isNaN(value) || value === 0 ? undefined : value))
.min(1, 'Biaya per item minimal 1!') .min(1, 'Biaya per item minimal 1!')
.typeError('Biaya per item harus berupa angka!') .typeError('Biaya per item harus berupa angka!')
.test( .test('one-of-cost-fields', 'Wajib diisi salah satu!', function (value) {
'one-of-cost-fields', const { delivery_cost } = this.parent;
'Biaya pengiriman atau biaya per item wajib diisi!', return (
function (value) { (value !== undefined && value > 0) ||
const { delivery_cost } = this.parent; (delivery_cost !== undefined && delivery_cost > 0)
return ( );
(value !== undefined && value > 0) || }),
(delivery_cost !== undefined && delivery_cost > 0)
);
}
),
document_path: Yup.string().optional(), document_path: Yup.string().optional(),
document_index: Yup.number().optional(), document_index: Yup.number().optional(),
document: Yup.mixed<File | string>() document: Yup.mixed<File | string>()
@@ -8,7 +8,6 @@ import { Icon } from '@iconify/react';
import Button from '@/components/Button'; import Button from '@/components/Button';
import TextInput from '@/components/input/TextInput'; import TextInput from '@/components/input/TextInput';
import SelectInput, { OptionType } from '@/components/input/SelectInput'; import SelectInput, { OptionType } from '@/components/input/SelectInput';
import ConfirmationModal from '@/components/modal/ConfirmationModal';
import { FormHeader } from '@/components/helper/form/FormHeader'; import { FormHeader } from '@/components/helper/form/FormHeader';
import { FormActions } from '@/components/helper/form/FormActions'; import { FormActions } from '@/components/helper/form/FormActions';
import { import {
@@ -29,6 +28,7 @@ import { SupplierApi, WarehouseApi } from '@/services/api/master-data';
import { ProductWarehouseApi } from '@/services/api/inventory'; import { ProductWarehouseApi } from '@/services/api/inventory';
import { toast } from 'react-hot-toast'; import { toast } from 'react-hot-toast';
import FileInput from '@/components/input/FileInput'; import FileInput from '@/components/input/FileInput';
import FieldMessage from '@/components/helper/FieldMessage';
interface MovementFormProps { interface MovementFormProps {
type?: 'add' | 'edit' | 'detail'; type?: 'add' | 'edit' | 'detail';
@@ -863,23 +863,26 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
<tr key={`product-row-${idx}-${product.product_id}`}> <tr key={`product-row-${idx}-${product.product_id}`}>
{type !== 'detail' && ( {type !== 'detail' && (
<td> <td>
<input <div className='flex flex-col items-start gap-2'>
type='checkbox' <input
className='checkbox' type='checkbox'
checked={selectedProducts.includes(idx)} className='checkbox'
onChange={(e) => { checked={selectedProducts.includes(idx)}
if (e.target.checked) { onChange={(e) => {
setSelectedProducts([ if (e.target.checked) {
...selectedProducts, setSelectedProducts([
idx, ...selectedProducts,
]); idx,
} else { ]);
setSelectedProducts( } else {
selectedProducts.filter((i) => i !== idx) setSelectedProducts(
); selectedProducts.filter((i) => i !== idx)
} );
}} }
/> }}
/>
<FieldMessage message={null} isVisible={false} />
</div>
</td> </td>
)} )}
<td> <td>
@@ -954,17 +957,20 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
</td> </td>
{type !== 'detail' && ( {type !== 'detail' && (
<td> <td>
<Button <div className='flex flex-col items-start gap-2'>
type='button' <Button
color='error' type='button'
onClick={() => removeProduct(idx)} color='error'
> onClick={() => removeProduct(idx)}
<Icon >
icon='material-symbols:delete-outline-rounded' <Icon
width={24} icon='material-symbols:delete-outline-rounded'
height={24} width={24}
/> height={24}
</Button> />
</Button>
<FieldMessage message={null} isVisible={false} />
</div>
</td> </td>
)} )}
</tr> </tr>
@@ -1051,23 +1057,28 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
<tr key={`delivery-row-${idx}`}> <tr key={`delivery-row-${idx}`}>
{type !== 'detail' && ( {type !== 'detail' && (
<td> <td>
<input <div className='flex flex-col items-start gap-2'>
type='checkbox' <input
className='checkbox' type='checkbox'
checked={selectedDeliveries.includes(idx)} className='checkbox'
onChange={(e) => { checked={selectedDeliveries.includes(idx)}
if (e.target.checked) { onChange={(e) => {
setSelectedDeliveries([ if (e.target.checked) {
...selectedDeliveries, setSelectedDeliveries([
idx, ...selectedDeliveries,
]); idx,
} else { ]);
setSelectedDeliveries( } else {
selectedDeliveries.filter((i) => i !== idx) setSelectedDeliveries(
); selectedDeliveries.filter(
} (i) => i !== idx
}} )
/> );
}
}}
/>
<FieldMessage message={null} isVisible={false} />
</div>
</td> </td>
)} )}
<td> <td>
@@ -1267,17 +1278,20 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
</td> </td>
{type !== 'detail' && ( {type !== 'detail' && (
<td> <td>
<Button <div className='flex flex-col items-start gap-2'>
type='button' <Button
color='error' type='button'
onClick={() => removeDelivery(idx)} color='error'
> onClick={() => removeDelivery(idx)}
<Icon >
icon='material-symbols:delete-outline-rounded' <Icon
width={24} icon='material-symbols:delete-outline-rounded'
height={24} width={24}
/> height={24}
</Button> />
</Button>
<FieldMessage message={null} isVisible={false} />
</div>
</td> </td>
)} )}
</tr> </tr>