wulf-pulse/components/admin/roles/permission-picker.tsx

110 lines
3.4 KiB
TypeScript
Raw Normal View History

"use client";
import { Checkbox } from "@/components/ui/checkbox";
import { Label } from "@/components/ui/label";
import { statement } from "@/lib/permissions";
interface PermissionPickerProps {
value: Record<string, string[]>;
onChange: (permissions: Record<string, string[]>) => void;
disabled?: boolean;
}
const resourceLabels: Record<string, string> = {
tickets: "Tickets",
configItems: "Configuration Items",
admin: "Admin Panel",
users: "User Management",
roles: "Role Management",
auditLog: "Audit Log",
settings: "Settings",
};
const actionLabels: Record<string, string> = {
create: "Create",
read: "Read",
update: "Update",
delete: "Delete",
access: "Access",
invite: "Invite",
ban: "Ban",
};
export function PermissionPicker({ value, onChange, disabled }: PermissionPickerProps) {
const handleToggle = (resource: string, action: string, checked: boolean) => {
const currentActions = value[resource] || [];
let newActions: string[];
if (checked) {
newActions = [...currentActions, action];
} else {
newActions = currentActions.filter((a) => a !== action);
}
onChange({
...value,
[resource]: newActions,
});
};
const handleToggleAll = (resource: string, checked: boolean) => {
const allActions = statement[resource as keyof typeof statement] as readonly string[];
onChange({
...value,
[resource]: checked ? [...allActions] : [],
});
};
return (
<div className="space-y-6">
{Object.entries(statement).map(([resource, actions]) => {
const currentActions = value[resource] || [];
const allChecked = actions.every((a) => currentActions.includes(a));
const someChecked = actions.some((a) => currentActions.includes(a));
return (
<div key={resource} className="space-y-3">
<div className="flex items-center gap-2">
<Checkbox
id={`${resource}-all`}
checked={allChecked}
onCheckedChange={(checked) =>
handleToggleAll(resource, checked as boolean)
}
disabled={disabled}
className={someChecked && !allChecked ? "data-[state=checked]:bg-muted" : ""}
/>
<Label
htmlFor={`${resource}-all`}
className="font-semibold cursor-pointer"
>
{resourceLabels[resource] || resource}
</Label>
</div>
<div className="ml-6 grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-3">
{(actions as readonly string[]).map((action) => (
<div key={action} className="flex items-center gap-2">
<Checkbox
id={`${resource}-${action}`}
checked={currentActions.includes(action)}
onCheckedChange={(checked) =>
handleToggle(resource, action, checked as boolean)
}
disabled={disabled}
/>
<Label
htmlFor={`${resource}-${action}`}
className="text-sm cursor-pointer"
>
{actionLabels[action] || action}
</Label>
</div>
))}
</div>
</div>
);
})}
</div>
);
}