diff --git a/src/components/pages/marketing/DeliveryOrderFormModal.tsx b/src/components/pages/marketing/DeliveryOrderFormModal.tsx index 76c46ebf..f1d5e3cc 100644 --- a/src/components/pages/marketing/DeliveryOrderFormModal.tsx +++ b/src/components/pages/marketing/DeliveryOrderFormModal.tsx @@ -111,6 +111,7 @@ const DeliveryOrderFormModal = ({ const successModal = useModal(); const rejectModal = useModal(); const deleteModal = useModal(); + const approveModal = useModal(); const formRef = useRef(null); const textareaRef = useRef(null); @@ -333,6 +334,33 @@ const DeliveryOrderFormModal = ({ refreshApproval(); }; + const approveMarketingHandler = async (notes: string) => { + if (!marketingId) { + toast.error(`Tidak ada data yang valid untuk di approve.`); + approveModal.closeModal(); + return; + } + + const approveMarketingRes = await SalesOrderApi.singleApproval( + Number(marketingId), + 'APPROVED', + notes + ); + + if (isResponseSuccess(approveMarketingRes)) { + approveModal.closeModal(); + toast.success(approveMarketingRes?.message as string); + closeModalHandler(); + router.push('/marketing'); + } + if (isResponseError(approveMarketingRes)) { + approveModal.closeModal(); + toast.error(approveMarketingRes?.message as string); + } + refreshMarketing(); + refreshApproval(); + }; + const deleteClickHandler = () => { deleteModal.openModal(); }; @@ -380,7 +408,77 @@ const DeliveryOrderFormModal = ({ }, [prevButtonHandler] ); - const handleUpdateDO = useCallback( + + const isApprovalStep3Approved = useMemo(() => { + return ( + isResponseSuccess(marketing) && + marketing.data.latest_approval?.step_number === 3 && + marketing.data.latest_approval?.action === 'APPROVED' + ); + }, [marketing]); + + const handleUpdateDOWithAPI = useCallback( + async (id: number, values: DeliveryOrderProductFormValues) => { + if (!marketingId) { + toast.error('Marketing ID tidak ditemukan'); + return; + } + + setIsLoading(true); + + const updatedDeliveryValues = deliveryOrderValues.map((product) => + product.id === id ? { ...product, ...values } : product + ); + + const payload = { + marketing_id: Number(marketingId), + delivery_products: updatedDeliveryValues + .map((product) => { + if (Boolean(product.delivery_date)) { + return { + marketing_product_id: product.marketing_product_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), + delivery_date: formatDate( + product.delivery_date as string, + 'yyyy-MM-DD' + ), + vehicle_number: product.vehicle_number, + }; + } + }) + .filter((item) => Boolean(item)), + } as UpdateDeliveryOrderPayload; + + const updateDeliveryRes = await DeliveryOrderApi.update( + Number(marketingId), + payload + ); + + if (isResponseSuccess(updateDeliveryRes)) { + toast.success(updateDeliveryRes?.message as string); + closeModalHandler(); + } + + if (isResponseError(updateDeliveryRes)) { + setFormErrorMessage(updateDeliveryRes?.message as string); + } + + setIsLoading(false); + }, + [ + marketingId, + deliveryOrderValues, + formik.values.sales_order, + prevButtonHandler, + refreshMarketing, + ] + ); + + const handleUpdateDOLocal = useCallback( async (id: number, values: DeliveryOrderProductFormValues) => { setDeliveryOrderValues((prev) => prev.map((product) => @@ -463,7 +561,26 @@ const DeliveryOrderFormModal = ({ ); formik.setValues(filledInitialValues); - setStep(1); + + if (modalAction === 'add_delivery') { + // add delivery + const firstDeliveryItem = filledInitialValues.delivery_order?.[0]; + if (firstDeliveryItem) { + setSelectedDeliveryProduct(firstDeliveryItem); + } + setStep(2); // Langsung ke form delivery + } else if (modalAction === 'edit_delivery') { + // edit delivery + const firstDeliveryItem = filledInitialValues.delivery_order?.[0]; + if (firstDeliveryItem) { + setSelectedDeliveryProduct(firstDeliveryItem); + setStep(2); // Langsung ke form edit + } else { + setStep(1); // Jika belum ada data, tampilkan detail view + } + } else { + setStep(1); // Detail view + } } if (isResponseError(marketing)) { @@ -474,7 +591,7 @@ const DeliveryOrderFormModal = ({ }; getFilledInitialValues(); - }, [marketingId, marketing]); + }, [marketingId, marketing, modalAction]); // Reset error message when step changes useEffect(() => { @@ -679,7 +796,12 @@ const DeliveryOrderFormModal = ({ exisitingValues={deliveryOrderValues} onSubmitForm={handleAddSubmitDO} initialValues={selectedDeliveryProduct ?? undefined} - onUpdateForm={handleUpdateDO} + onUpdateForm={ + isApprovalStep3Approved + ? handleUpdateDOWithAPI + : handleUpdateDOLocal + } + isLoading={isLoading} /> )} @@ -723,7 +845,15 @@ const DeliveryOrderFormModal = ({ type='button' color='primary' onClick={() => { - formRef.current?.requestSubmit(); + // Jika masih di step 1 approval, gunakan single approval API + if ( + marketing?.data?.latest_approval?.step_number === 1 + ) { + approveModal.openModal(); + } else { + // Jika sudah di step 2/3, gunakan form submit (delivery products) + formRef.current?.requestSubmit(); + } }} className='p-3 shadow-button-soft text-base-100 rounded-lg text-sm font-semibold' disabled={deliveryRejected} @@ -741,8 +871,8 @@ const DeliveryOrderFormModal = ({ ref={successModal.ref} iconPosition='left' type='success' - text={`${currentModalAction === 'add' ? 'Data Berhasil Disimpan' : 'Data Berhasil Diubah'}`} - subtitleText={`${currentModalAction === 'add' ? 'Data delivery order telah berhasil disimpan.' : 'Data delivery order telah berhasil diubah.'}`} + text={`${currentModalAction === 'add_delivery' ? 'Data Berhasil Disimpan' : 'Data Berhasil Diubah'}`} + subtitleText={`${currentModalAction === 'add_delivery' ? 'Data delivery order telah berhasil disimpan.' : 'Data delivery order telah berhasil diubah.'}`} primaryButton={{ text: 'Oke', color: 'primary', @@ -795,6 +925,21 @@ const DeliveryOrderFormModal = ({ onClick: confirmationModalDeleteClickHandler, }} /> + + ); }; diff --git a/src/components/pages/marketing/SalesOrderFormModal.tsx b/src/components/pages/marketing/SalesOrderFormModal.tsx index 7a15ea59..3d62c516 100644 --- a/src/components/pages/marketing/SalesOrderFormModal.tsx +++ b/src/components/pages/marketing/SalesOrderFormModal.tsx @@ -394,7 +394,7 @@ const SalesOrderFormModal = ({ } formik.setFieldValue('sales_order', updatedProducts); - console.log(formik.values); + setSelectedMarketingProduct(null); nextButtonHandler(); }, [memoSalesOrder, nextButtonHandler] @@ -418,6 +418,15 @@ const SalesOrderFormModal = ({ if (modalAction === 'add' || modalAction === 'edit') { setCurrentModalAction(modalAction); formModal.openModal(); + + if (modalAction === 'add') { + formik.resetForm(); + setStep(1); + setSelectedMarketingProduct(null); + setSelectedDeliveryProduct(null); + setFormErrorMessage(''); + setFormErrorList([]); + } } }, [modalAction]); diff --git a/src/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct.tsx b/src/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct.tsx index d7b97d8e..f233521c 100644 --- a/src/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct.tsx +++ b/src/components/pages/marketing/form/repeater/delivery-order/DeliverOrderProduct.tsx @@ -36,6 +36,7 @@ const DeliveryOrderProductForm = ({ exisitingValues, onSubmitForm, onUpdateForm, + isLoading, }: { formState: 'add' | 'edit'; salesOrders: BaseSalesOrder[]; @@ -46,6 +47,7 @@ const DeliveryOrderProductForm = ({ id: number, value: DeliveryOrderProductFormValues ) => Promise; + isLoading?: boolean; }) => { const [formikErrorMessage, setFormErrorMessage] = useState(''); const [selectedProduct, setSelectedProduct] = useState( @@ -178,6 +180,25 @@ const DeliveryOrderProductForm = ({ }, }); + const hasWeekField = useMemo(() => { + const marketingType = formik.values.marketing_type?.value?.toLowerCase(); + if (marketingType === 'ayam_pullet') { + return true; + } + + if (formik.values.marketing_product?.product_warehouse_data) { + return Boolean( + formik.values.marketing_product?.product_warehouse_data?.week !== + undefined && + formik.values.marketing_product?.product_warehouse_data?.week !== + null && + formik.values.marketing_product?.product_warehouse_data?.week > 0 + ); + } + + return false; + }, [formik.values.marketing_product, formik.values.marketing_type]); + const handleResetForm = () => { setFormErrorMessage(''); formik.resetForm({ @@ -362,20 +383,24 @@ const DeliveryOrderProductForm = ({ avg_weight: '', total_weight: '', vehicle_number: '', + week: null, }); return; } + const soFieldValues = SalesProductToFieldValues(so); + formik.setValues({ ...formik.values, marketing_product_id: selected.value as number, - marketing_product: SalesProductToFieldValues(so), + marketing_product: soFieldValues, qty: so.qty, unit_price: so.unit_price, total_price: so.total_price, avg_weight: so.avg_weight, total_weight: so.total_weight, vehicle_number: so.vehicle_number, + week: soFieldValues.week ?? null, }); }} startAdornment={ @@ -509,10 +534,14 @@ const DeliveryOrderProductForm = ({ )} {/* Konversi Satuan Week Pullet */} - {formik.values.marketing_type?.value.toLowerCase() === - 'ayam_pullet' && ( + {(formik.values.marketing_type?.value.toLowerCase() === + 'ayam_pullet' || + hasWeekField) && ( + + )} + + + + <> + + Gudang + + {doItem?.warehouse?.name || + item.marketing_product?.product_warehouse_data?.warehouse?.name} + + + + Produk + + {item.marketing_product?.product_warehouse?.label} + + + + Qty + + {item.qty + ? `${formatNumber(parseFloat(item.qty as string))} ${item.marketing_product?.uom ?? ''}` + : '-'} + + + {Number(item.avg_weight ?? 0) > 0 && ( + + Avg Bobot + + {formatNumber(Number(item.avg_weight))} Kg + + + )} + {Number(item.total_weight ?? 0) > 0 && ( + + Total Bobot + + {formatNumber(Number(item.total_weight))} + + + )} + + Total Harga Satuan + + {formatCurrency(parseFloat(item.unit_price as string))} + + + + Total Penjualan + + {formatCurrency(parseFloat(item.total_price as string))} + + + + + + Label + + +
+
Value
+
+ + + <> + {approvalStepNumber !== 1 && ( + + Tanggal Pengiriman + + {item.delivery_date ? ( + formatDate(item.delivery_date, 'DD MMM YYYY') + ) : formType === 'add_delivery' || + formType === 'edit_delivery' || + formType === 'detail' ? ( + { + onEditRef.current(item.id as number, item); + }} + > + Belum diisi + + ) : ( + Belum diisi + )} + + + )} + {item.do_number && ( + + No. Pengiriman + {item.do_number} + + )} + + No. Polisi + {item.vehicle_number} + + {doItem && ( + + Dokumen Pengiriman + + + + + )} + + + ); + }; + return ( <>
- {data.map((item) => { - const doItem = marketing?.delivery_order?.find( - (doItem) => doItem.do_number === item.do_number - ); - return ( - - ( +
+ {formType === 'success' ? ( +
+
+ {renderTableContent(item)} +
+
+ ) : ( + - - - - Label - - -
-
Value
- {(formType === 'add_delivery' || - formType === 'edit_delivery' || - formType === 'detail') && ( -
- - -
- )} -
- - - <> - {approvalStepNumber !== 1 && ( - - - Tanggal Pengiriman - - - {item.delivery_date ? ( - formatDate(item.delivery_date, 'DD MMM YYYY') - ) : ( - Belum diisi - )} - - - )} - {item.do_number && ( - - No. Pengiriman - {item.do_number} - - )} - - No. Polisi - - {item.vehicle_number} - - - - Gudang - - {doItem?.warehouse?.name || - item.marketing_product?.product_warehouse_data - ?.warehouse?.name} - - - - Produk - - {item.marketing_product?.product_warehouse?.label} - - - - Qty - - {item.qty - ? `${formatNumber(parseFloat(item.qty as string))} ${item.marketing_product?.uom ?? ''}` - : '-'} - - - - Avg Bobot - - {item.avg_weight - ? formatNumber( - parseFloat(item.avg_weight as string) - ) + ' Kg' - : '-'} - - - - Total Bobot - - {formatNumber(parseFloat(item.total_weight as string))} - - - - Total Harga Satuan - - {formatCurrency(parseFloat(item.unit_price as string))} - - - - Total Penjualan - - {formatCurrency(parseFloat(item.total_price as string))} - - - {doItem && ( - - - Dokumen Pengiriman - - - - - - )} - - - -
- ); - })} + + {renderTableContent(item)} +
+ + )} + + ))} ); diff --git a/src/components/pages/marketing/form/table-view/SalesOrderProductTable.tsx b/src/components/pages/marketing/form/table-view/SalesOrderProductTable.tsx index a13a1117..18f6145b 100644 --- a/src/components/pages/marketing/form/table-view/SalesOrderProductTable.tsx +++ b/src/components/pages/marketing/form/table-view/SalesOrderProductTable.tsx @@ -3,15 +3,9 @@ import Button from '@/components/Button'; import Card from '@/components/Card'; import { SalesOrderProductFormValues } from '@/components/pages/marketing/form/repeater/sales-order/SalesOrderProduct.schema'; -import { - formatCurrency, - formatNumber, - formatVechicleNumber, -} from '@/lib/helper'; +import { formatCurrency, formatNumber } from '@/lib/helper'; import { Icon } from '@iconify/react'; -import { useMemo, useRef } from 'react'; -import * as TanStack from '@tanstack/react-table'; -import CheckboxInput from '@/components/input/CheckboxInput'; +import { useRef } from 'react'; type SalesOrderProductTableProps = { data: SalesOrderProductFormValues[]; @@ -33,278 +27,168 @@ const SalesOrderProductTable = ({ const onEditRef = useRef(onEdit); onEditRef.current = onEdit; - const columns = useMemo( - () => [ - { - id: 'select', - header: ({ - table, - }: { - table: TanStack.Table; - }) => ( -
- + const renderTableContent = (item: SalesOrderProductFormValues) => ( + <> + + + Label + + +
+
Value
+ {formType !== 'success' && ( +
+ + +
+ )}
- ), - cell: ({ row }: { row: TanStack.Row }) => ( -
- -
- ), - }, - { - accessorFn: (row: SalesOrderProductFormValues) => - formatVechicleNumber(row.vehicle_number as string), - header: 'No. Polisi', - }, - { - accessorFn: (row: SalesOrderProductFormValues) => row.kandang?.label, - header: 'Kandang', - }, - { - accessorFn: (row: SalesOrderProductFormValues) => - row.product_warehouse?.label, - header: 'Produk', - }, - { - accessorFn: (row: SalesOrderProductFormValues) => - formatCurrency(parseFloat(row.unit_price as string)), - header: 'Harga Satuan (Rp)', - }, - { - accessorFn: (row: SalesOrderProductFormValues) => - formatNumber(parseFloat(row.total_weight as string), undefined, 0, 5), - header: 'Total Bobot (Kg)', - }, - { - accessorFn: (row: SalesOrderProductFormValues) => - formatNumber(parseFloat(row.qty as string)), - header: 'Kuantitas', - cell: ({ row }: { row: TanStack.Row }) => - formatNumber( - parseFloat(row.original.qty as string), - undefined, - 0, - 5 - ) + - ' ' + - (row.original.uom ?? ''), - }, - { - accessorFn: (row: SalesOrderProductFormValues) => - formatNumber(parseFloat(row.avg_weight as string), undefined, 0, 5), - header: 'Avg. Bobot (Kg)', - }, - { - accessorFn: (row: SalesOrderProductFormValues) => - formatCurrency(parseFloat(row.total_price as string)), - header: 'Total Penjualan (Rp)', - }, - { - header: 'Aksi', - cell: ( - props: TanStack.CellContext - ) => ( -
- - -
- ), - }, - ], - [] + + + <> + + No. Polisi + {item.vehicle_number} + + + Gudang + {item.kandang?.label} + + + Kategori + {item.marketing_type?.label} + + + Produk + {item.product_warehouse?.label} + + {item.marketing_type?.value.toLowerCase() === 'telur' && ( + + Tipe Konversi + {item.convertion_unit?.label} + + )} + {item.marketing_type?.value.toLowerCase() === 'ayam_pullet' && ( + + Tipe Konversi + Week {item.week} + + )} + {item.convertion_unit?.value.toLowerCase() === 'peti' && ( + + Total Peti + + {item.total_peti} {item.convertion_unit?.label} + + + )} + {item.marketing_type?.value.toLowerCase() !== 'trading' && ( + <> + + Total Bobot + + {item.total_weight + ? formatNumber(parseFloat(item.total_weight as string)) + + ' Kg' + : '0 Kg'} + + + + Avg Bobot + + {item.avg_weight + ? formatNumber(parseFloat(item.avg_weight as string)) + ' Kg' + : '0 Kg'} + + + + )} + + + {item.marketing_type?.value === 'telur' + ? 'Total Butir Telur' + : 'Qty'} + + + {`${formatNumber(parseFloat(item.qty as string))} ${item.uom || ''}`} + + + + Harga Satuan + + {formatCurrency(parseFloat(item.unit_price as string))} + + + + Total Penjualan + + {formatCurrency(parseFloat(item.total_price as string))} + + + + ); return ( <>
{data.map((item) => ( - - - - - - - - <> - - - - - - - - - - - - - - - - - {item.marketing_type?.value.toLowerCase() === 'telur' && ( - - - - - )} - {item.marketing_type?.value.toLowerCase() === - 'ayam_pullet' && ( - - - - - )} - {item.convertion_unit?.value.toLowerCase() === 'peti' && ( - - - - - )} - {item.marketing_type?.value.toLowerCase() !== 'trading' && ( - <> - - - - - - - - - - )} - - - - - - - - - - - - - - -
- Label - -
-
Value
- {formType !== 'success' && ( -
- - -
- )} -
-
No. Polisi{item.vehicle_number}
Gudang{item.kandang?.label}
Kategori - {item.marketing_type?.label} -
Produk - {item.product_warehouse?.label} -
Tipe Konversi - {item.convertion_unit?.label} -
Tipe KonversiWeek {item.week}
Total Peti - {item.total_peti} {item.convertion_unit?.label} -
Total Bobot - {item.total_weight - ? formatNumber( - parseFloat(item.total_weight as string) - ) + ' Kg' - : '0 Kg'} -
Avg Bobot - {item.avg_weight - ? formatNumber( - parseFloat(item.avg_weight as string) - ) + ' Kg' - : '0 Kg'} -
- {item.marketing_type?.value === 'telur' - ? 'Total Butir Telur' - : 'Qty'} - - {`${formatNumber(parseFloat(item.qty as string))} ${item.uom || ''}`} -
Harga Satuan - {formatCurrency(parseFloat(item.unit_price as string))} -
Total Penjualan - {formatCurrency(parseFloat(item.total_price as string))} -
-
+
+ {formType === 'success' ? ( +
+ + {renderTableContent(item)} +
+
+ ) : ( + + + {renderTableContent(item)} +
+
+ )} +
))} {formType != 'add_deliver' && formType != 'edit_deliver' && diff --git a/src/config/constant.ts b/src/config/constant.ts index 170a27bf..fc89763b 100644 --- a/src/config/constant.ts +++ b/src/config/constant.ts @@ -512,13 +512,9 @@ export const FILTER_TYPE_OPTIONS = [ ]; export const MARKETING_TYPE_OPTIONS = [ - { - label: 'Ayam Pullet', - value: 'AYAM_PULLET', - }, { label: 'Ayam', - value: 'AYAM', + value: 'AYAM,AYAM_PULLET', }, { label: 'Trading', diff --git a/src/types/api/inventory/product-warehouse.d.ts b/src/types/api/inventory/product-warehouse.d.ts index 8bed1aba..060be2ab 100644 --- a/src/types/api/inventory/product-warehouse.d.ts +++ b/src/types/api/inventory/product-warehouse.d.ts @@ -11,6 +11,7 @@ export type BaseProductWarehouse = { quantity: number; product: Product; warehouse: Warehouse; + week?: number | null; }; export type ProductWarehouse = BaseMetadata & BaseProductWarehouse;