mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-26 08:15:44 +00:00
feat(FE-195): implement bulk approve/reject in Expense list page
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { ChangeEventHandler, useEffect, useState } from 'react';
|
import { ChangeEventHandler, useEffect, useMemo, useState } from 'react';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import {
|
import {
|
||||||
CellContext,
|
CellContext,
|
||||||
@@ -31,13 +31,14 @@ import DateInput from '@/components/input/DateInput';
|
|||||||
|
|
||||||
import { Expense } from '@/types/api/expense';
|
import { Expense } from '@/types/api/expense';
|
||||||
import { ExpenseApi } from '@/services/api/expense';
|
import { ExpenseApi } from '@/services/api/expense';
|
||||||
import { cn, formatCurrency } from '@/lib/helper';
|
import { cn, formatCurrency, formatDate } from '@/lib/helper';
|
||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseSuccess } from '@/lib/api-helper';
|
||||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||||
import { ROWS_OPTIONS } from '@/config/constant';
|
import { ROWS_OPTIONS } from '@/config/constant';
|
||||||
import { LocationApi, SupplierApi } from '@/services/api/master-data';
|
import { LocationApi, SupplierApi } from '@/services/api/master-data';
|
||||||
import { Location } from '@/types/api/master-data/location';
|
import { Location } from '@/types/api/master-data/location';
|
||||||
import { Supplier } from '@/types/api/master-data/supplier';
|
import { Supplier } from '@/types/api/master-data/supplier';
|
||||||
|
import { BaseApiResponse } from '@/types/api/api-general';
|
||||||
|
|
||||||
const RowOptionsMenu = ({
|
const RowOptionsMenu = ({
|
||||||
type = 'dropdown',
|
type = 'dropdown',
|
||||||
@@ -53,18 +54,19 @@ const RowOptionsMenu = ({
|
|||||||
deleteClickHandler: () => void;
|
deleteClickHandler: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const showEditButton =
|
const showEditButton =
|
||||||
props.row.original.approval.action !== 'REJECTED' &&
|
props.row.original.latest_approval.step_number !== 5 &&
|
||||||
props.row.original.approval.step_number !== 5 &&
|
(props.row.original.latest_approval.step_number === 1 ||
|
||||||
props.row.original.approval.action !== 'APPROVED';
|
props.row.original.latest_approval.step_number === 2 ||
|
||||||
|
props.row.original.latest_approval.step_number === 3);
|
||||||
const showDeleteButton = showEditButton;
|
|
||||||
|
|
||||||
// TODO: apply RBAC
|
// TODO: apply RBAC
|
||||||
const showApproveButton = showEditButton;
|
const showRealizationButton =
|
||||||
const showRejectButton = showEditButton;
|
props.row.original.latest_approval.action !== 'REJECTED' &&
|
||||||
|
props.row.original.latest_approval.step_number === 3;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RowOptionsMenuWrapper type={type}>
|
<RowOptionsMenuWrapper type={type}>
|
||||||
|
<div className='w-full max-h-40 overflow-auto flex flex-col gap-1'>
|
||||||
<Button
|
<Button
|
||||||
href={`/expense/detail/?expenseId=${props.row.original.id}`}
|
href={`/expense/detail/?expenseId=${props.row.original.id}`}
|
||||||
variant='ghost'
|
variant='ghost'
|
||||||
@@ -87,32 +89,22 @@ const RowOptionsMenu = ({
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* TODO: apply RBAC */}
|
{showRealizationButton && (
|
||||||
{showApproveButton && (
|
|
||||||
<Button
|
<Button
|
||||||
|
href={`/expense/realization/?expenseId=${props.row.original.id}`}
|
||||||
variant='ghost'
|
variant='ghost'
|
||||||
color='success'
|
color='info'
|
||||||
onClick={approveClickHandler}
|
className='justify-start text-sm text-info focus-visible:text-info-content hover:text-info-content'
|
||||||
className='justify-start text-sm'
|
|
||||||
>
|
>
|
||||||
<Icon icon='material-symbols:check' width={24} height={24} />
|
<Icon
|
||||||
Approve
|
icon='material-symbols:money-bag-rounded'
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
/>
|
||||||
|
Realisasi
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{showRejectButton && (
|
|
||||||
<Button
|
|
||||||
variant='ghost'
|
|
||||||
color='error'
|
|
||||||
onClick={rejectClickHandler}
|
|
||||||
className='justify-start text-sm'
|
|
||||||
>
|
|
||||||
<Icon icon='material-symbols:close' width={24} height={24} />
|
|
||||||
Reject
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{showDeleteButton && (
|
|
||||||
<Button
|
<Button
|
||||||
onClick={deleteClickHandler}
|
onClick={deleteClickHandler}
|
||||||
variant='ghost'
|
variant='ghost'
|
||||||
@@ -127,7 +119,7 @@ const RowOptionsMenu = ({
|
|||||||
/>
|
/>
|
||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
</div>
|
||||||
</RowOptionsMenuWrapper>
|
</RowOptionsMenuWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -178,6 +170,7 @@ const ExpensesTable = () => {
|
|||||||
undefined
|
undefined
|
||||||
);
|
);
|
||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
|
const [isCompleteLoading, setIsCompleteLoading] = useState(false);
|
||||||
const [isApproveLoading, setIsApproveLoading] = useState(false);
|
const [isApproveLoading, setIsApproveLoading] = useState(false);
|
||||||
const [isRejectLoading, setIsRejectLoading] = useState(false);
|
const [isRejectLoading, setIsRejectLoading] = useState(false);
|
||||||
|
|
||||||
@@ -187,6 +180,57 @@ const ExpensesTable = () => {
|
|||||||
parseInt(item)
|
parseInt(item)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isAllSelectedRowLatestApprovalOnManager = useMemo(() => {
|
||||||
|
return selectedRowIds.every((rowId) => {
|
||||||
|
if (!isResponseSuccess(expenses)) return false;
|
||||||
|
|
||||||
|
const expenseItem = expenses.data.find((item) => item.id === rowId);
|
||||||
|
|
||||||
|
const isLatestApprovalRejected =
|
||||||
|
expenseItem?.latest_approval.action === 'REJECTED';
|
||||||
|
|
||||||
|
const isCurrentApprovalOnManager =
|
||||||
|
!isLatestApprovalRejected &&
|
||||||
|
expenseItem?.latest_approval.step_number === 1;
|
||||||
|
|
||||||
|
return isCurrentApprovalOnManager;
|
||||||
|
});
|
||||||
|
}, [expenses, selectedRowIds]);
|
||||||
|
|
||||||
|
const isAllSelectedRowLatestApprovalOnFinance = useMemo(() => {
|
||||||
|
return selectedRowIds.every((rowId) => {
|
||||||
|
if (!isResponseSuccess(expenses)) return false;
|
||||||
|
|
||||||
|
const expenseItem = expenses.data.find((item) => item.id === rowId);
|
||||||
|
|
||||||
|
const isLatestApprovalRejected =
|
||||||
|
expenseItem?.latest_approval.action === 'REJECTED';
|
||||||
|
|
||||||
|
const isCurrentApprovalOnFinance =
|
||||||
|
!isLatestApprovalRejected &&
|
||||||
|
expenseItem?.latest_approval.step_number === 2;
|
||||||
|
|
||||||
|
return isCurrentApprovalOnFinance;
|
||||||
|
});
|
||||||
|
}, [expenses, selectedRowIds]);
|
||||||
|
|
||||||
|
const isAllSelectedRowLatestApprovalOnRealization = useMemo(() => {
|
||||||
|
return selectedRowIds.every((rowId) => {
|
||||||
|
if (!isResponseSuccess(expenses)) return false;
|
||||||
|
|
||||||
|
const expenseItem = expenses.data.find((item) => item.id === rowId);
|
||||||
|
|
||||||
|
const isLatestApprovalRejected =
|
||||||
|
expenseItem?.latest_approval.action === 'REJECTED';
|
||||||
|
|
||||||
|
const isCurrentApprovalOnRealization =
|
||||||
|
!isLatestApprovalRejected &&
|
||||||
|
expenseItem?.latest_approval.step_number === 4;
|
||||||
|
|
||||||
|
return isCurrentApprovalOnRealization;
|
||||||
|
});
|
||||||
|
}, [expenses, selectedRowIds]);
|
||||||
|
|
||||||
const expensesColumns: ColumnDef<Expense>[] = [
|
const expensesColumns: ColumnDef<Expense>[] = [
|
||||||
{
|
{
|
||||||
id: 'select',
|
id: 'select',
|
||||||
@@ -202,7 +246,8 @@ const ExpensesTable = () => {
|
|||||||
),
|
),
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const isCheckboxDisabled =
|
const isCheckboxDisabled =
|
||||||
!row.getCanSelect() || row.original.approval.action === 'REJECTED';
|
!row.getCanSelect() ||
|
||||||
|
row.original.latest_approval.action === 'REJECTED';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -218,61 +263,52 @@ const ExpensesTable = () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'transaction_date',
|
accessorKey: 'expense_date',
|
||||||
header: 'Tanggal Pengajuan',
|
header: 'Tanggal Pengajuan',
|
||||||
|
cell: (props) =>
|
||||||
|
props.row.original.expense_date
|
||||||
|
? formatDate(props.row.original.expense_date, 'DD MMM YYYY')
|
||||||
|
: '-',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'realization_date',
|
accessorKey: 'realization_date',
|
||||||
header: 'Tanggal Realisasi',
|
header: 'Tanggal Realisasi',
|
||||||
cell: (props) => props.getValue() ?? '-',
|
cell: (props) =>
|
||||||
|
props.row.original.realization_date
|
||||||
|
? formatDate(props.row.original.realization_date, 'DD MMM YYYY')
|
||||||
|
: '-',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'location',
|
accessorKey: 'location',
|
||||||
header: 'Lokasi',
|
header: 'Lokasi',
|
||||||
cell: (props) => props.row.original.location.name ?? '-',
|
cell: (props) => props.row.original.location?.name ?? '-',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorFn: (row) => row.created_user.name ?? '-',
|
accessorFn: (row) => row.created_user.name ?? '-',
|
||||||
header: 'Nama Pengaju',
|
header: 'Nama Pengaju',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorFn: (row) => row.vendor.name ?? '-',
|
accessorFn: (row) => row.supplier.name ?? '-',
|
||||||
header: 'Vendor',
|
header: 'Vendor',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'nominal',
|
accessorKey: 'grand_total',
|
||||||
header: 'Nominal',
|
header: 'Nominal',
|
||||||
cell: (props) =>
|
cell: (props) =>
|
||||||
props.row.original.nominal
|
props.row.original.grand_total
|
||||||
? `Rp${formatCurrency(props.row.original.nominal)}`
|
? formatCurrency(props.row.original.grand_total)
|
||||||
: '-',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: 'paid',
|
|
||||||
header: 'Sudah Bayar',
|
|
||||||
cell: (props) =>
|
|
||||||
props.row.original.paid
|
|
||||||
? `Rp${formatCurrency(props.row.original.paid)}`
|
|
||||||
: '-',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: 'remaining_cost',
|
|
||||||
header: 'Sisa Bayar',
|
|
||||||
cell: (props) =>
|
|
||||||
props.row.original.remaining_cost
|
|
||||||
? `Rp${formatCurrency(props.row.original.remaining_cost)}`
|
|
||||||
: '-',
|
: '-',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Status Pencairan',
|
header: 'Status Pencairan',
|
||||||
cell: (props) => (
|
cell: (props) => (
|
||||||
<RealizationStatusBadge approval={props.row.original.approval} />
|
<RealizationStatusBadge approval={props.row.original.latest_approval} />
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Status BOP',
|
header: 'Status BOP',
|
||||||
cell: (props) => (
|
cell: (props) => (
|
||||||
<ExpenseStatusBadge approval={props.row.original.approval} />
|
<ExpenseStatusBadge approval={props.row.original.latest_approval} />
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -283,7 +319,7 @@ const ExpensesTable = () => {
|
|||||||
const currentRowRelativeIndex =
|
const currentRowRelativeIndex =
|
||||||
currentPageRows.findIndex((r) => r.id === props.row.id) + 1;
|
currentPageRows.findIndex((r) => r.id === props.row.id) + 1;
|
||||||
|
|
||||||
const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2;
|
const isLast2Rows = currentRowRelativeIndex > currentPageSize - 3;
|
||||||
|
|
||||||
const approveClickHandler = () => {
|
const approveClickHandler = () => {
|
||||||
setSelectedExpense(props.row.original);
|
setSelectedExpense(props.row.original);
|
||||||
@@ -314,7 +350,7 @@ const ExpensesTable = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{currentPageSize > 2 && (
|
{currentPageSize > 3 && (
|
||||||
<RowDropdownOptions isLast2Rows={isLast2Rows}>
|
<RowDropdownOptions isLast2Rows={isLast2Rows}>
|
||||||
<RowOptionsMenu
|
<RowOptionsMenu
|
||||||
type='dropdown'
|
type='dropdown'
|
||||||
@@ -326,7 +362,7 @@ const ExpensesTable = () => {
|
|||||||
</RowDropdownOptions>
|
</RowDropdownOptions>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{currentPageSize <= 2 && (
|
{currentPageSize <= 3 && (
|
||||||
<RowCollapseOptions>
|
<RowCollapseOptions>
|
||||||
<RowOptionsMenu
|
<RowOptionsMenu
|
||||||
type='dropdown'
|
type='dropdown'
|
||||||
@@ -346,9 +382,20 @@ const ExpensesTable = () => {
|
|||||||
const tableEnableRowSelectionHandler: (row: Row<Expense>) => boolean = (
|
const tableEnableRowSelectionHandler: (row: Row<Expense>) => boolean = (
|
||||||
row
|
row
|
||||||
) => {
|
) => {
|
||||||
return row.original.approval.action !== 'REJECTED';
|
return (
|
||||||
|
row.original.latest_approval.action !== 'REJECTED' &&
|
||||||
|
row.original.latest_approval.step_number !== 5
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// const bulkApproveClickHandler = () => {
|
||||||
|
// approveModal.openModal();
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const bulkRejectClickHandler = () => {
|
||||||
|
// rejectModal.openModal();
|
||||||
|
// };
|
||||||
|
|
||||||
const bulkApproveClickHandler = () => {
|
const bulkApproveClickHandler = () => {
|
||||||
approveModal.openModal();
|
approveModal.openModal();
|
||||||
};
|
};
|
||||||
@@ -371,17 +418,26 @@ const ExpensesTable = () => {
|
|||||||
const confirmationModalApproveClickHandler = async (notes: string) => {
|
const confirmationModalApproveClickHandler = async (notes: string) => {
|
||||||
setIsApproveLoading(true);
|
setIsApproveLoading(true);
|
||||||
|
|
||||||
const bulkApproveResponse = await ExpenseApi.bulkApprove(
|
let bulkApproveResponse: BaseApiResponse<Expense> | undefined = undefined;
|
||||||
|
|
||||||
|
if (isAllSelectedRowLatestApprovalOnManager) {
|
||||||
|
bulkApproveResponse = await ExpenseApi.bulkApproveManager(
|
||||||
selectedRowIds,
|
selectedRowIds,
|
||||||
notes
|
notes
|
||||||
);
|
);
|
||||||
|
} else if (isAllSelectedRowLatestApprovalOnFinance) {
|
||||||
|
bulkApproveResponse = await ExpenseApi.bulkApproveFinance(
|
||||||
|
selectedRowIds,
|
||||||
|
notes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (isResponseSuccess(bulkApproveResponse)) {
|
if (isResponseSuccess(bulkApproveResponse)) {
|
||||||
refreshExpenses();
|
refreshExpenses();
|
||||||
approveModal.closeModal();
|
approveModal.closeModal();
|
||||||
|
|
||||||
toast.success(
|
toast.success(
|
||||||
`Berhasil approve ${selectedRowIds.length} data transfer ke laying!`
|
`Berhasil approve ${selectedRowIds.length} data biaya operasional!`
|
||||||
);
|
);
|
||||||
|
|
||||||
setRowSelection({});
|
setRowSelection({});
|
||||||
@@ -389,7 +445,7 @@ const ExpensesTable = () => {
|
|||||||
approveModal.closeModal();
|
approveModal.closeModal();
|
||||||
|
|
||||||
toast.error(
|
toast.error(
|
||||||
`Gagal approve ${selectedRowIds.length} data transfer ke laying!`
|
`Gagal approve ${selectedRowIds.length} data biaya operasional!`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,24 +455,33 @@ const ExpensesTable = () => {
|
|||||||
const confirmationModalRejectClickHandler = async (notes: string) => {
|
const confirmationModalRejectClickHandler = async (notes: string) => {
|
||||||
setIsRejectLoading(true);
|
setIsRejectLoading(true);
|
||||||
|
|
||||||
const bulkRejectResponse = await ExpenseApi.bulkReject(
|
let bulkRejectResponse: BaseApiResponse<Expense> | undefined = undefined;
|
||||||
|
|
||||||
|
if (isAllSelectedRowLatestApprovalOnManager) {
|
||||||
|
bulkRejectResponse = await ExpenseApi.bulkRejectManager(
|
||||||
selectedRowIds,
|
selectedRowIds,
|
||||||
notes
|
notes
|
||||||
);
|
);
|
||||||
|
} else if (isAllSelectedRowLatestApprovalOnFinance) {
|
||||||
|
bulkRejectResponse = await ExpenseApi.bulkRejectFinance(
|
||||||
|
selectedRowIds,
|
||||||
|
notes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (isResponseSuccess(bulkRejectResponse)) {
|
if (isResponseSuccess(bulkRejectResponse)) {
|
||||||
refreshExpenses();
|
refreshExpenses();
|
||||||
rejectModal.closeModal();
|
rejectModal.closeModal();
|
||||||
|
|
||||||
toast.success(
|
toast.success(
|
||||||
`Berhasil reject ${selectedRowIds.length} data transfer ke laying!`
|
`Berhasil reject ${selectedRowIds.length} data biaya operasional!`
|
||||||
);
|
);
|
||||||
setRowSelection({});
|
setRowSelection({});
|
||||||
} else {
|
} else {
|
||||||
rejectModal.closeModal();
|
rejectModal.closeModal();
|
||||||
|
|
||||||
toast.error(
|
toast.error(
|
||||||
`Gagal reject ${selectedRowIds.length} data transfer ke laying!`
|
`Gagal reject ${selectedRowIds.length} data biaya operasional!`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -506,27 +571,36 @@ const ExpensesTable = () => {
|
|||||||
|
|
||||||
{selectedRowIds.length > 0 && (
|
{selectedRowIds.length > 0 && (
|
||||||
<>
|
<>
|
||||||
{/* TODO: apply RBAC */}
|
<Button
|
||||||
|
variant='outline'
|
||||||
|
color='info'
|
||||||
|
onClick={bulkApproveClickHandler}
|
||||||
|
disabled={!isAllSelectedRowLatestApprovalOnManager}
|
||||||
|
className='w-full sm:w-fit'
|
||||||
|
>
|
||||||
|
<Icon icon='lucide-lab:farm' width={24} height={24} />
|
||||||
|
Approve Manager
|
||||||
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant='outline'
|
variant='outline'
|
||||||
color='success'
|
color='success'
|
||||||
onClick={bulkApproveClickHandler}
|
onClick={bulkApproveClickHandler}
|
||||||
disabled={selectedRowIds.length === 0}
|
disabled={!isAllSelectedRowLatestApprovalOnFinance}
|
||||||
className='w-full sm:w-fit'
|
className='w-full sm:w-fit'
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon icon='tdesign:money' width={24} height={24} />
|
||||||
icon='material-symbols:check'
|
Approve Finance
|
||||||
width={24}
|
|
||||||
height={24}
|
|
||||||
/>
|
|
||||||
Approve
|
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant='outline'
|
variant='outline'
|
||||||
color='error'
|
color='error'
|
||||||
onClick={bulkRejectClickHandler}
|
onClick={bulkRejectClickHandler}
|
||||||
disabled={selectedRowIds.length === 0}
|
disabled={
|
||||||
|
!isAllSelectedRowLatestApprovalOnManager &&
|
||||||
|
!isAllSelectedRowLatestApprovalOnFinance
|
||||||
|
}
|
||||||
className='w-full sm:w-fit'
|
className='w-full sm:w-fit'
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
@@ -666,7 +740,7 @@ const ExpensesTable = () => {
|
|||||||
<ConfirmationModalWithNotes
|
<ConfirmationModalWithNotes
|
||||||
ref={approveModal.ref}
|
ref={approveModal.ref}
|
||||||
type='success'
|
type='success'
|
||||||
text={`Apakah anda yakin ingin approve data biaya operasional ini (${selectedRowIds.length} data)?`}
|
text='Apakah anda yakin ingin approve data biaya operasional ini?'
|
||||||
secondaryButton={{
|
secondaryButton={{
|
||||||
text: 'Tidak',
|
text: 'Tidak',
|
||||||
}}
|
}}
|
||||||
@@ -681,7 +755,7 @@ const ExpensesTable = () => {
|
|||||||
<ConfirmationModalWithNotes
|
<ConfirmationModalWithNotes
|
||||||
ref={rejectModal.ref}
|
ref={rejectModal.ref}
|
||||||
type='error'
|
type='error'
|
||||||
text={`Apakah anda yakin ingin reject data biaya operasional ini (${selectedRowIds.length} data)?`}
|
text='Apakah anda yakin ingin reject data biaya operasional ini?'
|
||||||
secondaryButton={{
|
secondaryButton={{
|
||||||
text: 'Tidak',
|
text: 'Tidak',
|
||||||
}}
|
}}
|
||||||
|
|||||||
Reference in New Issue
Block a user