mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 21:41:57 +00:00
feat(FE-177): refactor sales order management with new schema and API integration
This commit is contained in:
@@ -12,7 +12,12 @@ import { TableRowSizeSelector } from '@/components/table/TableRowSizeSelector';
|
||||
import { TableToolbar } from '@/components/table/TableToolbar';
|
||||
import { ROWS_OPTIONS } from '@/config/constant';
|
||||
import { isResponseSuccess } from '@/lib/api-helper';
|
||||
import { cn, formatCurrency, formatVechicleNumber } from '@/lib/helper';
|
||||
import {
|
||||
cn,
|
||||
formatCurrency,
|
||||
formatDate,
|
||||
formatVechicleNumber,
|
||||
} from '@/lib/helper';
|
||||
import { MarketingApi } from '@/services/api/marketing/marketing';
|
||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||
import { Marketing, MarketingProduct } from '@/types/api/marketing/marketing';
|
||||
@@ -216,12 +221,15 @@ const SalesOrderTable = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: 'so_number',
|
||||
accessorKey: 'name',
|
||||
header: 'No. Order',
|
||||
},
|
||||
{
|
||||
accessorKey: 'so_date',
|
||||
header: 'Tanggal',
|
||||
cell: (props) => {
|
||||
return formatDate(props.row.original.so_date, 'DD MMM yyyy');
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'approval.step_name',
|
||||
@@ -232,8 +240,18 @@ const SalesOrderTable = () => {
|
||||
header: 'Customer',
|
||||
},
|
||||
{
|
||||
accessorKey: 'grand_total',
|
||||
accessorFn: (row) =>
|
||||
row.marketing_products
|
||||
?.map((product) => product.total_price)
|
||||
.reduce((a, b) => a + b, 0) ?? 0,
|
||||
header: 'Grand Total',
|
||||
cell: (props) => {
|
||||
return formatCurrency(
|
||||
props.row.original?.marketing_products
|
||||
?.map((product) => product.total_price)
|
||||
.reduce((a, b) => a + b, 0) ?? 0
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'marketing_products.length',
|
||||
|
||||
@@ -5,10 +5,15 @@ import Card from '@/components/Card';
|
||||
import { FormHeader } from '@/components/helper/form/FormHeader';
|
||||
import { useModal } from '@/components/Modal';
|
||||
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
||||
import ApprovalSteps, {
|
||||
useApprovalSteps,
|
||||
} from '@/components/pages/ApprovalSteps';
|
||||
import Table from '@/components/Table';
|
||||
import { MARKETING_APPROVAL_LINE } from '@/config/approval-line';
|
||||
import {
|
||||
cn,
|
||||
formatCurrency,
|
||||
formatDate,
|
||||
formatNumber,
|
||||
formatVechicleNumber,
|
||||
} from '@/lib/helper';
|
||||
@@ -20,27 +25,42 @@ import toast from 'react-hot-toast';
|
||||
|
||||
const SalesOrderDetail = ({
|
||||
initialValues,
|
||||
refreshValues,
|
||||
refresh,
|
||||
}: {
|
||||
initialValues?: Marketing;
|
||||
refreshValues?: () => void;
|
||||
refresh?: () => void;
|
||||
}) => {
|
||||
const [approvalAction, setApprovalAction] = useState<'approve' | 'reject'>(
|
||||
'approve'
|
||||
const [approvalAction, setApprovalAction] = useState<'APPROVED' | 'REJECTED'>(
|
||||
'APPROVED'
|
||||
);
|
||||
const [grandTotal, setGrandTotal] = useState(
|
||||
initialValues?.marketing_products
|
||||
?.map((item) => item.total_price)
|
||||
.reduce((a, b) => a + b, 0)
|
||||
);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const deleteModal = useModal();
|
||||
const confirmationModal = useModal();
|
||||
const deliveryModal = useModal();
|
||||
const {
|
||||
approvals,
|
||||
isLoading: isLoadingApproval,
|
||||
refresh: refreshApproval,
|
||||
} = useApprovalSteps({
|
||||
latestApproval: initialValues?.approval,
|
||||
approvalLines: MARKETING_APPROVAL_LINE,
|
||||
moduleName: 'MARKETINGS',
|
||||
moduleId: initialValues?.id as number as unknown as string,
|
||||
});
|
||||
|
||||
const approveClickHandler = () => {
|
||||
setApprovalAction('approve');
|
||||
setApprovalAction('APPROVED');
|
||||
confirmationModal.openModal();
|
||||
};
|
||||
|
||||
const rejectClickHandler = () => {
|
||||
setApprovalAction('reject');
|
||||
setApprovalAction('REJECTED');
|
||||
confirmationModal.openModal();
|
||||
};
|
||||
|
||||
@@ -54,23 +74,24 @@ const SalesOrderDetail = ({
|
||||
|
||||
const confirmationModalDeleteClickHandler = async () => {
|
||||
setIsLoading(true);
|
||||
// await MarketingApi.delete(initialValues?.id as number);
|
||||
const res = await MarketingApi.delete(initialValues?.id as number);
|
||||
setIsLoading(false);
|
||||
deleteModal.closeModal();
|
||||
toast.success('Successfully deleted Sales Order!');
|
||||
refreshValues?.();
|
||||
toast.success(res?.message as string);
|
||||
refresh?.();
|
||||
};
|
||||
|
||||
const confirmationModalApproveClickHandler = async () => {
|
||||
setIsLoading(true);
|
||||
// await MarketingApi.singleApproval(
|
||||
// initialValues?.id as number,
|
||||
// approvalAction
|
||||
// );
|
||||
const res = await MarketingApi.singleApproval(
|
||||
initialValues?.id as number,
|
||||
approvalAction
|
||||
);
|
||||
setIsLoading(false);
|
||||
confirmationModal.closeModal();
|
||||
toast.success('Successfully approved Sales Order!');
|
||||
refreshValues?.();
|
||||
toast.success(res?.message as string);
|
||||
refresh?.();
|
||||
refreshApproval?.();
|
||||
};
|
||||
|
||||
const confirmationModalDeliveryClickHandler = async () => {
|
||||
@@ -79,7 +100,7 @@ const SalesOrderDetail = ({
|
||||
setIsLoading(false);
|
||||
deliveryModal.closeModal();
|
||||
toast.success('Successfully delivered Sales Order!');
|
||||
refreshValues?.();
|
||||
refresh?.();
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -89,6 +110,9 @@ const SalesOrderDetail = ({
|
||||
title='Detail Sales Order'
|
||||
backUrl='/marketing/sales-orders'
|
||||
/>
|
||||
{!isLoadingApproval && approvals && (
|
||||
<ApprovalSteps approvals={approvals} />
|
||||
)}
|
||||
<div className='flex-row flex gap-3'>
|
||||
{initialValues?.approval?.step_number != 3 && (
|
||||
<>
|
||||
@@ -117,6 +141,7 @@ const SalesOrderDetail = ({
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Card
|
||||
title='Informasi Sales Order'
|
||||
className={{
|
||||
@@ -131,7 +156,7 @@ const SalesOrderDetail = ({
|
||||
No. Sales Order
|
||||
</td>
|
||||
<td>:</td>
|
||||
<td width='50%'>{initialValues?.so_number}</td>
|
||||
<td width='50%'>{initialValues?.name}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='font-semibold'>Nama Pelanggan</td>
|
||||
@@ -146,14 +171,12 @@ const SalesOrderDetail = ({
|
||||
<tr>
|
||||
<td className='font-semibold'>Tanggal Penjualan</td>
|
||||
<td>:</td>
|
||||
<td>{initialValues?.so_date}</td>
|
||||
<td>{formatDate(initialValues?.so_date, 'DD MMM yyyy')}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='font-semibold'>Total Penjualan</td>
|
||||
<td>:</td>
|
||||
<td>
|
||||
{formatCurrency(initialValues?.grand_total as number)}
|
||||
</td>
|
||||
<td>{formatCurrency(grandTotal as number)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='font-semibold'>Catatan</td>
|
||||
@@ -178,7 +201,7 @@ const SalesOrderDetail = ({
|
||||
header: 'No. Polisi',
|
||||
accessorFn(row) {
|
||||
return formatVechicleNumber(
|
||||
row.marketing_delivery_products?.vehicle_number as string
|
||||
row.delivery_product?.vehicle_number as string
|
||||
);
|
||||
},
|
||||
},
|
||||
@@ -275,14 +298,14 @@ const SalesOrderDetail = ({
|
||||
/>
|
||||
<ConfirmationModal
|
||||
ref={confirmationModal.ref}
|
||||
type={approvalAction === 'approve' ? 'success' : 'error'}
|
||||
type={approvalAction === 'APPROVED' ? 'success' : 'error'}
|
||||
text={`Apakah anda yakin ingin ${approvalAction} data penjualan ini?`}
|
||||
secondaryButton={{
|
||||
text: 'Tidak',
|
||||
}}
|
||||
primaryButton={{
|
||||
text: 'Ya',
|
||||
color: approvalAction === 'approve' ? 'success' : 'error',
|
||||
color: approvalAction === 'APPROVED' ? 'success' : 'error',
|
||||
isLoading: isLoading,
|
||||
onClick: confirmationModalApproveClickHandler,
|
||||
}}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import * as Yup from 'yup';
|
||||
import { MarketingProduct } from '@/types/api/marketing/marketing';
|
||||
import {
|
||||
MarketingProductFormValues,
|
||||
MarketingProductSchema,
|
||||
SalesOrderProductFormValues,
|
||||
SalesOrderProductSchema,
|
||||
} from './repeater/MarketingProduct.schema';
|
||||
|
||||
type MarketingSchema = {
|
||||
type MarketingSchemaType = {
|
||||
customer_id: number | undefined;
|
||||
sales_person_id: number | undefined;
|
||||
customer:
|
||||
| {
|
||||
value: number;
|
||||
@@ -16,23 +17,28 @@ type MarketingSchema = {
|
||||
| null;
|
||||
so_date: string | undefined;
|
||||
notes: string | undefined;
|
||||
marketing_products: MarketingProductFormValues[];
|
||||
};
|
||||
|
||||
export const MarketingSchema: Yup.ObjectSchema<MarketingSchema> = Yup.object({
|
||||
customer_id: Yup.number().required('Customer wajib diisi!'),
|
||||
customer: Yup.object({
|
||||
value: Yup.number().required(),
|
||||
label: Yup.string().required(),
|
||||
}).nullable(),
|
||||
so_date: Yup.string().required('Tanggal wajib diisi!'),
|
||||
notes: Yup.string().required('Catatan wajib diisi!'),
|
||||
marketing_products: Yup.array()
|
||||
.of(MarketingProductSchema)
|
||||
.min(1, 'Minimal harus ada 1 produk!')
|
||||
.required('Produk wajib diisi!'),
|
||||
});
|
||||
type SalesOrderSchemaType = MarketingSchemaType & {
|
||||
marketing_products: SalesOrderProductFormValues[];
|
||||
};
|
||||
|
||||
export const UpdateMarketingSchema = MarketingSchema;
|
||||
export const SalesOrderSchema: Yup.ObjectSchema<SalesOrderSchemaType> =
|
||||
Yup.object({
|
||||
customer_id: Yup.number().required('Customer wajib diisi!'),
|
||||
sales_person_id: Yup.number().required('Sales Person wajib diisi!'),
|
||||
customer: Yup.object({
|
||||
value: Yup.number().required(),
|
||||
label: Yup.string().required(),
|
||||
}).nullable(),
|
||||
so_date: Yup.string().required('Tanggal wajib diisi!'),
|
||||
notes: Yup.string().required('Catatan wajib diisi!'),
|
||||
marketing_products: Yup.array()
|
||||
.of(SalesOrderProductSchema)
|
||||
.min(1, 'Produk wajib diisi!')
|
||||
.required('Produk wajib diisi!'),
|
||||
});
|
||||
|
||||
export type MarketingFormValues = Yup.InferType<typeof MarketingSchema>;
|
||||
export const UpdateSalesOrderSchema = SalesOrderSchema;
|
||||
|
||||
export type SalesOrderFormValues = Yup.InferType<typeof SalesOrderSchema>;
|
||||
|
||||
@@ -12,10 +12,10 @@ import TextArea from '@/components/input/TextArea';
|
||||
import Modal, { useModal } from '@/components/Modal';
|
||||
import * as TanStack from '@tanstack/react-table';
|
||||
import Table from '@/components/Table'; // Keep this import
|
||||
import { cn, formatCurrency, formatNumber } from '@/lib/helper';
|
||||
import { cn, formatCurrency, formatDate, formatNumber } from '@/lib/helper';
|
||||
import {
|
||||
CreateMarketingPayload,
|
||||
CreateMarketingProductPayload,
|
||||
CreateSalesOrderPayload,
|
||||
CreateSalesOrderProductPayload,
|
||||
Marketing,
|
||||
MarketingProduct,
|
||||
} from '@/types/api/marketing/marketing';
|
||||
@@ -26,10 +26,10 @@ import CheckboxInput from '@/components/input/CheckboxInput';
|
||||
import { Customer } from '@/types/api/master-data/customer';
|
||||
import { CustomerApi } from '@/services/api/master-data';
|
||||
import { useFormik } from 'formik';
|
||||
import { MarketingFormValues, MarketingSchema } from './SalesForm.schema';
|
||||
import { SalesOrderFormValues, SalesOrderSchema } from './SalesForm.schema';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
import { MarketingApi } from '@/services/api/marketing/marketing';
|
||||
import { MarketingProductFormValues } from './repeater/MarketingProduct.schema';
|
||||
import { SalesOrderProductFormValues } from './repeater/MarketingProduct.schema';
|
||||
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
||||
import toast from 'react-hot-toast';
|
||||
import { useRouter } from 'next/navigation';
|
||||
@@ -37,9 +37,11 @@ import { useRouter } from 'next/navigation';
|
||||
const SalesForm = ({
|
||||
formType = 'add',
|
||||
initialValues,
|
||||
afterSubmit,
|
||||
}: {
|
||||
formType?: 'add' | 'edit';
|
||||
initialValues?: Marketing;
|
||||
afterSubmit?: () => void;
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const addProductModal = useModal();
|
||||
@@ -61,11 +63,9 @@ const SalesForm = ({
|
||||
parseInt(item)
|
||||
);
|
||||
const [grandTotal, setGrandTotal] = useState<number>(
|
||||
initialValues?.grand_total ?? 0
|
||||
);
|
||||
const marketingProducts = useMemo(
|
||||
() => rawMarketingProducts,
|
||||
[rawMarketingProducts]
|
||||
initialValues?.marketing_products
|
||||
?.map((item) => item.total_price)
|
||||
.reduce((a, b) => a + b, 0) ?? 0
|
||||
);
|
||||
|
||||
const {
|
||||
@@ -91,8 +91,8 @@ const SalesForm = ({
|
||||
|
||||
const handleAddSubmitProduct = useCallback(
|
||||
async (
|
||||
tableValue: CreateMarketingProductPayload,
|
||||
fieldValues: MarketingProductFormValues
|
||||
tableValue: CreateSalesOrderProductPayload,
|
||||
fieldValues: SalesOrderProductFormValues
|
||||
) => {
|
||||
const newMarketingProduct: MarketingProduct = {
|
||||
id: rawMarketingProducts.length + 1,
|
||||
@@ -102,10 +102,10 @@ const SalesForm = ({
|
||||
qty: tableValue.qty as number,
|
||||
avg_weight: tableValue.avg_weight as number,
|
||||
total_price: tableValue.total_price as number,
|
||||
marketing_delivery_products: {
|
||||
delivery_product: {
|
||||
id: rawMarketingProducts.length + 1,
|
||||
vehicle_number: tableValue.vehicle_number as string,
|
||||
delivery_date: tableValue.delivery_date as string,
|
||||
delivery_date: '' as string,
|
||||
unit_price: tableValue.unit_price as number,
|
||||
total_weight: tableValue.total_weight as number,
|
||||
qty: tableValue.qty as number,
|
||||
@@ -133,32 +133,30 @@ const SalesForm = ({
|
||||
[selectedCustomer, setSelectedCustomer]
|
||||
);
|
||||
|
||||
const createMarketingHandler = async (values: CreateMarketingPayload) => {
|
||||
const createMarketingHandler = async (values: CreateSalesOrderPayload) => {
|
||||
console.log(values);
|
||||
const createMarketingRes = await MarketingApi.create(values);
|
||||
if (isResponseSuccess(createMarketingRes)) {
|
||||
console.log(createMarketingRes);
|
||||
toast.success(createMarketingRes?.message as string);
|
||||
router.push('/marketing/sales-orders');
|
||||
}
|
||||
if (isResponseError(createMarketingRes)) {
|
||||
console.log(createMarketingRes);
|
||||
toast.error(createMarketingRes?.message as string);
|
||||
}
|
||||
toast.success('Successfully created Sales Order!');
|
||||
router.push('/marketing/sales-orders');
|
||||
};
|
||||
const updateMarketingHandler = async (values: CreateMarketingPayload) => {
|
||||
const updateMarketingHandler = async (values: CreateSalesOrderPayload) => {
|
||||
console.log(values);
|
||||
const createMarketingRes = await MarketingApi.update(
|
||||
const updateMarketingRes = await MarketingApi.update(
|
||||
initialValues?.id as number,
|
||||
values
|
||||
);
|
||||
if (isResponseSuccess(createMarketingRes)) {
|
||||
console.log(createMarketingRes);
|
||||
if (isResponseSuccess(updateMarketingRes)) {
|
||||
toast.success(updateMarketingRes?.message as string);
|
||||
router.push('/marketing/sales-orders');
|
||||
}
|
||||
if (isResponseError(createMarketingRes)) {
|
||||
console.log(createMarketingRes);
|
||||
if (isResponseError(updateMarketingRes)) {
|
||||
toast.error(updateMarketingRes?.message as string);
|
||||
}
|
||||
toast.success('Successfully updated Sales Order!');
|
||||
router.push('/marketing/sales-orders');
|
||||
};
|
||||
const deleteMarketingHandler = async () => {
|
||||
setIsLoading(true);
|
||||
@@ -180,9 +178,9 @@ const SalesForm = ({
|
||||
|
||||
const MarketingProductToFieldValues = (
|
||||
product: MarketingProduct
|
||||
): MarketingProductFormValues => {
|
||||
): SalesOrderProductFormValues => {
|
||||
return {
|
||||
vehicle_number: product.marketing_delivery_products?.vehicle_number,
|
||||
vehicle_number: product.delivery_product?.vehicle_number,
|
||||
kandang_id: product.product_warehouse.warehouse.id,
|
||||
kandang: {
|
||||
value: product.product_warehouse.warehouse.id,
|
||||
@@ -196,19 +194,17 @@ const SalesForm = ({
|
||||
unit_price: product.unit_price,
|
||||
total_weight: product.total_weight,
|
||||
qty: product.qty,
|
||||
uom: product.product_warehouse?.product?.uom?.name,
|
||||
avg_weight: product.avg_weight,
|
||||
total_price: product.total_price,
|
||||
delivery_date: product.marketing_delivery_products?.delivery_date,
|
||||
};
|
||||
};
|
||||
|
||||
const formik = useFormik<MarketingFormValues>({
|
||||
enableReinitialize: true,
|
||||
initialValues: {
|
||||
const formikInitialValues = useMemo(() => {
|
||||
return {
|
||||
so_date: initialValues?.so_date || undefined,
|
||||
notes: initialValues?.notes || undefined,
|
||||
customer_id: initialValues?.customer?.id || undefined,
|
||||
sales_person_id: initialValues?.sales_person_id || 1,
|
||||
customer: {
|
||||
value: initialValues?.customer?.id as number,
|
||||
label: initialValues?.customer?.name as string,
|
||||
@@ -217,15 +213,33 @@ const SalesForm = ({
|
||||
initialValues?.marketing_products?.map((product) =>
|
||||
MarketingProductToFieldValues(product)
|
||||
) ?? [],
|
||||
},
|
||||
validationSchema: MarketingSchema,
|
||||
};
|
||||
}, [initialValues]);
|
||||
|
||||
const formik = useFormik<SalesOrderFormValues>({
|
||||
enableReinitialize: true,
|
||||
initialValues: formikInitialValues,
|
||||
validationSchema: SalesOrderSchema,
|
||||
validateOnMount: true,
|
||||
onSubmit: async (values) => {
|
||||
const payload = {
|
||||
customer_id: values.customer_id as number,
|
||||
date: values.so_date as string,
|
||||
sales_person_id: values.sales_person_id as number,
|
||||
date: formatDate(values.so_date as string, 'yyyy-MM-DD'),
|
||||
notes: values.notes as string,
|
||||
marketing_products: values.marketing_products,
|
||||
} as CreateMarketingPayload;
|
||||
marketing_products: values.marketing_products.map((product) => {
|
||||
return {
|
||||
vehicle_number: product.vehicle_number as string,
|
||||
kandang_id: product.kandang_id as number,
|
||||
product_warehouse_id: product.product_warehouse_id as number,
|
||||
unit_price: parseFloat(product.unit_price as string),
|
||||
total_weight: parseFloat(product.total_weight as string),
|
||||
qty: parseFloat(product.qty as string),
|
||||
avg_weight: parseFloat(product.avg_weight as string),
|
||||
total_price: parseFloat(product.total_price as string),
|
||||
} as CreateSalesOrderProductPayload;
|
||||
}),
|
||||
} as CreateSalesOrderPayload;
|
||||
switch (formType) {
|
||||
case 'add':
|
||||
createMarketingHandler(payload);
|
||||
@@ -236,6 +250,7 @@ const SalesForm = ({
|
||||
default:
|
||||
break;
|
||||
}
|
||||
afterSubmit?.();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -245,6 +260,33 @@ const SalesForm = ({
|
||||
formikSetValues(formik.initialValues);
|
||||
}, [formikSetValues, formik.initialValues]);
|
||||
|
||||
useEffect(() => {
|
||||
// Konversi array MarketingProduct ke format SalesOrderProductFormValues
|
||||
const newMarketingProductValues = rawMarketingProducts.map((product) =>
|
||||
MarketingProductToFieldValues(product)
|
||||
);
|
||||
|
||||
// Hitung Grand Total baru
|
||||
const newGrandTotal = rawMarketingProducts.reduce(
|
||||
(total, product) => total + product.total_price,
|
||||
0
|
||||
);
|
||||
|
||||
// Perbarui nilai formik.values.marketing_products
|
||||
formik.setFieldValue(
|
||||
'marketing_products',
|
||||
newMarketingProductValues,
|
||||
false
|
||||
);
|
||||
// Parameter ketiga (false) untuk menghindari validasi secara langsung
|
||||
|
||||
// Perbarui state grandTotal
|
||||
setGrandTotal(newGrandTotal);
|
||||
|
||||
// Reset row selection setiap kali daftar produk berubah (opsional, tapi disarankan)
|
||||
setRowSelection({});
|
||||
}, [rawMarketingProducts]);
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
@@ -273,7 +315,7 @@ const SalesForm = ({
|
||||
},
|
||||
{
|
||||
accessorFn: (row: MarketingProduct) =>
|
||||
row.marketing_delivery_products?.vehicle_number,
|
||||
row.delivery_product?.vehicle_number,
|
||||
header: 'No. Polisi',
|
||||
},
|
||||
{
|
||||
@@ -321,7 +363,7 @@ const SalesForm = ({
|
||||
),
|
||||
},
|
||||
],
|
||||
[handleDeleteProduct] // dependensi tunggal
|
||||
[handleDeleteProduct, initialValues, rawMarketingProducts] // dependensi tunggal
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -372,10 +414,15 @@ const SalesForm = ({
|
||||
wrapper: 'bg-white w-full',
|
||||
}}
|
||||
>
|
||||
{JSON.stringify(formik.values.marketing_products)}
|
||||
<div className='text-green-500'>
|
||||
{JSON.stringify(formik.values.marketing_products)}
|
||||
</div>
|
||||
<span className='text-red-500'>{JSON.stringify(formik.errors)}</span>
|
||||
<Table<MarketingProduct>
|
||||
rowSelection={rowSelection}
|
||||
setRowSelection={setRowSelection}
|
||||
data={marketingProducts}
|
||||
data={rawMarketingProducts}
|
||||
columns={columns}
|
||||
className={{
|
||||
tableWrapperClassName: 'overflow-x-auto min-h-full!',
|
||||
|
||||
+8
-7
@@ -15,13 +15,13 @@ type MarketingProductSchemaType = {
|
||||
unit_price: string | number | undefined;
|
||||
total_weight: string | number | undefined;
|
||||
qty: string | number | undefined;
|
||||
uom: string | undefined | null;
|
||||
avg_weight: string | number | undefined;
|
||||
total_price: string | number | undefined;
|
||||
delivery_date?: string | undefined | null;
|
||||
};
|
||||
|
||||
export const MarketingProductSchema: Yup.ObjectSchema<MarketingProductSchemaType> =
|
||||
type SalesOrderProductSchemaType = MarketingProductSchemaType;
|
||||
|
||||
const MarketingProductSchema: Yup.ObjectSchema<MarketingProductSchemaType> =
|
||||
Yup.object({
|
||||
vehicle_number: Yup.string().required('No. Polisi wajib diisi!'),
|
||||
kandang: Yup.object({
|
||||
@@ -47,16 +47,17 @@ export const MarketingProductSchema: Yup.ObjectSchema<MarketingProductSchemaType
|
||||
qty: Yup.number()
|
||||
.min(1, 'Kuantitas wajib diisi!')
|
||||
.required('Kuantitas wajib diisi!'),
|
||||
uom: Yup.string().nullable(),
|
||||
avg_weight: Yup.number()
|
||||
.min(1, 'Avg. Bobot wajib diisi!')
|
||||
.required('Avg. Bobot wajib diisi!'),
|
||||
total_price: Yup.number()
|
||||
.min(1, 'Total Penjualan wajib diisi!')
|
||||
.required('Total Penjualan wajib diisi!'),
|
||||
delivery_date: Yup.string().required().nullable(),
|
||||
});
|
||||
|
||||
export type MarketingProductFormValues = Yup.InferType<
|
||||
typeof MarketingProductSchema
|
||||
export const SalesOrderProductSchema: Yup.ObjectSchema<SalesOrderProductSchemaType> =
|
||||
MarketingProductSchema;
|
||||
|
||||
export type SalesOrderProductFormValues = Yup.InferType<
|
||||
typeof SalesOrderProductSchema
|
||||
>;
|
||||
|
||||
+10
-21
@@ -1,17 +1,15 @@
|
||||
'use client';
|
||||
|
||||
import TextInput from '@/components/input/TextInput';
|
||||
import {
|
||||
CreateMarketingPayload,
|
||||
CreateMarketingProductPayload,
|
||||
CreateSalesOrderProductPayload,
|
||||
MarketingProduct,
|
||||
} from '@/types/api/marketing/marketing';
|
||||
import { useFormik } from 'formik';
|
||||
import {
|
||||
MarketingProductFormValues,
|
||||
MarketingProductSchema,
|
||||
SalesOrderProductFormValues,
|
||||
SalesOrderProductSchema,
|
||||
} from './MarketingProduct.schema';
|
||||
import { RefObject, use, useEffect, useRef, useState } from 'react';
|
||||
import { RefObject, useEffect, useState } from 'react';
|
||||
import SelectInput, {
|
||||
OptionType,
|
||||
useSelect,
|
||||
@@ -36,8 +34,8 @@ const MarketingProductForm = ({
|
||||
data: MarketingProduct[];
|
||||
modalRef?: RefObject<HTMLDialogElement | null>;
|
||||
onSubmitForm?: (
|
||||
tableValues: CreateMarketingProductPayload,
|
||||
fieldValues: MarketingProductFormValues
|
||||
tableValues: CreateSalesOrderProductPayload,
|
||||
fieldValues: SalesOrderProductFormValues
|
||||
) => Promise<void>;
|
||||
}) => {
|
||||
// State
|
||||
@@ -96,11 +94,11 @@ const MarketingProductForm = ({
|
||||
};
|
||||
|
||||
// Formik
|
||||
const formik = useFormik<MarketingProductFormValues>({
|
||||
const formik = useFormik<SalesOrderProductFormValues>({
|
||||
enableReinitialize: true,
|
||||
initialValues: {
|
||||
vehicle_number:
|
||||
initialValues?.marketing_delivery_products?.vehicle_number || undefined,
|
||||
initialValues?.delivery_product?.vehicle_number || undefined,
|
||||
kandang_id: initialValues?.product_warehouse.warehouse.id || undefined,
|
||||
kandang: {
|
||||
value: initialValues?.product_warehouse.warehouse.id as number,
|
||||
@@ -115,15 +113,10 @@ const MarketingProductForm = ({
|
||||
unit_price: initialValues?.unit_price || undefined,
|
||||
total_weight: initialValues?.total_weight || undefined,
|
||||
qty: initialValues?.qty || undefined,
|
||||
uom: initialValues?.product_warehouse?.product?.uom?.name || undefined,
|
||||
avg_weight: initialValues?.avg_weight || undefined,
|
||||
total_price: initialValues?.total_price || undefined,
|
||||
delivery_date:
|
||||
initialValues?.marketing_delivery_products?.delivery_date ||
|
||||
new Date().toDateString() ||
|
||||
undefined,
|
||||
},
|
||||
validationSchema: MarketingProductSchema,
|
||||
validationSchema: SalesOrderProductSchema,
|
||||
onSubmit: async (values) => {
|
||||
setFormErrorMessage('');
|
||||
if (
|
||||
@@ -137,7 +130,7 @@ const MarketingProductForm = ({
|
||||
(item: Kandang) => item.id === values.kandang_id
|
||||
);
|
||||
|
||||
const marketingProduct: CreateMarketingProductPayload = {
|
||||
const marketingProduct: CreateSalesOrderProductPayload = {
|
||||
id: initialValues?.id || undefined,
|
||||
vehicle_number: formatVechicleNumber(values.vehicle_number as string),
|
||||
kandang_id: values.kandang_id as number,
|
||||
@@ -147,10 +140,8 @@ const MarketingProductForm = ({
|
||||
unit_price: values.unit_price as number,
|
||||
total_weight: values.total_weight as number,
|
||||
qty: values.qty as number,
|
||||
uom: values.uom as string,
|
||||
avg_weight: values.avg_weight as number,
|
||||
total_price: values.total_price as number,
|
||||
delivery_date: values.delivery_date as string,
|
||||
};
|
||||
|
||||
onSubmitForm?.(marketingProduct, values);
|
||||
@@ -178,10 +169,8 @@ const MarketingProductForm = ({
|
||||
unit_price: '',
|
||||
total_weight: '',
|
||||
qty: '',
|
||||
uom: '',
|
||||
avg_weight: '',
|
||||
total_price: '',
|
||||
delivery_date: new Date().toDateString(),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user