mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-25 07:45:47 +00:00
refactor(FE-177-166-167): separate table repeater component and adjust data types with new API Payload
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
import * as Yup from 'yup';
|
||||
|
||||
type SalesOrderProductSchemaType = {
|
||||
kandang_id?: number;
|
||||
kandang?: {
|
||||
value: number;
|
||||
label: string;
|
||||
} | null;
|
||||
product_warehouse?: {
|
||||
value: number;
|
||||
label: string;
|
||||
} | null;
|
||||
product_warehouse_id?: number;
|
||||
unit_price: string | number | undefined;
|
||||
total_weight: string | number | undefined;
|
||||
qty: string | number | undefined;
|
||||
avg_weight: string | number | undefined;
|
||||
total_price: string | number | undefined;
|
||||
};
|
||||
|
||||
export const SalesOrderProductSchema: Yup.ObjectSchema<SalesOrderProductSchemaType> =
|
||||
Yup.object({
|
||||
kandang: Yup.object({
|
||||
value: Yup.number().min(1).required(),
|
||||
label: Yup.string().required(),
|
||||
}).nullable(),
|
||||
kandang_id: Yup.number()
|
||||
.min(1, 'Kandang wajib diisi!')
|
||||
.required('Kandang wajib diisi!'),
|
||||
product_warehouse: Yup.object({
|
||||
value: Yup.number().min(1).required(),
|
||||
label: Yup.string().required(),
|
||||
}).nullable(),
|
||||
product_warehouse_id: Yup.number()
|
||||
.min(1, 'Produk wajib diisi!')
|
||||
.required('Produk wajib diisi!'),
|
||||
unit_price: Yup.number()
|
||||
.min(1, 'Harga Satuan wajib diisi!')
|
||||
.required('Harga Satuan wajib diisi!'),
|
||||
total_weight: Yup.number()
|
||||
.min(1, 'Total Bobot wajib diisi!')
|
||||
.required('Total Bobot wajib diisi!'),
|
||||
qty: Yup.number()
|
||||
.min(1, 'Kuantitas wajib diisi!')
|
||||
.required('Kuantitas wajib diisi!'),
|
||||
avg_weight: Yup.number()
|
||||
.min(0, 'Avg. Bobot wajib diisi!')
|
||||
.required('Avg. Bobot wajib diisi!'),
|
||||
total_price: Yup.number()
|
||||
.min(1, 'Total Penjualan wajib diisi!')
|
||||
.required('Total Penjualan wajib diisi!'),
|
||||
});
|
||||
|
||||
export type SalesOrderProductFormValues = Yup.InferType<
|
||||
typeof SalesOrderProductSchema
|
||||
>;
|
||||
@@ -0,0 +1,315 @@
|
||||
'use client';
|
||||
|
||||
import { useFormik } from 'formik';
|
||||
import {
|
||||
SalesOrderProductFormValues,
|
||||
SalesOrderProductSchema,
|
||||
} from './SalesOrderProduct.schema';
|
||||
import { RefObject, useEffect, useState } from 'react';
|
||||
import SelectInput, {
|
||||
OptionType,
|
||||
useSelect,
|
||||
} from '@/components/input/SelectInput';
|
||||
import { Kandang } from '@/types/api/master-data/kandang';
|
||||
import { KandangApi } from '@/services/api/master-data';
|
||||
import { ProductWarehouse } from '@/types/api/inventory/product-warehouse';
|
||||
import { ProductWarehouseApi } from '@/services/api/inventory';
|
||||
import NumberInput from '@/components/input/NumberInput';
|
||||
import Button from '@/components/Button';
|
||||
import { isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
const SalesOrderProductForm = ({
|
||||
initialValues,
|
||||
modalRef,
|
||||
onSubmitForm,
|
||||
}: {
|
||||
initialValues?: SalesOrderProductFormValues;
|
||||
modalRef?: RefObject<HTMLDialogElement | null>;
|
||||
onSubmitForm?: (value: SalesOrderProductFormValues) => Promise<void>;
|
||||
}) => {
|
||||
// State
|
||||
const [selectedOptionsKandang, setSelectedOptionsKandang] =
|
||||
useState<OptionType | null>(null);
|
||||
const [selectedOptionsWarehouse, setSelectedOptionsWarehouse] = useState<
|
||||
OptionType | null | undefined
|
||||
>(undefined);
|
||||
const [formErrorMessage, setFormErrorMessage] = useState('');
|
||||
|
||||
// Options Data
|
||||
const {
|
||||
options: kandangSourceOptions,
|
||||
rawData: kandangSourceRawData,
|
||||
isLoadingOptions: isLoadingKandangSourceOptions,
|
||||
} = useSelect<Kandang>(KandangApi.basePath, 'id', 'name');
|
||||
const {
|
||||
options: warehouseSourceOptions,
|
||||
rawData: warehouseSourceRawData,
|
||||
isLoadingOptions: isLoadingWarehouseSourceOptions,
|
||||
} = useSelect<ProductWarehouse>(
|
||||
ProductWarehouseApi.basePath,
|
||||
'id',
|
||||
'product.name',
|
||||
'search',
|
||||
{
|
||||
warehouse_id: selectedOptionsKandang?.value?.toString() ?? '',
|
||||
}
|
||||
);
|
||||
|
||||
// Handler
|
||||
const kandangChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||
setSelectedOptionsKandang(val as OptionType);
|
||||
formik.setFieldValue('kandang', val as OptionType);
|
||||
formik.setFieldValue('kandang_id', (val as OptionType)?.value);
|
||||
formik.setFieldValue('product_warehouse_id', null);
|
||||
formik.setFieldValue('qty', null);
|
||||
warehouseChangeHandler(null);
|
||||
};
|
||||
|
||||
const warehouseChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||
setSelectedOptionsWarehouse(val as OptionType);
|
||||
formik.setFieldValue('product_warehouse', val as OptionType);
|
||||
formik.setFieldValue('product_warehouse_id', (val as OptionType)?.value);
|
||||
if (isResponseSuccess(warehouseSourceRawData)) {
|
||||
const productWarehouse = warehouseSourceRawData?.data.find(
|
||||
(item: ProductWarehouse) => item.id === (val as OptionType)?.value
|
||||
);
|
||||
if (selectedOptionsWarehouse?.value !== null) {
|
||||
formik.setFieldValue('qty', productWarehouse?.quantity);
|
||||
handleBlurField('qty');
|
||||
} else {
|
||||
formik.setFieldValue('qty', null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Formik
|
||||
const formik = useFormik<SalesOrderProductFormValues>({
|
||||
enableReinitialize: true,
|
||||
initialValues: {
|
||||
kandang_id: initialValues?.kandang_id || undefined,
|
||||
kandang: initialValues?.kandang || undefined,
|
||||
product_warehouse: initialValues?.product_warehouse || undefined,
|
||||
product_warehouse_id: initialValues?.product_warehouse_id || undefined,
|
||||
unit_price: initialValues?.unit_price || undefined,
|
||||
total_weight: initialValues?.total_weight || undefined,
|
||||
qty: initialValues?.qty || undefined,
|
||||
avg_weight: initialValues?.avg_weight || undefined,
|
||||
total_price: initialValues?.total_price || undefined,
|
||||
},
|
||||
validationSchema: SalesOrderProductSchema,
|
||||
onSubmit: async (values) => {
|
||||
setFormErrorMessage('');
|
||||
if (
|
||||
isResponseSuccess(kandangSourceRawData) &&
|
||||
isResponseSuccess(warehouseSourceRawData)
|
||||
) {
|
||||
const productWarehouse = warehouseSourceRawData?.data.find(
|
||||
(item: ProductWarehouse) => item.id === values.product_warehouse_id
|
||||
);
|
||||
const kandang = kandangSourceRawData?.data.find(
|
||||
(item: Kandang) => item.id === values.kandang_id
|
||||
);
|
||||
|
||||
onSubmitForm?.(values);
|
||||
handleResetForm();
|
||||
}
|
||||
},
|
||||
});
|
||||
const { setValues: formikSetValues } = formik;
|
||||
|
||||
useEffect(() => {
|
||||
formikSetValues(formik.initialValues);
|
||||
}, [formikSetValues, formik.initialValues]);
|
||||
|
||||
const handleResetForm = () => {
|
||||
setSelectedOptionsKandang(null);
|
||||
setSelectedOptionsWarehouse(null);
|
||||
setFormErrorMessage('');
|
||||
formik.resetForm({
|
||||
values: {
|
||||
kandang_id: undefined,
|
||||
kandang: null,
|
||||
product_warehouse: null,
|
||||
product_warehouse_id: undefined,
|
||||
unit_price: '',
|
||||
total_weight: '',
|
||||
qty: '',
|
||||
avg_weight: '',
|
||||
total_price: '',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleBlurField = (field: string) => {
|
||||
const { qty, unit_price, total_price, avg_weight, total_weight } =
|
||||
formik.values;
|
||||
|
||||
if (field === 'unit_price' || field === 'total_price' || field === 'qty') {
|
||||
if (qty && unit_price && field === 'unit_price') {
|
||||
formik.setFieldValue(
|
||||
'total_price',
|
||||
(qty as number) * (unit_price as number)
|
||||
);
|
||||
} else if (qty && total_price && field === 'total_price') {
|
||||
formik.setFieldValue(
|
||||
'unit_price',
|
||||
(total_price as number) / (qty as number)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (field === 'avg_weight' || field === 'total_weight' || field === 'qty') {
|
||||
if (qty && avg_weight && field === 'avg_weight') {
|
||||
formik.setFieldValue(
|
||||
'total_weight',
|
||||
(qty as number) * (avg_weight as number)
|
||||
);
|
||||
} else if (qty && total_weight && field === 'total_weight') {
|
||||
formik.setFieldValue(
|
||||
'avg_weight',
|
||||
(total_weight as number) / (qty as number)
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<form
|
||||
className='size-full'
|
||||
onSubmit={formik.handleSubmit}
|
||||
onReset={handleResetForm}
|
||||
>
|
||||
<div className='grid grid-cols-2 gap-4 z-200'>
|
||||
{/* <PatternInput
|
||||
name='vehicle_number'
|
||||
label='No. Polisi'
|
||||
format='AA #### AAA'
|
||||
mask='_'
|
||||
inputVehicleNumber
|
||||
required
|
||||
type='text'
|
||||
placeholder='B 1234 CDE'
|
||||
value={formatVechicleNumber(formik.values.vehicle_number ?? '')}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
isError={
|
||||
formik.touched.vehicle_number &&
|
||||
Boolean(formik.errors.vehicle_number)
|
||||
}
|
||||
errorMessage={formik.errors.vehicle_number}
|
||||
/> */}
|
||||
<SelectInput
|
||||
required
|
||||
label='Kandang'
|
||||
options={kandangSourceOptions}
|
||||
isLoading={isLoadingKandangSourceOptions}
|
||||
value={selectedOptionsKandang}
|
||||
onChange={kandangChangeHandler}
|
||||
isClearable
|
||||
menuPortalTarget={modalRef?.current}
|
||||
isError={
|
||||
formik.touched.kandang_id && Boolean(formik.errors.kandang_id)
|
||||
}
|
||||
errorMessage={formik.errors.kandang_id}
|
||||
placeholder='Pilih Kandang'
|
||||
/>
|
||||
<SelectInput
|
||||
required
|
||||
label='Produk'
|
||||
options={warehouseSourceOptions}
|
||||
isLoading={isLoadingWarehouseSourceOptions}
|
||||
value={selectedOptionsWarehouse}
|
||||
onChange={warehouseChangeHandler}
|
||||
isClearable
|
||||
menuPortalTarget={modalRef?.current}
|
||||
placeholder='Pilih Kandang Terlebih Dahulu'
|
||||
isDisabled={!selectedOptionsKandang?.value}
|
||||
isError={
|
||||
formik.touched.product_warehouse_id &&
|
||||
Boolean(formik.errors.product_warehouse_id)
|
||||
}
|
||||
errorMessage={formik.errors.product_warehouse_id}
|
||||
/>
|
||||
<NumberInput
|
||||
required
|
||||
label='Kuantitas'
|
||||
name='qty'
|
||||
value={formik.values.qty}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={() => handleBlurField('qty')}
|
||||
isError={formik.touched.qty && Boolean(formik.errors.qty)}
|
||||
errorMessage={formik.errors.qty}
|
||||
placeholder='Masukan Kuantitas'
|
||||
/>
|
||||
<NumberInput
|
||||
required
|
||||
label='Avg. Bobot (Kg)'
|
||||
name='avg_weight'
|
||||
value={formik.values.avg_weight}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={() => handleBlurField('avg_weight')}
|
||||
isError={
|
||||
formik.touched.avg_weight && Boolean(formik.errors.avg_weight)
|
||||
}
|
||||
errorMessage={formik.errors.avg_weight}
|
||||
placeholder='Masukan Bobot Rata-rata'
|
||||
/>
|
||||
<NumberInput
|
||||
required
|
||||
label='Harga Satuan (Rp)'
|
||||
name='unit_price'
|
||||
value={formik.values.unit_price}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={() => handleBlurField('unit_price')}
|
||||
isError={
|
||||
formik.touched.unit_price && Boolean(formik.errors.unit_price)
|
||||
}
|
||||
errorMessage={formik.errors.unit_price}
|
||||
placeholder='Masukan Harga Satuan'
|
||||
/>
|
||||
<NumberInput
|
||||
required
|
||||
label='Total Bobot (Kg)'
|
||||
name='total_weight'
|
||||
value={formik.values.total_weight}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={() => handleBlurField('total_weight')}
|
||||
isError={
|
||||
formik.touched.total_weight && Boolean(formik.errors.total_weight)
|
||||
}
|
||||
errorMessage={formik.errors.total_weight}
|
||||
placeholder='Masukan Total Bobot'
|
||||
/>
|
||||
<NumberInput
|
||||
required
|
||||
label='Total Penjualan (Rp)'
|
||||
name='total_price'
|
||||
value={formik.values.total_price}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={() => handleBlurField('total_price')}
|
||||
isError={
|
||||
formik.touched.total_price && Boolean(formik.errors.total_price)
|
||||
}
|
||||
errorMessage={formik.errors.total_price}
|
||||
placeholder='Masukan Total Penjualan'
|
||||
/>
|
||||
</div>
|
||||
<div className='flex flex-row justify-end gap-3 mt-4'>
|
||||
<Button type='reset' color='warning' onClick={handleResetForm}>
|
||||
Reset
|
||||
</Button>
|
||||
<Button
|
||||
type='submit'
|
||||
isLoading={formik.isSubmitting}
|
||||
disabled={!formik.isValid || formik.isSubmitting}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SalesOrderProductForm;
|
||||
Reference in New Issue
Block a user