CreateUserModal.tsx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import { zodResolver } from '@hookform/resolvers/zod'
  2. import { PermissionAction } from '@supabase/shared-types/out/constants'
  3. import { useParams } from 'common'
  4. import { Lock, Mail } from 'lucide-react'
  5. import { SubmitHandler, useForm } from 'react-hook-form'
  6. import { toast } from 'sonner'
  7. import {
  8. Button,
  9. Checkbox,
  10. Dialog,
  11. DialogContent,
  12. DialogHeader,
  13. DialogSectionSeparator,
  14. DialogTitle,
  15. Form,
  16. FormControl,
  17. FormField,
  18. FormItem,
  19. FormLabel,
  20. FormMessage,
  21. Input,
  22. } from 'ui'
  23. import * as z from 'zod'
  24. import { useUserCreateMutation } from '@/data/auth/user-create-mutation'
  25. import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
  26. export type CreateUserModalProps = {
  27. visible: boolean
  28. setVisible: (visible: boolean) => void
  29. }
  30. const CreateUserFormSchema = z.object({
  31. email: z.string().min(1, 'Email is required').email('Must be a valid email address'),
  32. password: z.string().min(1, 'Password is required'),
  33. autoConfirmUser: z.boolean(),
  34. })
  35. const CreateUserModal = ({ visible, setVisible }: CreateUserModalProps) => {
  36. const { ref: projectRef } = useParams()
  37. const { can: canCreateUsers } = useAsyncCheckPermissions(
  38. PermissionAction.AUTH_EXECUTE,
  39. 'create_user'
  40. )
  41. const { mutate: createUser, isPending: isCreatingUser } = useUserCreateMutation({
  42. onSuccess(res) {
  43. toast.success(`Successfully created user: ${res.email}`)
  44. form.reset({ email: '', password: '', autoConfirmUser: true })
  45. setVisible(false)
  46. },
  47. })
  48. const onCreateUser: SubmitHandler<z.infer<typeof CreateUserFormSchema>> = async (values) => {
  49. if (!projectRef) return console.error('Project ref is required')
  50. createUser({ projectRef, user: values })
  51. }
  52. const form = useForm<z.infer<typeof CreateUserFormSchema>>({
  53. resolver: zodResolver(CreateUserFormSchema as any),
  54. defaultValues: { email: '', password: '', autoConfirmUser: true },
  55. })
  56. return (
  57. <Dialog open={visible} onOpenChange={setVisible}>
  58. <DialogContent size="small">
  59. <DialogHeader>
  60. <DialogTitle>Create a new user</DialogTitle>
  61. </DialogHeader>
  62. <DialogSectionSeparator />
  63. <Form {...form}>
  64. <form
  65. id="create-user"
  66. className="flex flex-col gap-y-4 p-6"
  67. onSubmit={form.handleSubmit(onCreateUser)}
  68. >
  69. <FormField
  70. name="email"
  71. control={form.control}
  72. render={({ field }) => (
  73. <FormItem className="flex flex-col gap-1">
  74. <FormLabel>Email address</FormLabel>
  75. <FormControl>
  76. <div className="items-center relative">
  77. <Mail
  78. size={18}
  79. className="absolute left-2 top-1/2 transform -translate-y-1/2"
  80. strokeWidth={1.5}
  81. />
  82. <Input
  83. autoFocus
  84. {...field}
  85. autoComplete="off"
  86. type="email"
  87. name="email"
  88. placeholder="user@example.com"
  89. disabled={isCreatingUser}
  90. className="pl-8"
  91. />
  92. </div>
  93. </FormControl>
  94. <FormMessage />
  95. </FormItem>
  96. )}
  97. />
  98. <FormField
  99. name="password"
  100. control={form.control}
  101. render={({ field }) => (
  102. <FormItem className="flex flex-col gap-1">
  103. <FormLabel>User Password</FormLabel>
  104. <FormControl>
  105. <div className="items-center relative">
  106. <Lock
  107. size={18}
  108. className="absolute left-2 top-1/2 transform -translate-y-1/2"
  109. strokeWidth={1.5}
  110. />
  111. <Input
  112. {...field}
  113. autoComplete="new-password"
  114. type="password"
  115. name="password"
  116. placeholder="&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;"
  117. disabled={isCreatingUser}
  118. className="pl-8"
  119. />
  120. </div>
  121. </FormControl>
  122. <FormMessage />
  123. </FormItem>
  124. )}
  125. />
  126. <FormField
  127. name="autoConfirmUser"
  128. control={form.control}
  129. render={({ field }) => (
  130. <FormItem className="flex items-center gap-x-2">
  131. <FormControl>
  132. <Checkbox
  133. checked={field.value}
  134. onCheckedChange={(value) => field.onChange(value)}
  135. />
  136. </FormControl>
  137. <FormLabel>Auto confirm user?</FormLabel>
  138. </FormItem>
  139. )}
  140. />
  141. <FormLabel>
  142. <p className="text-sm text-foreground-lighter">
  143. A confirmation email will not be sent when creating a user via this form.
  144. </p>
  145. </FormLabel>
  146. <Button
  147. block
  148. size="small"
  149. htmlType="submit"
  150. loading={isCreatingUser}
  151. disabled={!canCreateUsers || isCreatingUser}
  152. >
  153. Create user
  154. </Button>
  155. </form>
  156. </Form>
  157. </DialogContent>
  158. </Dialog>
  159. )
  160. }
  161. export default CreateUserModal