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

This commit is contained in:
ValdiANS
2025-12-26 16:21:22 +07:00
parent 1152cd2bef
commit 4be719b9d8
3 changed files with 128 additions and 88 deletions
@@ -15,6 +15,7 @@ import SelectInput, { OptionType } from '@/components/input/SelectInput';
import RowDropdownOptions from '@/components/table/RowDropdownOptions'; import RowDropdownOptions from '@/components/table/RowDropdownOptions';
import RowCollapseOptions from '@/components/table/RowCollapseOptions'; import RowCollapseOptions from '@/components/table/RowCollapseOptions';
import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper'; import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper';
import RequirePermission from '@/components/helper/RequirePermission';
import { cn, formatDate } from '@/lib/helper'; import { cn, formatDate } from '@/lib/helper';
import { isResponseSuccess } from '@/lib/api-helper'; import { isResponseSuccess } from '@/lib/api-helper';
@@ -38,6 +39,7 @@ const RowOptionsMenu = ({
}: RowOptionsMenuProps) => { }: RowOptionsMenuProps) => {
return ( return (
<RowOptionsMenuWrapper type={type}> <RowOptionsMenuWrapper type={type}>
<RequirePermission permissions='lti.purchase.detail'>
<Button <Button
href={`/purchase/detail/?purchaseId=${props.row.original.id}`} href={`/purchase/detail/?purchaseId=${props.row.original.id}`}
variant='ghost' variant='ghost'
@@ -47,6 +49,7 @@ const RowOptionsMenu = ({
<Icon icon='mdi:eye-outline' width={16} height={16} /> <Icon icon='mdi:eye-outline' width={16} height={16} />
Detail Detail
</Button> </Button>
</RequirePermission>
{/*<Button*/} {/*<Button*/}
{/* href={`/purchase/detail/edit/?purchaseId=${props.row.original.id}`}*/} {/* href={`/purchase/detail/edit/?purchaseId=${props.row.original.id}`}*/}
@@ -58,6 +61,7 @@ const RowOptionsMenu = ({
{/* Edit*/} {/* Edit*/}
{/*</Button>*/} {/*</Button>*/}
<RequirePermission permissions='lti.purchase.delete'>
<Button <Button
onClick={deleteClickHandler} onClick={deleteClickHandler}
variant='ghost' variant='ghost'
@@ -72,6 +76,7 @@ const RowOptionsMenu = ({
/> />
Delete Delete
</Button> </Button>
</RequirePermission>
</RowOptionsMenuWrapper> </RowOptionsMenuWrapper>
); );
}; };
@@ -227,6 +232,7 @@ const PurchaseTable = () => {
<div className='flex flex-col gap-2 mb-4'> <div className='flex flex-col gap-2 mb-4'>
<div className='w-full flex flex-col xl:flex-row justify-between items-end xl:items-center gap-2'> <div className='w-full flex flex-col xl:flex-row justify-between items-end xl:items-center gap-2'>
<div className='w-full flex flex-row gap-2'> <div className='w-full flex flex-row gap-2'>
<RequirePermission permissions='lti.purchase.create'>
<Button <Button
href='/purchase/add' href='/purchase/add'
variant='outline' variant='outline'
@@ -236,6 +242,7 @@ const PurchaseTable = () => {
<Icon icon='ic:round-plus' width={24} height={24} /> <Icon icon='ic:round-plus' width={24} height={24} />
Tambah Tambah
</Button> </Button>
</RequirePermission>
</div> </div>
<DebouncedTextInput <DebouncedTextInput
@@ -32,6 +32,7 @@ import {
} from '@/types/api/purchase/purchase'; } from '@/types/api/purchase/purchase';
import { BaseApproval, BaseGroupedApproval } from '@/types/api/api-general'; import { BaseApproval, BaseGroupedApproval } from '@/types/api/api-general';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import RequirePermission from '@/components/helper/RequirePermission';
interface PurchaseOrderStaffApprovalFormProps { interface PurchaseOrderStaffApprovalFormProps {
type?: 'add' | 'edit'; type?: 'add' | 'edit';
@@ -897,11 +898,15 @@ const PurchaseOrderStaffApprovalForm = ({
<div className='flex justify-center'> <div className='flex justify-center'>
{canUpdatePurchaseItems && {canUpdatePurchaseItems &&
canShowDeleteAddButtons && ( canShowDeleteAddButtons && (
<RequirePermission permissions='lti.purchase.delete.item'>
<Button <Button
type='button' type='button'
color='error' color='error'
className='text-sm w-fit'
onClick={() => onClick={() =>
removePurchaseItem(formItemIndex) removePurchaseItem(
formItemIndex
)
} }
title='Hapus item' title='Hapus item'
> >
@@ -911,6 +916,7 @@ const PurchaseOrderStaffApprovalForm = ({
height={16} height={16}
/> />
</Button> </Button>
</RequirePermission>
)} )}
</div> </div>
</td> </td>
@@ -39,10 +39,12 @@ import { toast } from 'react-hot-toast';
import { useSearchParams } from 'next/navigation'; import { useSearchParams } from 'next/navigation';
import { formatCurrency, formatNumber, formatDate } from '@/lib/helper'; import { formatCurrency, formatNumber, formatDate } from '@/lib/helper';
import { PURCHASE_ORDER_APPROVAL_LINE } from '@/config/approval-line'; import { PURCHASE_ORDER_APPROVAL_LINE } from '@/config/approval-line';
import RequirePermission from '@/components/helper/RequirePermission';
const ItemPembelianDropdown = ({ onEdit }: { onEdit: () => void }) => { const ItemPembelianDropdown = ({ onEdit }: { onEdit: () => void }) => {
return ( return (
<RowOptionsMenuWrapper type='dropdown'> <RowOptionsMenuWrapper type='dropdown'>
<RequirePermission permissions='lti.purchase.update'>
<Button <Button
onClick={onEdit} onClick={onEdit}
variant='ghost' variant='ghost'
@@ -52,6 +54,7 @@ const ItemPembelianDropdown = ({ onEdit }: { onEdit: () => void }) => {
<Icon icon='material-symbols:edit-outline' width={16} height={16} /> <Icon icon='material-symbols:edit-outline' width={16} height={16} />
Edit Edit
</Button> </Button>
</RequirePermission>
</RowOptionsMenuWrapper> </RowOptionsMenuWrapper>
); );
}; };
@@ -59,6 +62,7 @@ const ItemPembelianDropdown = ({ onEdit }: { onEdit: () => void }) => {
const PenerimaanBarangDropdown = ({ onEdit }: { onEdit: () => void }) => { const PenerimaanBarangDropdown = ({ onEdit }: { onEdit: () => void }) => {
return ( return (
<RowOptionsMenuWrapper type='dropdown'> <RowOptionsMenuWrapper type='dropdown'>
<RequirePermission permissions='lti.purchase.receive'>
<Button <Button
onClick={onEdit} onClick={onEdit}
variant='ghost' variant='ghost'
@@ -68,6 +72,7 @@ const PenerimaanBarangDropdown = ({ onEdit }: { onEdit: () => void }) => {
<Icon icon='material-symbols:edit-outline' width={16} height={16} /> <Icon icon='material-symbols:edit-outline' width={16} height={16} />
Edit Edit
</Button> </Button>
</RequirePermission>
</RowOptionsMenuWrapper> </RowOptionsMenuWrapper>
); );
}; };
@@ -496,6 +501,7 @@ const PurchaseOrderDetail = ({
}; };
return ( return (
<RequirePermission permissions='lti.purchase.delete.item'>
<Button <Button
type='button' type='button'
color='error' color='error'
@@ -504,6 +510,7 @@ const PurchaseOrderDetail = ({
> >
<Icon icon='mdi:trash-can' width={16} height={16} /> <Icon icon='mdi:trash-can' width={16} height={16} />
</Button> </Button>
</RequirePermission>
); );
}, },
}, },
@@ -632,6 +639,15 @@ const PurchaseOrderDetail = ({
{showApprovalButton && ( {showApprovalButton && (
<div className='flex flex-row gap-2'> <div className='flex flex-row gap-2'>
<RequirePermission
permissions={
approvalStep === 1
? 'lti.purchase.approve.staff'
: approvalStep === 2
? 'lti.purchase.approve.manager'
: 'lti.purchase.receive'
}
>
<Button <Button
onClick={handleApprovalClick} onClick={handleApprovalClick}
variant='outline' variant='outline'
@@ -641,7 +657,17 @@ const PurchaseOrderDetail = ({
<Icon icon='material-symbols:check' width={24} height={24} /> <Icon icon='material-symbols:check' width={24} height={24} />
Approve Approve
</Button> </Button>
</RequirePermission>
<RequirePermission
permissions={
approvalStep === 1
? 'lti.purchase.approve.staff'
: approvalStep === 2
? 'lti.purchase.approve.manager'
: 'lti.purchase.receive'
}
>
<Button <Button
variant='outline' variant='outline'
color='error' color='error'
@@ -651,6 +677,7 @@ const PurchaseOrderDetail = ({
<Icon icon='material-symbols:close' width={24} height={24} /> <Icon icon='material-symbols:close' width={24} height={24} />
Reject Reject
</Button> </Button>
</RequirePermission>
</div> </div>
)} )}
</div> </div>