feat(FE-331): implement permission guard in expense

This commit is contained in:
ValdiANS
2025-12-24 11:08:37 +07:00
parent dda29e10d1
commit 6ed7dcfa6d
5 changed files with 362 additions and 294 deletions
+113 -87
View File
@@ -28,6 +28,7 @@ import ExpenseStatusBadge from '@/components/pages/expense/ExpenseStatusBadge';
import CheckboxInput from '@/components/input/CheckboxInput';
import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes';
import DateInput from '@/components/input/DateInput';
import RequirePermission from '@/components/helper/RequirePermission';
import { Expense } from '@/types/api/expense';
import { ExpenseApi } from '@/services/api/expense';
@@ -67,58 +68,70 @@ const RowOptionsMenu = ({
return (
<RowOptionsMenuWrapper type={type}>
<div className='w-full max-h-40 overflow-auto flex flex-col gap-1'>
<Button
href={`/expense/detail/?expenseId=${props.row.original.id}`}
variant='ghost'
color='primary'
className='justify-start text-sm'
>
<Icon icon='mdi:eye-outline' width={16} height={16} />
Detail
</Button>
{showEditButton && (
<RequirePermission permissions='lti.expense.detail'>
<Button
href={`/expense/detail/edit/?expenseId=${props.row.original.id}`}
href={`/expense/detail/?expenseId=${props.row.original.id}`}
variant='ghost'
color='warning'
color='primary'
className='justify-start text-sm'
>
<Icon icon='material-symbols:edit-outline' width={16} height={16} />
Edit
<Icon icon='mdi:eye-outline' width={16} height={16} />
Detail
</Button>
</RequirePermission>
{showEditButton && (
<RequirePermission permissions='lti.expense.update'>
<Button
href={`/expense/detail/edit/?expenseId=${props.row.original.id}`}
variant='ghost'
color='warning'
className='justify-start text-sm'
>
<Icon
icon='material-symbols:edit-outline'
width={16}
height={16}
/>
Edit
</Button>
</RequirePermission>
)}
{showRealizationButton && (
<Button
href={`/expense/realization/?expenseId=${props.row.original.id}`}
variant='ghost'
color='info'
className='justify-start text-sm text-info focus-visible:text-info-content hover:text-info-content'
>
<Icon
icon='material-symbols:money-bag-rounded'
width={16}
height={16}
/>
Realisasi
</Button>
<RequirePermission permissions='lti.expense.create.realization'>
<Button
href={`/expense/realization/?expenseId=${props.row.original.id}`}
variant='ghost'
color='info'
className='justify-start text-sm text-info focus-visible:text-info-content hover:text-info-content'
>
<Icon
icon='material-symbols:money-bag-rounded'
width={16}
height={16}
/>
Realisasi
</Button>
</RequirePermission>
)}
<Button
onClick={deleteClickHandler}
variant='ghost'
color='error'
className='justify-start text-sm text-error focus-visible:text-error-content hover:text-error-content'
>
<Icon
icon='material-symbols:delete-outline-rounded'
width={16}
height={16}
className='justify-start text-sm'
/>
Delete
</Button>
<RequirePermission permissions='lti.expense.delete'>
<Button
onClick={deleteClickHandler}
variant='ghost'
color='error'
className='justify-start text-sm text-error focus-visible:text-error-content hover:text-error-content'
>
<Icon
icon='material-symbols:delete-outline-rounded'
width={16}
height={16}
className='justify-start text-sm'
/>
Delete
</Button>
</RequirePermission>
</div>
</RowOptionsMenuWrapper>
);
@@ -559,57 +572,70 @@ const ExpensesTable = () => {
<div className='flex flex-col gap-2 mb-4'>
<div className='w-full flex flex-col sm:flex-row justify-between items-end sm:items-center gap-4'>
<div className='w-full sm:w-fit flex flex-col sm:flex-row self-start gap-2'>
<Button
href='/expense/add'
variant='outline'
color='primary'
className='w-full sm:w-fit'
>
<Icon icon='ic:round-plus' width={24} height={24} />
Tambah
</Button>
<RequirePermission permissions='lti.expense.create'>
<Button
href='/expense/add'
variant='outline'
color='primary'
className='w-full sm:w-fit'
>
<Icon icon='ic:round-plus' width={24} height={24} />
Tambah
</Button>
</RequirePermission>
{selectedRowIds.length > 0 && (
<>
<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>
<RequirePermission permissions='lti.expense.approve.manager'>
<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>
</RequirePermission>
<Button
variant='outline'
color='success'
onClick={bulkApproveClickHandler}
disabled={!isAllSelectedRowLatestApprovalOnFinance}
className='w-full sm:w-fit'
>
<Icon icon='tdesign:money' width={24} height={24} />
Approve Finance
</Button>
<RequirePermission permissions='lti.expense.approve.finance'>
<Button
variant='outline'
color='success'
onClick={bulkApproveClickHandler}
disabled={!isAllSelectedRowLatestApprovalOnFinance}
className='w-full sm:w-fit'
>
<Icon icon='tdesign:money' width={24} height={24} />
Approve Finance
</Button>
</RequirePermission>
<Button
variant='outline'
color='error'
onClick={bulkRejectClickHandler}
disabled={
!isAllSelectedRowLatestApprovalOnManager &&
!isAllSelectedRowLatestApprovalOnFinance
}
className='w-full sm:w-fit'
<RequirePermission
permissions={[
'lti.expense.approve.manager',
'lti.expense.approve.finance',
]}
>
<Icon
icon='material-symbols:close'
width={24}
height={24}
/>
Reject
</Button>
<Button
variant='outline'
color='error'
onClick={bulkRejectClickHandler}
disabled={
!isAllSelectedRowLatestApprovalOnManager &&
!isAllSelectedRowLatestApprovalOnFinance
}
className='w-full sm:w-fit'
>
<Icon
icon='material-symbols:close'
width={24}
height={24}
/>
Reject
</Button>
</RequirePermission>
</>
)}
</div>