sign-up-form.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import { HTMLAttributes, useState } from 'react'
  2. import { cn } from '@/lib/utils'
  3. import { zodResolver } from '@hookform/resolvers/zod'
  4. import { useForm } from 'react-hook-form'
  5. import { z } from 'zod'
  6. import { Button } from '@/components/ui/button'
  7. import {
  8. Form,
  9. FormControl,
  10. FormField,
  11. FormItem,
  12. FormLabel,
  13. FormMessage,
  14. } from '@/components/ui/form'
  15. import { Input } from '@/components/ui/input'
  16. import {
  17. IconBrandFacebook,
  18. IconBrandGithub,
  19. IconLoader2,
  20. } from '@tabler/icons-react'
  21. import { PasswordInput } from '@/components/password-input'
  22. interface SignUpFormProps extends HTMLAttributes<HTMLDivElement> {}
  23. const formSchema = z
  24. .object({
  25. email: z
  26. .string()
  27. .min(1, { message: 'Please enter your email' })
  28. .email({ message: 'Invalid email address' }),
  29. password: z
  30. .string()
  31. .min(1, {
  32. message: 'Please enter your password',
  33. })
  34. .min(7, {
  35. message: 'Password must be at least 7 characters long',
  36. }),
  37. confirmPassword: z.string(),
  38. })
  39. .refine((data) => data.password === data.confirmPassword, {
  40. message: "Passwords don't match.",
  41. path: ['confirmPassword'],
  42. })
  43. export function SignUpForm({ className, ...props }: SignUpFormProps) {
  44. const [isLoading, setIsLoading] = useState(false)
  45. const form = useForm<z.infer<typeof formSchema>>({
  46. resolver: zodResolver(formSchema),
  47. defaultValues: {
  48. email: '',
  49. password: '',
  50. confirmPassword: '',
  51. },
  52. })
  53. function onSubmit(data: z.infer<typeof formSchema>) {
  54. setIsLoading(true)
  55. console.log(data)
  56. setTimeout(() => {
  57. setIsLoading(false)
  58. }, 3000)
  59. }
  60. return (
  61. <div className={cn('grid gap-6', className)} {...props}>
  62. <Form {...form}>
  63. <form onSubmit={form.handleSubmit(onSubmit)}>
  64. <div className='grid gap-2'>
  65. <FormField
  66. control={form.control}
  67. name='email'
  68. render={({ field }) => (
  69. <FormItem className='space-y-1'>
  70. <FormLabel>Email</FormLabel>
  71. <FormControl>
  72. <Input placeholder='name@example.com' {...field} />
  73. </FormControl>
  74. <FormMessage />
  75. </FormItem>
  76. )}
  77. />
  78. <FormField
  79. control={form.control}
  80. name='password'
  81. render={({ field }) => (
  82. <FormItem className='space-y-1'>
  83. <FormLabel>Password</FormLabel>
  84. <FormControl>
  85. <PasswordInput placeholder='********' {...field} />
  86. </FormControl>
  87. <FormMessage />
  88. </FormItem>
  89. )}
  90. />
  91. <FormField
  92. control={form.control}
  93. name='confirmPassword'
  94. render={({ field }) => (
  95. <FormItem className='space-y-1'>
  96. <FormLabel>Confirm Password</FormLabel>
  97. <FormControl>
  98. <PasswordInput placeholder='********' {...field} />
  99. </FormControl>
  100. <FormMessage />
  101. </FormItem>
  102. )}
  103. />
  104. <Button className='mt-2' disabled={isLoading}>
  105. {isLoading && (
  106. <IconLoader2 className='mr-2 h-4 w-4 animate-spin' />
  107. )}
  108. Create Account
  109. </Button>
  110. <div className='relative my-2'>
  111. <div className='absolute inset-0 flex items-center'>
  112. <span className='w-full border-t' />
  113. </div>
  114. <div className='relative flex justify-center text-xs uppercase'>
  115. <span className='bg-background px-2 text-muted-foreground'>
  116. Or continue with
  117. </span>
  118. </div>
  119. </div>
  120. <div className='flex items-center gap-2'>
  121. <Button
  122. variant='outline'
  123. className='w-full'
  124. type='button'
  125. disabled={isLoading}
  126. >
  127. {isLoading ? (
  128. <IconLoader2 className='mr-2 h-4 w-4 animate-spin' />
  129. ) : (
  130. <IconBrandGithub className='mr-2 h-4 w-4' />
  131. )}{' '}
  132. GitHub
  133. </Button>
  134. <Button
  135. variant='outline'
  136. className='w-full'
  137. type='button'
  138. disabled={isLoading}
  139. >
  140. {isLoading ? (
  141. <IconLoader2 className='mr-2 h-4 w-4 animate-spin' />
  142. ) : (
  143. <IconBrandFacebook className='mr-2 h-4 w-4' />
  144. )}{' '}
  145. Facebook
  146. </Button>
  147. </div>
  148. </div>
  149. </form>
  150. </Form>
  151. </div>
  152. )
  153. }