diff --git a/src/components/pages/marketing/MarketingTable.tsx b/src/components/pages/marketing/MarketingTable.tsx index 911c9e9a..15ea81d4 100644 --- a/src/components/pages/marketing/MarketingTable.tsx +++ b/src/components/pages/marketing/MarketingTable.tsx @@ -2,6 +2,8 @@ import Button from '@/components/Button'; import CheckboxInput from '@/components/input/CheckboxInput'; +import DateInput from '@/components/input/DateInput'; +import TextArea from '@/components/input/TextArea'; import Modal, { useModal } from '@/components/Modal'; import ConfirmationModal from '@/components/modal/ConfirmationModal'; import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes'; @@ -13,6 +15,7 @@ import { SalesOrderApi, } from '@/services/api/marketing/marketing'; import { useTableFilter } from '@/services/hooks/useTableFilter'; +import { BaseApiResponse } from '@/types/api/api-general'; import { BaseSalesOrder, Marketing, @@ -21,7 +24,7 @@ import { import { Icon } from '@iconify/react'; import { CellContext, ColumnDef, Row } from '@tanstack/react-table'; import { useRouter } from 'next/navigation'; -import { useMemo, useState } from 'react'; +import { ChangeEventHandler, useCallback, useMemo, useState } from 'react'; import toast from 'react-hot-toast'; import useSWR from 'swr'; import RequirePermission from '@/components/helper/RequirePermission'; @@ -154,12 +157,17 @@ const MarketingTable = () => { ); const [selectedItem, setSelectedItem] = useState(null); const [rowSelection, setRowSelection] = useState>({}); + const [bulkDeliveryDate, setBulkDeliveryDate] = useState(''); + const [bulkDeliveryNotes, setBulkDeliveryNotes] = useState(''); + const [isSubmittingBulkDelivery, setIsSubmittingBulkDelivery] = + useState(false); const router = useRouter(); const deleteModal = useModal(); const confirmationModal = useModal(); const productsModal = useModal(); const deliveryModal = useModal(); + const bulkDeliveryModal = useModal(); const filterModal = useModal(); const { @@ -182,6 +190,9 @@ const MarketingTable = () => { status: 'status', customer_id: 'customer_id', }, + + persist: true, + storeName: 'marketing-table', }); // ===== FETCH DATA ===== @@ -198,12 +209,14 @@ const MarketingTable = () => { const filterSubmitHandler = (values: MarketingFilter) => { updateFilter( 'product_ids', - values.product_ids?.map((item) => item.toString()).join(',') + values.product_ids?.map((item) => item.toString()).join(','), + true ); - updateFilter('status', values.status ? values.status.toString() : ''); + updateFilter('status', values.status ? values.status.toString() : '', true); updateFilter( 'customer_id', - values.customer_id ? values.customer_id.toString() : '' + values.customer_id ? values.customer_id.toString() : '', + true ); }; @@ -211,13 +224,19 @@ const MarketingTable = () => { useState(false); const filterResetHandler = () => { - updateFilter('product_ids', ''); - updateFilter('status', ''); - updateFilter('customer_id', ''); + updateFilter('product_ids', '', true); + updateFilter('status', '', true); + updateFilter('customer_id', '', true); }; const approveClickHandler = () => { setApproveAction('APPROVED'); + + if (selectedApprovalStep === 2) { + bulkDeliveryModal.openModal(); + return; + } + confirmationModal.openModal(); }; @@ -226,10 +245,13 @@ const MarketingTable = () => { confirmationModal.openModal(); }; - const productsClickHandler = (item: Marketing) => { - setSelectedItem(item); - productsModal.openModal(); - }; + const productsClickHandler = useCallback( + (item: Marketing) => { + setSelectedItem(item); + productsModal.openModal(); + }, + [productsModal] + ); const deleteMarketingHandler = async () => { const deleteMarketingRes = await MarketingApi.delete( @@ -251,61 +273,135 @@ const MarketingTable = () => { const selectedRowsData = allData.filter( (row) => rowSelection[row.id.toString()] ); + const selectedApprovalStep = + selectedRowsData.length > 0 + ? selectedRowsData[0].latest_approval.step_number + : null; - const hasApprovable = selectedRowsData.some( - (row) => - row.latest_approval.step_number === 1 && - row.latest_approval.action !== 'REJECTED' - ); - const hasRejectable = selectedRowsData.some( - (row) => - row.latest_approval.step_number === 1 && - row.latest_approval.action !== 'REJECTED' - ); + const eligibleSelectedRows = selectedRowsData.filter((row) => { + const approval = row.latest_approval; + + if (approval.action === 'REJECTED') { + return false; + } + + if (selectedApprovalStep === null) { + return approval.step_number === 1 || approval.step_number === 2; + } + + return approval.step_number === selectedApprovalStep; + }); + + const hasApprovable = eligibleSelectedRows.length > 0; + const hasRejectable = eligibleSelectedRows.length > 0; const disableApprove = !hasApprovable; const disableReject = !hasRejectable; - const idsToProcess = - approveAction === 'APPROVED' - ? selectedRowsData - .filter((row) => row.latest_approval.step_number === 1) - .map((row) => row.id) - : selectedRowsData - .filter((row) => row.latest_approval.step_number === 2) - .map((row) => row.id); + const idsToProcess = eligibleSelectedRows.map((row) => row.id); + const nextApprovalStatus = + selectedApprovalStep === 1 + ? 'SALES_ORDER' + : selectedApprovalStep === 2 + ? 'DELIVERY_ORDER' + : null; const approveMarketingHandler = async (notes: string) => { - let idsToProcess: number[] = []; - - idsToProcess = selectedRowsData - .filter((row) => row.latest_approval.step_number === 1) - .map((row) => row.id); - if (idsToProcess.length === 0) { toast.error(`Tidak ada data yang valid untuk di ${approveAction}.`); confirmationModal.closeModal(); return; } - const approveMarketingRes = await SalesOrderApi.bulkApprovals( - idsToProcess, - approveAction, - notes - ); + if (approveAction === 'APPROVED' && selectedApprovalStep !== 1) { + toast.error('Approve tahap ini harus menggunakan tanggal pengiriman.'); + confirmationModal.closeModal(); + return; + } + + if (approveAction === 'APPROVED' && !nextApprovalStatus) { + toast.error('Status approval berikutnya tidak valid.'); + confirmationModal.closeModal(); + return; + } + + const approveMarketingRes: BaseApiResponse | undefined = + approveAction === 'APPROVED' + ? await MarketingApi.bulkApprovals( + idsToProcess, + nextApprovalStatus as 'SALES_ORDER' | 'DELIVERY_ORDER', + '', + notes || `APPROVED marketing ${idsToProcess.join(', ')}` + ) + : await SalesOrderApi.bulkApprovals(idsToProcess, approveAction, notes); if (isResponseSuccess(approveMarketingRes)) { confirmationModal.closeModal(); toast.success(approveMarketingRes?.message as string); setRowSelection({}); } - if (isResponseError(approveMarketingRes)) { - confirmationModal.closeModal(); - toast.error(approveMarketingRes?.message as string); - } + refreshMarketing(); }; + const bulkDeliveryDateChangeHandler: ChangeEventHandler = ( + e + ) => { + setBulkDeliveryDate(e.target.value); + }; + + const bulkDeliveryNotesChangeHandler: ChangeEventHandler< + HTMLTextAreaElement + > = (e) => { + setBulkDeliveryNotes(e.target.value); + }; + + const submitBulkDeliveryApprovalHandler = async ( + selectedIds: number[], + deliveryDate: string, + notes: string + ) => { + if (selectedIds.length === 0) { + toast.error('Tidak ada data yang valid untuk diproses.'); + return; + } + + if (!deliveryDate) { + toast.error('Tanggal pengiriman wajib diisi.'); + return; + } + + setIsSubmittingBulkDelivery(true); + + try { + const bulkDeliveryApprovalRes = await MarketingApi.bulkApprovals( + selectedIds, + 'DELIVERY_ORDER', + deliveryDate, + notes || `APPROVED delivery marketing ${selectedIds.join(', ')}` + ); + + if (isResponseError(bulkDeliveryApprovalRes)) { + toast.error(bulkDeliveryApprovalRes?.message as string); + return; + } + + if (!isResponseSuccess(bulkDeliveryApprovalRes)) { + toast.error('Gagal memproses bulk approve delivery.'); + return; + } + + toast.success(bulkDeliveryApprovalRes?.message as string); + bulkDeliveryModal.closeModal(); + setBulkDeliveryDate(''); + setBulkDeliveryNotes(''); + setRowSelection({}); + refreshMarketing(); + } finally { + setIsSubmittingBulkDelivery(false); + } + }; + const confirmationModalDeliveryClickHandler = async (notes: string) => { const res = await SalesOrderApi.delivery(selectedItem?.id as number, notes); deliveryModal.closeModal(); @@ -316,10 +412,24 @@ const MarketingTable = () => { ); }; - const getRowCanSelect = (row: Row): boolean => { - const approval = row.original.latest_approval; - return approval?.step_number === 1 && approval?.action !== 'REJECTED'; - }; + const getRowCanSelect = useCallback( + (row: Row): boolean => { + const approval = row.original.latest_approval; + const isSelectableStep = + approval?.step_number === 1 || approval?.step_number === 2; + + if (!isSelectableStep || approval?.action === 'REJECTED') { + return false; + } + + if (selectedApprovalStep === null) { + return true; + } + + return approval?.step_number === selectedApprovalStep; + }, + [selectedApprovalStep] + ); const exportToExcelHandler = async () => { setIsLoadingExportingToExcel(true); @@ -336,7 +446,22 @@ const MarketingTable = () => { size: 1, header: ({ table }) => { const allRows = table.getRowModel().rows; - const selectableRows = allRows.filter(getRowCanSelect); + const stepForBulkSelection = + selectedApprovalStep ?? + allRows.find(getRowCanSelect)?.original.latest_approval.step_number; + const selectableRows = allRows.filter((row) => { + if (!getRowCanSelect(row)) { + return false; + } + + if (!stepForBulkSelection) { + return false; + } + + return ( + row.original.latest_approval.step_number === stepForBulkSelection + ); + }); const allSelected = selectableRows.length > 0 && @@ -504,7 +629,13 @@ const MarketingTable = () => { }, }, ]; - }, []); + }, [ + deleteModal, + deliveryModal, + getRowCanSelect, + productsClickHandler, + selectedApprovalStep, + ]); return ( <> @@ -677,7 +808,7 @@ const MarketingTable = () => { { }} /> + +
+
+

+ Bulk Approve Delivery +

+ +
+ +
+

+ Pilih tanggal pengiriman untuk approve {idsToProcess.length} data + penjualan tahap 2. +

+ + + +