import { zodResolver } from '@hookform/resolvers/zod' import { ChevronUp, MoreVertical, Trash } from 'lucide-react' import { useEffect, useState } from 'react' import { useForm, type SubmitHandler } from 'react-hook-form' import { toast } from 'sonner' import { Button, cn, Collapsible, CollapsibleContent, CollapsibleTrigger, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, Form, FormControl, FormField, Switch, } from 'ui' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' import * as z from 'zod' import { ROLE_PERMISSIONS } from './Roles.constants' import { useDatabaseRoleUpdateMutation } from '@/data/database-roles/database-role-update-mutation' import type { PgRole } from '@/data/database-roles/database-roles-query' import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject' interface RoleRowProps { role: PgRole disabled?: boolean onSelectDelete: (role: string) => void } const permissionSchema = z.boolean().optional() const formSchema = z.object( Object.keys(ROLE_PERMISSIONS).reduce( (acc, key) => ({ ...acc, [key]: permissionSchema, }), {} as Record ) ) export const RoleRow = ({ role, disabled = false, onSelectDelete }: RoleRowProps) => { const { data: project } = useSelectedProjectQuery() const [isExpanded, setIsExpanded] = useState(false) const { mutate: updateDatabaseRole, isPending: isUpdating } = useDatabaseRoleUpdateMutation() const form = useForm>({ resolver: zodResolver(formSchema as any), defaultValues: role, }) const { reset, formState } = form const { isDirty } = formState useEffect(() => { reset(role) }, [role, reset]) const onSaveChanges: SubmitHandler> = async (values) => { if (!project) return console.error('Project is required') const changed = Object.fromEntries( Object.entries(values).filter(([k, v]) => { const key = k as keyof PgRole return v !== role[key] }) ) if (Object.keys(changed).length === 0) { // No actual changes to persist; avoid sending an empty update payload reset(role) return } updateDatabaseRole( { projectRef: project.ref, connectionString: project.connectionString, id: role.id, payload: changed, }, { onSuccess: () => { toast.success(`Successfully updated role "${role.name}"`) reset(values) }, } ) } const formId = `role-update-form-${role.id}` return (
{!disabled && (
{(Object.keys(ROLE_PERMISSIONS) as (keyof typeof ROLE_PERMISSIONS)[]).map( (permission) => ( ( )} /> ) )}
{!disabled && (
)}
) }