dress/resources/js/pages/admin/system/role/edit.tsx

267 lines
12 KiB
TypeScript

import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import { Checkbox } from '@/components/ui/checkbox';
import { Field, FieldError } from '@/components/ui/field';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import roleRoutes from '@/routes/system/role';
import type { Permission, Role } from '@/types';
import { Head, Link, useForm } from '@inertiajs/react';
import { ArrowLeft, Loader, Save } from 'lucide-react';
import React, { useMemo } from 'react';
import { toast } from 'sonner';
export default function RoleEdit({
role,
permissions,
}: {
role: Role;
permissions: Permission[];
}) {
const { data, setData, put, processing, errors } = useForm<{
name: string;
guard_name: string;
permissions: number[];
}>({
name: role.name,
guard_name: role.guard_name,
permissions: role.permissions?.map((p: Permission) => p.id) || [],
});
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
put(roleRoutes.update(role.id).url, {
onSuccess: (response: any) => {
const flash = response.props.flash;
if (flash?.error) {
toast.error(flash.error);
} else if (flash?.success) {
toast.success(flash.success);
}
},
});
};
const groupedPermissions = useMemo(() => {
const groups: Record<string, Permission[]> = {};
permissions.forEach((permission) => {
const parts = permission.name.split(':');
const groupName = parts.length > 1 ? parts[1] : 'Lainnya';
if (!groups[groupName]) {
groups[groupName] = [];
}
groups[groupName].push(permission);
});
return groups;
}, [permissions]);
const handleCheckboxChange = (id: number, checked: boolean) => {
if (checked) {
setData('permissions', [...data.permissions, id]);
} else {
setData(
'permissions',
data.permissions.filter((pId) => pId !== id),
);
}
};
const toggleGroup = (groupName: string) => {
const groupPermissionIds = groupedPermissions[groupName].map(
(p) => p.id,
);
const allInGroupSelected = groupPermissionIds.every((id) =>
data.permissions.includes(id),
);
if (allInGroupSelected) {
setData(
'permissions',
data.permissions.filter(
(id) => !groupPermissionIds.includes(id),
),
);
} else {
const otherPermissions = data.permissions.filter(
(id) => !groupPermissionIds.includes(id),
);
setData('permissions', [
...otherPermissions,
...groupPermissionIds,
]);
}
};
const selectAll = () => {
setData(
'permissions',
permissions.map((p) => p.id),
);
};
const deselectAll = () => {
setData('permissions', []);
};
return (
<div className="flex flex-col gap-6 p-6">
<Head title="Ubah Role" />
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold tracking-tight">
Ubah Role
</h1>
</div>
<Link href={roleRoutes.index().url}>
<Button variant="outline" className="gap-2">
<ArrowLeft className="size-4" />
Kembali
</Button>
</Link>
</div>
<form onSubmit={onSubmit} className="space-y-6">
<Card className="overflow-hidden border-none bg-card/50 shadow-lg backdrop-blur-sm">
<CardContent className="p-0">
<div className="border-b bg-muted/30 p-6">
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
<Field>
<Label htmlFor="name" required>
Nama
</Label>
<Input
id="name"
name="name"
value={data.name}
onChange={(e) =>
setData('name', e.target.value)
}
autoComplete="off"
/>
<FieldError
error={errors.name}
label="Nama"
className="text-xs"
/>
</Field>
<Field>
<Label htmlFor="guard_name" required>
Guard
</Label>
<Input
id="guard_name"
name="guard_name"
value={data.guard_name}
onChange={(e) =>
setData(
'guard_name',
e.target.value,
)
}
autoComplete="off"
/>
<FieldError
error={errors.guard_name}
label="Guard"
className="text-xs"
/>
</Field>
</div>
</div>
<div className="space-y-8 p-6">
{Object.entries(groupedPermissions).map(
([groupName, groupPermissions]) => {
const allInGroupSelected =
groupPermissions.every((p) =>
data.permissions.includes(p.id),
);
return (
<div
key={groupName}
className="space-y-4"
>
<div className="flex items-center justify-between border-b pb-2">
<h3 className="text-xs font-bold tracking-widest text-muted-foreground uppercase">
{groupName}
</h3>
<Button
type="button"
variant="outline"
size="sm"
className="h-8 gap-1.5"
onClick={() =>
toggleGroup(groupName)
}
>
{allInGroupSelected
? 'Lepas Semua'
: 'Pilih Semua'}
</Button>
</div>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
{groupPermissions.map(
(permission) => (
<div
key={permission.id}
className="group flex items-center space-x-3"
>
<Checkbox
id={`permission-${permission.id}`}
checked={data.permissions.includes(
permission.id,
)}
onCheckedChange={(
checked,
) =>
handleCheckboxChange(
permission.id,
checked as boolean,
)
}
className="transition-transform active:scale-90"
/>
<Label
htmlFor={`permission-${permission.id}`}
className="cursor-pointer text-sm leading-none font-medium transition-colors group-hover:text-primary"
>
{
permission.name.split(
':',
)[0]
}
</Label>
</div>
),
)}
</div>
</div>
);
},
)}
</div>
</CardContent>
</Card>
<div className="flex justify-end">
<Button type="submit" disabled={processing}>
{processing ? (
<Loader className="size-4 animate-spin" />
) : (
<Save className="size-4" />
)}
{processing ? 'Menyimpan...' : 'Simpan'}
</Button>
</div>
</form>
</div>
);
}
RoleEdit.layout = {
breadcrumbs: [{ title: 'Sistem' }],
};