mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
refactor(FE-208,212): rename product_warehouse to warehouse and update related schema and form handling
This commit is contained in:
@@ -18,18 +18,13 @@ type PurchaseRequestFormSchemaType = {
|
||||
label: string;
|
||||
} | null;
|
||||
location_id: number;
|
||||
warehouse?: {
|
||||
value: number;
|
||||
label: string;
|
||||
} | null;
|
||||
warehouse_id: number;
|
||||
notes: string | null;
|
||||
items: {
|
||||
product_warehouse?: {
|
||||
warehouse?: {
|
||||
value: number;
|
||||
label: string;
|
||||
} | null;
|
||||
product_warehouse_id: number;
|
||||
warehouse_id: number;
|
||||
product?: {
|
||||
value: number;
|
||||
label: string;
|
||||
@@ -40,11 +35,11 @@ type PurchaseRequestFormSchemaType = {
|
||||
};
|
||||
|
||||
export type PurchaseItemSchema = {
|
||||
product_warehouse?: {
|
||||
warehouse?: {
|
||||
value: number;
|
||||
label: string;
|
||||
} | null;
|
||||
product_warehouse_id: number;
|
||||
warehouse_id: number;
|
||||
product?: {
|
||||
value: number;
|
||||
label: string;
|
||||
@@ -55,14 +50,14 @@ export type PurchaseItemSchema = {
|
||||
|
||||
const PurchaseItemObjectSchema: Yup.ObjectSchema<PurchaseItemSchema> =
|
||||
Yup.object({
|
||||
product_warehouse: Yup.object({
|
||||
warehouse: Yup.object({
|
||||
value: Yup.number().min(1).required(),
|
||||
label: Yup.string().required(),
|
||||
}).nullable(),
|
||||
product_warehouse_id: Yup.number()
|
||||
.required('Produk wajib dipilih!')
|
||||
.min(1, 'Produk wajib dipilih!')
|
||||
.typeError('Produk wajib dipilih!'),
|
||||
warehouse_id: Yup.number()
|
||||
.required('Gudang wajib dipilih!')
|
||||
.min(1, 'Gudang wajib dipilih!')
|
||||
.typeError('Gudang wajib dipilih!'),
|
||||
product: Yup.object({
|
||||
value: Yup.number().min(1).required(),
|
||||
label: Yup.string().required(),
|
||||
@@ -107,14 +102,6 @@ export const PurchaseRequestFormSchema: Yup.ObjectSchema<PurchaseRequestFormSche
|
||||
.required('Lokasi wajib dipilih!')
|
||||
.min(1, 'Lokasi wajib dipilih!')
|
||||
.typeError('Lokasi wajib dipilih!'),
|
||||
warehouse: Yup.object({
|
||||
value: Yup.number().min(1).required(),
|
||||
label: Yup.string().required(),
|
||||
}),
|
||||
warehouse_id: Yup.number()
|
||||
.required('Gudang wajib dipilih!')
|
||||
.min(1, 'Gudang wajib dipilih!')
|
||||
.typeError('Gudang wajib dipilih!'),
|
||||
notes: Yup.string().nullable().default(null),
|
||||
items: Yup.array()
|
||||
.of(PurchaseItemObjectSchema)
|
||||
@@ -154,13 +141,6 @@ export const getPurchaseRequestFormInitialValues = (
|
||||
}
|
||||
: null,
|
||||
location_id: initialValues?.location?.id ?? 0,
|
||||
warehouse: initialValues?.warehouse
|
||||
? {
|
||||
value: initialValues.warehouse.id,
|
||||
label: initialValues.warehouse.name,
|
||||
}
|
||||
: undefined,
|
||||
warehouse_id: initialValues?.warehouse?.id ?? 0,
|
||||
notes: initialValues?.notes ?? null,
|
||||
items: [],
|
||||
});
|
||||
|
||||
@@ -32,7 +32,6 @@ import {
|
||||
} from '@/services/api/master-data';
|
||||
import { Supplier } from '@/types/api/master-data/supplier';
|
||||
import { Product } from '@/types/api/master-data/product';
|
||||
import { ProductWarehouseApi } from '@/services/api/inventory';
|
||||
import { isResponseSuccess, isResponseError } from '@/lib/api-helper';
|
||||
import { PurchaseRequestApi } from '@/services/api/purchase';
|
||||
|
||||
@@ -63,18 +62,15 @@ const PurchaseRequestForm = ({
|
||||
useState('');
|
||||
|
||||
// ===== TYPE DEFINITIONS =====
|
||||
interface ProductWarehouseOptionType extends OptionType {
|
||||
product_id: number;
|
||||
product_warehouse_id: number;
|
||||
warehouse_id: number;
|
||||
warehouse_name: string;
|
||||
qty: number;
|
||||
interface ProductOptionType {
|
||||
value: number;
|
||||
label: string;
|
||||
}
|
||||
|
||||
// ===== UTILITY FUNCTIONS =====
|
||||
const isRepeaterInputError = (
|
||||
idx: number,
|
||||
field: 'product_warehouse_id' | 'product_id' | 'qty'
|
||||
field: 'warehouse_id' | 'product_id' | 'qty'
|
||||
): { isError: boolean; errorMessage: string } => {
|
||||
if (!formik.touched.items || !Array.isArray(formik.touched.items)) {
|
||||
return {
|
||||
@@ -83,7 +79,13 @@ const PurchaseRequestForm = ({
|
||||
};
|
||||
}
|
||||
|
||||
const touchedField = formik.touched.items[idx]?.[field];
|
||||
const touchedField = (
|
||||
formik.touched.items[idx] as Partial<{
|
||||
warehouse_id: boolean;
|
||||
product_id: boolean;
|
||||
qty: boolean;
|
||||
}>
|
||||
)?.[field];
|
||||
const errorItem = formik.errors.items?.[idx] as
|
||||
| Record<string, string>
|
||||
| undefined;
|
||||
@@ -142,7 +144,6 @@ const PurchaseRequestForm = ({
|
||||
|
||||
// ===== SELECT INPUT DATA =====
|
||||
const {
|
||||
inputValue: supplierSelectInputValue,
|
||||
setInputValue: setSupplierSelectInputValue,
|
||||
options: supplierOptions,
|
||||
isLoadingOptions: isLoadingSuppliers,
|
||||
@@ -150,7 +151,6 @@ const PurchaseRequestForm = ({
|
||||
} = useSelect<Supplier>(SupplierApi.basePath, 'id', 'name', 'search');
|
||||
|
||||
const {
|
||||
inputValue: areaSelectInputValue,
|
||||
setInputValue: setAreaSelectInputValue,
|
||||
options: areaOptions,
|
||||
isLoadingOptions: isLoadingAreas,
|
||||
@@ -176,27 +176,23 @@ const PurchaseRequestForm = ({
|
||||
: PurchaseRequestFormSchema,
|
||||
validateOnChange: true,
|
||||
validateOnBlur: true,
|
||||
validateOnMount: false,
|
||||
enableReinitialize: true,
|
||||
onSubmit: async (values) => {
|
||||
const payload: CreatePurchaseRequestPayload = {
|
||||
supplier_id:
|
||||
typeof values.supplier_id === 'string'
|
||||
? parseInt(values.supplier_id) || 0
|
||||
: values.supplier_id || 0,
|
||||
credit_term: values.credit_term || 0,
|
||||
credit_term:
|
||||
typeof values.credit_term === 'string'
|
||||
? parseInt(values.credit_term) || 0
|
||||
: values.credit_term || 0,
|
||||
notes: values.notes || '',
|
||||
items: (values.items || []).map((item) => ({
|
||||
warehouse_id:
|
||||
typeof values.warehouse_id === 'string'
|
||||
? parseInt(values.warehouse_id) || 0
|
||||
: values.warehouse_id || 0,
|
||||
product_id:
|
||||
typeof item.product_id === 'string'
|
||||
? parseInt(item.product_id) || 0
|
||||
: item.product_id || 0,
|
||||
qty:
|
||||
typeof item.qty === 'string'
|
||||
? parseFloat(item.qty) || 0
|
||||
: item.qty || 0,
|
||||
warehouse_id: Number(item.warehouse_id) || 0,
|
||||
product_id: Number(item.product_id) || 0,
|
||||
qty: Number(item.qty) || 0,
|
||||
})),
|
||||
};
|
||||
|
||||
@@ -215,57 +211,22 @@ const PurchaseRequestForm = ({
|
||||
});
|
||||
|
||||
// ===== API DATA FETCHING =====
|
||||
const productWarehousesUrl = useMemo(() => {
|
||||
if (!formik.values.warehouse_id || formik.values.warehouse_id === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const params = new URLSearchParams({
|
||||
warehouse_id: formik.values.warehouse_id.toString(),
|
||||
});
|
||||
|
||||
return `${ProductWarehouseApi.basePath}?${params.toString()}`;
|
||||
}, [formik.values.warehouse_id]);
|
||||
|
||||
const { data: productWarehouses, isLoading: isLoadingProductWarehouses } =
|
||||
useSWR(productWarehousesUrl, ProductWarehouseApi.getAllFetcher);
|
||||
|
||||
const getProductWarehouseOptionsForItem = useCallback(() => {
|
||||
if (!isResponseSuccess(productWarehouses)) return [];
|
||||
|
||||
return (
|
||||
productWarehouses?.data.map((pw) => ({
|
||||
value: pw.id,
|
||||
label: pw.product.name,
|
||||
product_id: pw.product.id,
|
||||
product_warehouse_id: pw.id,
|
||||
warehouse_id: pw.warehouse.id,
|
||||
warehouse_name: pw.warehouse.name,
|
||||
qty: pw.quantity,
|
||||
})) || []
|
||||
);
|
||||
}, [productWarehouses]);
|
||||
|
||||
const productUrl = useMemo(() => {
|
||||
const productIds =
|
||||
formik.values.items
|
||||
?.filter(
|
||||
(item) => item.product_id && typeof item.product_id === 'number'
|
||||
)
|
||||
.map((item) => item.product_id as number) || [];
|
||||
|
||||
return productIds.length > 0
|
||||
? `${ProductApi.basePath}?${new URLSearchParams({
|
||||
id: productIds.join(','),
|
||||
}).toString()}`
|
||||
: null;
|
||||
}, [formik.values.items]);
|
||||
|
||||
const { data: productsResponse } = useSWR(
|
||||
productUrl,
|
||||
const { data: productsResponse, isLoading: isLoadingProducts } = useSWR(
|
||||
`${ProductApi.basePath}`,
|
||||
ProductApi.getAllFetcher
|
||||
);
|
||||
|
||||
const productOptions = useMemo(() => {
|
||||
if (!isResponseSuccess(productsResponse)) return [];
|
||||
|
||||
return (
|
||||
productsResponse?.data.map((product: Product) => ({
|
||||
value: product.id,
|
||||
label: product.name,
|
||||
})) || []
|
||||
);
|
||||
}, [productsResponse]);
|
||||
|
||||
const productData = useMemo(() => {
|
||||
if (!isResponseSuccess(productsResponse)) return {};
|
||||
|
||||
@@ -340,19 +301,18 @@ const PurchaseRequestForm = ({
|
||||
);
|
||||
}, [warehouses]);
|
||||
|
||||
// Purchase Items Handlers
|
||||
const addPurchaseItem = () => {
|
||||
const newPurchaseItems = [
|
||||
const newItems = [
|
||||
...(formik.values.items || []),
|
||||
{
|
||||
product_warehouse: null,
|
||||
product_warehouse_id: null,
|
||||
warehouse: null,
|
||||
warehouse_id: 0,
|
||||
product: null,
|
||||
product_id: '',
|
||||
qty: '',
|
||||
product_id: 0,
|
||||
qty: 0,
|
||||
},
|
||||
];
|
||||
formik.setFieldValue('items', newPurchaseItems);
|
||||
formik.setFieldValue('items', newItems);
|
||||
};
|
||||
|
||||
const removePurchaseItem = (idx: number) => {
|
||||
@@ -445,9 +405,25 @@ const PurchaseRequestForm = ({
|
||||
supplierData.due_date.toString()
|
||||
);
|
||||
}
|
||||
|
||||
if (formik.values.items) {
|
||||
formik.values.items.forEach((_, idx) => {
|
||||
formik.setFieldValue(`items.${idx}.product`, null);
|
||||
formik.setFieldValue(`items.${idx}.product_id`, 0);
|
||||
formik.setFieldValue(`items.${idx}.qty`, 0);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
formik.setFieldTouched('credit_term', false);
|
||||
formik.setFieldValue('credit_term', '');
|
||||
|
||||
if (formik.values.items) {
|
||||
formik.values.items.forEach((_, idx) => {
|
||||
formik.setFieldValue(`items.${idx}.product`, null);
|
||||
formik.setFieldValue(`items.${idx}.product_id`, 0);
|
||||
formik.setFieldValue(`items.${idx}.qty`, 0);
|
||||
});
|
||||
}
|
||||
}
|
||||
}}
|
||||
options={supplierOptions}
|
||||
@@ -508,16 +484,8 @@ const PurchaseRequestForm = ({
|
||||
|
||||
if (formik.values.items) {
|
||||
formik.values.items.forEach((_, idx) => {
|
||||
formik.setFieldValue(
|
||||
`items.${idx}.product_warehouse`,
|
||||
null
|
||||
);
|
||||
formik.setFieldValue(
|
||||
`items.${idx}.product_warehouse_id`,
|
||||
null
|
||||
);
|
||||
formik.setFieldValue(`items.${idx}.product`, null);
|
||||
formik.setFieldValue(`items.${idx}.product_id`, '');
|
||||
formik.setFieldValue(`items.${idx}.product_id`, 0);
|
||||
});
|
||||
}
|
||||
}}
|
||||
@@ -553,16 +521,8 @@ const PurchaseRequestForm = ({
|
||||
|
||||
if (formik.values.items) {
|
||||
formik.values.items.forEach((_, idx) => {
|
||||
formik.setFieldValue(
|
||||
`items.${idx}.product_warehouse`,
|
||||
null
|
||||
);
|
||||
formik.setFieldValue(
|
||||
`items.${idx}.product_warehouse_id`,
|
||||
null
|
||||
);
|
||||
formik.setFieldValue(`items.${idx}.product`, null);
|
||||
formik.setFieldValue(`items.${idx}.product_id`, '');
|
||||
formik.setFieldValue(`items.${idx}.product_id`, 0);
|
||||
});
|
||||
}
|
||||
}}
|
||||
@@ -691,45 +651,42 @@ const PurchaseRequestForm = ({
|
||||
? 'Pilih Gudang...'
|
||||
: 'Pilih Area dan Lokasi terlebih dahulu'
|
||||
}
|
||||
value={formik.values.warehouse}
|
||||
value={item.warehouse}
|
||||
onChange={(val) => {
|
||||
const warehouse = val as OptionType | null;
|
||||
formik.setFieldTouched('warehouse_id', true);
|
||||
formik.setFieldValue(
|
||||
'warehouse_id',
|
||||
(warehouse as OptionType)?.value || 0
|
||||
const warehouseId =
|
||||
(warehouse as OptionType)?.value || 0;
|
||||
|
||||
formik.setFieldTouched(
|
||||
`items.${idx}.warehouse`,
|
||||
true
|
||||
);
|
||||
formik.setFieldTouched('warehouse', true);
|
||||
formik.setFieldValue('warehouse', warehouse);
|
||||
if (formik.values.items) {
|
||||
formik.values.items.forEach((_, idx) => {
|
||||
formik.setFieldValue(
|
||||
`items.${idx}.product_warehouse`,
|
||||
null
|
||||
);
|
||||
formik.setFieldValue(
|
||||
`items.${idx}.product_warehouse_id`,
|
||||
null
|
||||
);
|
||||
formik.setFieldValue(
|
||||
`items.${idx}.product`,
|
||||
null
|
||||
);
|
||||
formik.setFieldValue(
|
||||
`items.${idx}.product_id`,
|
||||
''
|
||||
);
|
||||
});
|
||||
}
|
||||
formik.setFieldValue(
|
||||
`items.${idx}.warehouse`,
|
||||
warehouse
|
||||
);
|
||||
formik.setFieldTouched(
|
||||
`items.${idx}.warehouse_id`,
|
||||
true
|
||||
);
|
||||
formik.setFieldValue(
|
||||
`items.${idx}.warehouse_id`,
|
||||
warehouseId
|
||||
);
|
||||
|
||||
formik.setFieldValue(`items.${idx}.product`, null);
|
||||
formik.setFieldValue(`items.${idx}.product_id`, 0);
|
||||
}}
|
||||
options={warehouseOptions}
|
||||
onInputChange={setWarehouseSelectInputValue}
|
||||
isLoading={isLoadingWarehouses}
|
||||
isError={
|
||||
formik.touched.warehouse_id &&
|
||||
Boolean(formik.errors.warehouse_id)
|
||||
isRepeaterInputError(idx, 'warehouse_id').isError
|
||||
}
|
||||
errorMessage={
|
||||
isRepeaterInputError(idx, 'warehouse_id')
|
||||
.errorMessage
|
||||
}
|
||||
errorMessage={formik.errors.warehouse_id as string}
|
||||
isDisabled={
|
||||
type === 'detail' ||
|
||||
!formik.values.area_id ||
|
||||
@@ -746,32 +703,20 @@ const PurchaseRequestForm = ({
|
||||
<td>
|
||||
<SelectInput
|
||||
required
|
||||
value={item.product_warehouse}
|
||||
key={`product-warehouse-${idx}-${formik.values.warehouse_id}`}
|
||||
value={item.product ?? undefined}
|
||||
onChange={(val) => {
|
||||
const productWarehouse =
|
||||
val as ProductWarehouseOptionType | null;
|
||||
formik.setFieldTouched(
|
||||
`items.${idx}.product_warehouse`,
|
||||
true
|
||||
);
|
||||
formik.setFieldValue(
|
||||
`items.${idx}.product_warehouse`,
|
||||
productWarehouse
|
||||
);
|
||||
formik.setFieldTouched(
|
||||
`items.${idx}.product_warehouse_id`,
|
||||
true
|
||||
);
|
||||
formik.setFieldValue(
|
||||
`items.${idx}.product_warehouse_id`,
|
||||
(productWarehouse as ProductWarehouseOptionType)
|
||||
?.value || 0
|
||||
);
|
||||
|
||||
const product = val as ProductOptionType | null;
|
||||
const productId =
|
||||
(productWarehouse as ProductWarehouseOptionType)
|
||||
?.product_id || 0;
|
||||
(product as ProductOptionType)?.value || 0;
|
||||
|
||||
formik.setFieldTouched(
|
||||
`items.${idx}.product`,
|
||||
true
|
||||
);
|
||||
formik.setFieldValue(
|
||||
`items.${idx}.product`,
|
||||
product
|
||||
);
|
||||
formik.setFieldTouched(
|
||||
`items.${idx}.product_id`,
|
||||
true
|
||||
@@ -781,27 +726,17 @@ const PurchaseRequestForm = ({
|
||||
productId
|
||||
);
|
||||
}}
|
||||
options={getProductWarehouseOptionsForItem()}
|
||||
isLoading={isLoadingProductWarehouses}
|
||||
options={productOptions}
|
||||
isLoading={isLoadingProducts}
|
||||
isError={
|
||||
isRepeaterInputError(idx, 'product_warehouse_id')
|
||||
.isError
|
||||
isRepeaterInputError(idx, 'product_id').isError
|
||||
}
|
||||
errorMessage={
|
||||
isRepeaterInputError(idx, 'product_warehouse_id')
|
||||
.errorMessage
|
||||
}
|
||||
isDisabled={
|
||||
type === 'detail' || !formik.values.warehouse_id
|
||||
}
|
||||
isClearable={
|
||||
type !== 'detail' && !!formik.values.warehouse_id
|
||||
}
|
||||
placeholder={
|
||||
!formik.values.warehouse_id
|
||||
? 'Pilih Gudang terlebih dahulu'
|
||||
: 'Pilih Produk'
|
||||
isRepeaterInputError(idx, 'product_id').errorMessage
|
||||
}
|
||||
isDisabled={type === 'detail'}
|
||||
isClearable={type !== 'detail'}
|
||||
placeholder='Pilih Produk'
|
||||
className={{
|
||||
wrapper: 'min-w-32',
|
||||
}}
|
||||
@@ -841,7 +776,7 @@ const PurchaseRequestForm = ({
|
||||
).toLocaleString('en-US')
|
||||
: ''
|
||||
}
|
||||
onChange={(e) => {}}
|
||||
onChange={() => {}}
|
||||
onBlur={formik.handleBlur}
|
||||
type='text'
|
||||
className={{
|
||||
|
||||
Reference in New Issue
Block a user