feat(FE-331): implement permission guard in project flock

This commit is contained in:
ValdiANS
2025-12-26 21:08:00 +07:00
parent 4be719b9d8
commit 9e0d3e2bbf
5 changed files with 242 additions and 173 deletions
+52 -24
View File
@@ -5,6 +5,8 @@ import Tooltip from '@/components/Tooltip';
import { cn } from '@/lib/helper';
import { Icon } from '@iconify/react';
import { useAuth } from '@/services/hooks/useAuth';
type FloatingActionsButtonProps = {
actions: {
action: 'DETAIL' | 'EDIT' | 'DELETE';
@@ -13,6 +15,7 @@ type FloatingActionsButtonProps = {
onClick?: () => void;
hidden?: boolean;
disabled?: boolean;
permissions?: string | string[];
}[];
approvals: {
action: 'APPROVED' | 'REJECTED';
@@ -20,6 +23,7 @@ type FloatingActionsButtonProps = {
label?: string;
onClick?: () => void;
disabled?: boolean;
permissions?: string | string[];
}[];
selectedRowIds: number[];
onClose: () => void;
@@ -31,6 +35,7 @@ const FloatingActionsButton = ({
selectedRowIds,
onClose,
}: FloatingActionsButtonProps) => {
const { permissionCheck } = useAuth();
// Jika tidak ada baris yang dipilih, jangan tampilkan FAB
const positionStyles =
selectedRowIds.length > 0
@@ -71,7 +76,18 @@ const FloatingActionsButton = ({
<div className='flex gap-4 items-center'>
{/* Render Aksi dari props.actions */}
{actions
.filter((action) => !action.hidden)
.filter((action) => {
if (action.hidden) return false;
if (action.permissions) {
if (typeof action.permissions === 'string') {
return permissionCheck(action.permissions);
}
return action.permissions.some((permission) =>
permissionCheck(permission)
);
}
return true;
})
.map((action, index) => {
return (
<Button
@@ -111,29 +127,41 @@ const FloatingActionsButton = ({
{/* === BARIS BAWAH: Approval Buttons (Approve/Reject) === */}
<div className={`grid grid-cols-${approvals.length} gap-3`}>
{approvals.map((approval, index) => (
<Button
key={index}
onClick={approval.onClick}
className={cn(
'btn btn-lg w-full',
'bg-white/20 border-white/30',
'text-white/50 font-semibold flex items-center gap-2 rounded-lg transition-all duration-200',
approval.disabled
? 'cursor-not-allowed'
: 'hover:text-white/100 hover:bg-white/40 hover:border-white/50'
)}
disabled={approval.disabled}
>
<Icon
icon={approval.icon}
width={20}
height={20}
className={`text-${getApprovalColor(approval.action)}`}
/>
{approval.label || approval.action}
</Button>
))}
{approvals
.filter((approval) => {
if (approval.permissions) {
if (typeof approval.permissions === 'string') {
return permissionCheck(approval.permissions);
}
return approval.permissions.some((permission) =>
permissionCheck(permission)
);
}
return true;
})
.map((approval, index) => (
<Button
key={index}
onClick={approval.onClick}
className={cn(
'btn btn-lg w-full',
'bg-white/20 border-white/30',
'text-white/50 font-semibold flex items-center gap-2 rounded-lg transition-all duration-200',
approval.disabled
? 'cursor-not-allowed'
: 'hover:text-white/100 hover:bg-white/40 hover:border-white/50'
)}
disabled={approval.disabled}
>
<Icon
icon={approval.icon}
width={20}
height={20}
className={`text-${getApprovalColor(approval.action)}`}
/>
{approval.label || approval.action}
</Button>
))}
</div>
</div>
</div>