"use client"; import { zodResolver } from "@hookform/resolvers/zod"; import { SearchIcon } from "lucide-react"; import { useEffect, useMemo, useState } from "react"; import { Controller, Resolver, useForm } from "react-hook-form"; import { z } from "zod/v4"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; import { Drawer, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, } from "@/components/ui/drawer"; import { Field, FieldContent, FieldError, FieldLabel, } from "@/components/ui/field"; import { Input } from "@/components/ui/input"; import type { AdminPermission, AdminRole } from "@/lib/api/admin"; import { useAppLocale, useI18n } from "@/i18n/provider"; import { getPermissionDisplayName, getPermissionGroupName } from "@/lib/permission-i18n"; import { getRoleDisplayName } from "@/lib/role-i18n"; type AssignPermissionsDrawerProps = { open: boolean; saving: boolean; loading: boolean; item: AdminRole | null; permissions: AdminPermission[]; selectedPermissionIds: number[]; onOpenChange: (open: boolean) => void; onSubmit: (permissionIds: number[]) => Promise; }; const assignPermissionsSchema = z.object({ permissionIds: z.array(z.number().int().positive()), }); type AssignPermissionsForm = z.infer; const assignPermissionsResolver = zodResolver( assignPermissionsSchema as never, ) as Resolver< z.input, undefined, z.output >; function buildForm(selectedPermissionIds: number[]): AssignPermissionsForm { return { permissionIds: selectedPermissionIds, }; } export function AssignPermissionsDrawer({ open, saving, loading, item, permissions, selectedPermissionIds, onOpenChange, onSubmit, }: AssignPermissionsDrawerProps) { return ( {open ? ( ) : null} ); } type AssignPermissionsDrawerBodyProps = { saving: boolean; loading: boolean; item: AdminRole | null; permissions: AdminPermission[]; selectedPermissionIds: number[]; onOpenChange: (open: boolean) => void; onSubmit: (permissionIds: number[]) => Promise; }; function AssignPermissionsDrawerBody({ saving, loading, item, permissions, selectedPermissionIds, onOpenChange, onSubmit, }: AssignPermissionsDrawerBodyProps) { const t = useI18n(); const { locale } = useAppLocale(); const [keyword, setKeyword] = useState(""); const form = useForm< z.input, undefined, z.output >({ resolver: assignPermissionsResolver, defaultValues: buildForm(selectedPermissionIds), }); const { control, handleSubmit, reset, formState: { errors }, } = form; useEffect(() => { reset(buildForm(selectedPermissionIds)); }, [reset, selectedPermissionIds]); const groupedPermissions = useMemo(() => { const output = keyword.trim().toLowerCase(); const filtered = output ? permissions.filter((permission) => `${permission.name} ${getPermissionDisplayName(permission.code, permission.name, locale)} ${permission.code} ${permission.groupName} ${getPermissionGroupName(permission.groupName, locale)} ${permission.apiPath}` .toLowerCase() .includes(output), ) : permissions; const groups = new Map(); filtered.forEach((permission) => { const groupName = permission.groupName || "default"; const list = groups.get(groupName) ?? []; list.push(permission); groups.set(groupName, list); }); return Array.from(groups.entries()).sort(([left], [right]) => getPermissionGroupName(left, locale).localeCompare(getPermissionGroupName(right, locale)), ); }, [keyword, locale, permissions]); async function onFormSubmit(values: AssignPermissionsForm) { await onSubmit(values.permissionIds); } return ( {t("role.assignTitle")} {t("role.currentRole", { name: item ? getRoleDisplayName(item.code, item.name, locale) : "-", code: item?.code ? `(${item.code})` : "", })}
{t("role.permissionList")}
setKeyword(event.target.value)} placeholder={t("role.searchPermission")} className="pl-9" disabled={loading} />
{ const value = field.value || []; return (
{loading ? (
{t("role.loadingPermissions")}
) : groupedPermissions.length > 0 ? ( groupedPermissions.map(([groupName, list]) => (
{getPermissionGroupName(groupName, locale)} {t("role.permissionCount", { count: list.length })}
{list.map((permission) => { const checked = value.includes(permission.id); return ( ); })}
)) ) : (
{t("role.noPermissions")}
)}
); }} />
); }