| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- import { zodResolver } from '@hookform/resolvers/zod'
- import { SubmitHandler, useForm } from 'react-hook-form'
- import { toast } from 'sonner'
- import {
- Form,
- FormControl,
- FormField,
- FormItem,
- FormLabel,
- FormMessage,
- Input,
- SidePanel,
- Switch,
- } from 'ui'
- import z from 'zod'
- import { ROLE_PERMISSIONS } from './Roles.constants'
- import { FormActions } from '@/components/ui/Forms/FormActions'
- import { useDatabaseRoleCreateMutation } from '@/data/database-roles/database-role-create-mutation'
- import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
- interface CreateRolePanelProps {
- visible: boolean
- onClose: () => void
- }
- const FormSchema = z.object({
- name: z.string().trim().min(1, 'You must provide a name').default(''),
- isSuperuser: z.boolean().default(false),
- canLogin: z.boolean().default(false),
- canCreateRole: z.boolean().default(false),
- canCreateDb: z.boolean().default(false),
- isReplicationRole: z.boolean().default(false),
- canBypassRls: z.boolean().default(false),
- })
- const initialValues = {
- name: '',
- isSuperuser: false,
- canLogin: false,
- canCreateRole: false,
- canCreateDb: false,
- isReplicationRole: false,
- canBypassRls: false,
- }
- export const CreateRolePanel = ({ visible, onClose }: CreateRolePanelProps) => {
- const formId = 'create-new-role'
- const { data: project } = useSelectedProjectQuery()
- const form = useForm<z.infer<typeof FormSchema>>({
- resolver: zodResolver(FormSchema as any),
- })
- const { mutate: createDatabaseRole, isPending: isCreating } = useDatabaseRoleCreateMutation({
- onSuccess: (_, vars) => {
- toast.success(`Successfully created new role: ${vars.payload.name}`)
- handleClose()
- },
- })
- const onSubmit: SubmitHandler<z.infer<typeof FormSchema>> = async (values) => {
- if (!project) return console.error('Project is required')
- createDatabaseRole({
- projectRef: project.ref,
- connectionString: project.connectionString,
- payload: values,
- })
- }
- const handleClose = () => {
- onClose()
- form.reset(initialValues)
- }
- return (
- <SidePanel
- size="large"
- visible={visible}
- header="Create a new role"
- className="mr-0 transform transition-all duration-300 ease-in-out"
- loading={false}
- onCancel={handleClose}
- customFooter={
- <div className="flex w-full justify-end space-x-3 border-t border-default px-3 py-4">
- <FormActions
- form={formId}
- isSubmitting={isCreating}
- hasChanges={form.formState.isDirty}
- handleReset={handleClose}
- />
- </div>
- }
- >
- <Form {...form}>
- <form
- id={formId}
- className="grid gap-6 w-full px-8 py-8"
- onSubmit={form.handleSubmit(onSubmit)}
- >
- <FormField
- control={form.control}
- name="name"
- render={({ field }) => (
- <FormItem className="grid gap-2 md:grid md:grid-cols-12 space-y-0">
- <FormLabel className="flex flex-col space-y-2 col-span-4 text-sm justify-center text-foreground-light">
- Name
- </FormLabel>
- <FormControl className="col-span-8">
- <Input {...field} className="w-full" />
- </FormControl>
- <FormMessage className="col-start-5 col-span-8" />
- </FormItem>
- )}
- />
- <div className="grid gap-2 mt-4 md:grid md:grid-cols-12">
- <div className="col-span-4">
- <FormLabel className="flex flex-col space-y-2 col-span-4 text-sm justify-center text-foreground-light">
- Role privileges
- </FormLabel>
- </div>
- <div className="col-span-8 grid gap-4">
- {(Object.keys(ROLE_PERMISSIONS) as (keyof typeof ROLE_PERMISSIONS)[])
- .filter((permissionKey) => ROLE_PERMISSIONS[permissionKey].grant_by_dashboard)
- .map((permissionKey) => {
- const permission = ROLE_PERMISSIONS[permissionKey]
- return (
- <FormField
- key={permissionKey}
- control={form.control}
- name={permissionKey}
- render={({ field }) => (
- <FormItem className="grid gap-2 md:grid md:grid-cols-12 space-y-0">
- <FormControl className="col-span-8 flex items-center gap-4">
- <div className="w-full text-sm">
- <Switch checked={field.value} onCheckedChange={field.onChange} />
- <FormLabel>{permission.description}</FormLabel>
- </div>
- </FormControl>
- <FormMessage className="col-start-5 col-span-8" />
- </FormItem>
- )}
- />
- )
- })}
- <SidePanel.Separator />
- <div className="grid gap-4">
- <p className="text-sm">These privileges cannot be granted via the Dashboard:</p>
- {(Object.keys(ROLE_PERMISSIONS) as (keyof typeof ROLE_PERMISSIONS)[])
- .filter((permissionKey) => !ROLE_PERMISSIONS[permissionKey].grant_by_dashboard)
- .map((permissionKey) => {
- const permission = ROLE_PERMISSIONS[permissionKey]
- return (
- <FormField
- key={permissionKey}
- control={form.control}
- name={permissionKey}
- render={({ field }) => (
- <FormItem className="space-y-0 opacity-70">
- <FormControl className="flex items-center gap-4">
- <div className="w-full text-sm">
- <Switch
- checked={field.value}
- onCheckedChange={field.onChange}
- disabled
- aria-readonly
- />
- <FormLabel>{permission.description}</FormLabel>
- </div>
- </FormControl>
- <FormMessage className="col-start-5 col-span-8" />
- </FormItem>
- )}
- />
- )
- })}
- </div>
- </div>
- </div>
- </form>
- </Form>
- </SidePanel>
- )
- }
|