|
@@ -0,0 +1,161 @@
|
|
|
|
|
+import { HTMLAttributes, useState } from 'react'
|
|
|
|
|
+import { cn } from '@/lib/utils'
|
|
|
|
|
+import { zodResolver } from '@hookform/resolvers/zod'
|
|
|
|
|
+import { useForm } from 'react-hook-form'
|
|
|
|
|
+import { z } from 'zod'
|
|
|
|
|
+import { Button } from '@/components/ui/button'
|
|
|
|
|
+import {
|
|
|
|
|
+ Form,
|
|
|
|
|
+ FormControl,
|
|
|
|
|
+ FormField,
|
|
|
|
|
+ FormItem,
|
|
|
|
|
+ FormLabel,
|
|
|
|
|
+ FormMessage,
|
|
|
|
|
+} from '@/components/ui/form'
|
|
|
|
|
+import { Input } from '@/components/ui/input'
|
|
|
|
|
+import {
|
|
|
|
|
+ IconBrandFacebook,
|
|
|
|
|
+ IconBrandGithub,
|
|
|
|
|
+ IconLoader2,
|
|
|
|
|
+} from '@tabler/icons-react'
|
|
|
|
|
+
|
|
|
|
|
+interface SignUpFormProps extends HTMLAttributes<HTMLDivElement> {}
|
|
|
|
|
+
|
|
|
|
|
+const formSchema = z
|
|
|
|
|
+ .object({
|
|
|
|
|
+ email: z
|
|
|
|
|
+ .string()
|
|
|
|
|
+ .min(1, { message: 'Please enter your email' })
|
|
|
|
|
+ .email({ message: 'Invalid email address' }),
|
|
|
|
|
+ password: z
|
|
|
|
|
+ .string()
|
|
|
|
|
+ .min(1, {
|
|
|
|
|
+ message: 'Please enter your password',
|
|
|
|
|
+ })
|
|
|
|
|
+ .min(7, {
|
|
|
|
|
+ message: 'Password must be at least 7 characters long',
|
|
|
|
|
+ }),
|
|
|
|
|
+ confirmPassword: z.string(),
|
|
|
|
|
+ })
|
|
|
|
|
+ .refine((data) => data.password === data.confirmPassword, {
|
|
|
|
|
+ message: "Passwords don't match.",
|
|
|
|
|
+ path: ['confirmPassword'],
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+export function SignUpForm({ className, ...props }: SignUpFormProps) {
|
|
|
|
|
+ const [isLoading, setIsLoading] = useState(false)
|
|
|
|
|
+
|
|
|
|
|
+ const form = useForm<z.infer<typeof formSchema>>({
|
|
|
|
|
+ resolver: zodResolver(formSchema),
|
|
|
|
|
+ defaultValues: {
|
|
|
|
|
+ email: '',
|
|
|
|
|
+ password: '',
|
|
|
|
|
+ confirmPassword: '',
|
|
|
|
|
+ },
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ function onSubmit(data: z.infer<typeof formSchema>) {
|
|
|
|
|
+ setIsLoading(true)
|
|
|
|
|
+ console.log(data)
|
|
|
|
|
+
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ setIsLoading(false)
|
|
|
|
|
+ }, 3000)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <div className={cn('grid gap-6', className)} {...props}>
|
|
|
|
|
+ <Form {...form}>
|
|
|
|
|
+ <form onSubmit={form.handleSubmit(onSubmit)}>
|
|
|
|
|
+ <div className='grid gap-2'>
|
|
|
|
|
+ <FormField
|
|
|
|
|
+ control={form.control}
|
|
|
|
|
+ name='email'
|
|
|
|
|
+ render={({ field }) => (
|
|
|
|
|
+ <FormItem className='space-y-1'>
|
|
|
|
|
+ <FormLabel>Email</FormLabel>
|
|
|
|
|
+ <FormControl>
|
|
|
|
|
+ <Input placeholder='name@example.com' {...field} />
|
|
|
|
|
+ </FormControl>
|
|
|
|
|
+ <FormMessage />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ )}
|
|
|
|
|
+ />
|
|
|
|
|
+ <FormField
|
|
|
|
|
+ control={form.control}
|
|
|
|
|
+ name='password'
|
|
|
|
|
+ render={({ field }) => (
|
|
|
|
|
+ <FormItem className='space-y-1'>
|
|
|
|
|
+ <FormLabel>Password</FormLabel>
|
|
|
|
|
+ <FormControl>
|
|
|
|
|
+ <Input placeholder='********' {...field} type='password' />
|
|
|
|
|
+ </FormControl>
|
|
|
|
|
+ <FormMessage />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ )}
|
|
|
|
|
+ />
|
|
|
|
|
+ <FormField
|
|
|
|
|
+ control={form.control}
|
|
|
|
|
+ name='confirmPassword'
|
|
|
|
|
+ render={({ field }) => (
|
|
|
|
|
+ <FormItem className='space-y-1'>
|
|
|
|
|
+ <FormLabel>Confirm Password</FormLabel>
|
|
|
|
|
+ <FormControl>
|
|
|
|
|
+ <Input placeholder='********' {...field} type='password' />
|
|
|
|
|
+ </FormControl>
|
|
|
|
|
+ <FormMessage />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ )}
|
|
|
|
|
+ />
|
|
|
|
|
+ <Button className='mt-2' disabled={isLoading}>
|
|
|
|
|
+ {isLoading && (
|
|
|
|
|
+ <IconLoader2 className='mr-2 h-4 w-4 animate-spin' />
|
|
|
|
|
+ )}
|
|
|
|
|
+ Create Account
|
|
|
|
|
+ </Button>
|
|
|
|
|
+
|
|
|
|
|
+ <div className='relative my-2'>
|
|
|
|
|
+ <div className='absolute inset-0 flex items-center'>
|
|
|
|
|
+ <span className='w-full border-t' />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className='relative flex justify-center text-xs uppercase'>
|
|
|
|
|
+ <span className='bg-background px-2 text-muted-foreground'>
|
|
|
|
|
+ Or continue with
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div className='flex items-center gap-2'>
|
|
|
|
|
+ <Button
|
|
|
|
|
+ variant='outline'
|
|
|
|
|
+ className='w-full'
|
|
|
|
|
+ type='button'
|
|
|
|
|
+ disabled={isLoading}
|
|
|
|
|
+ >
|
|
|
|
|
+ {isLoading ? (
|
|
|
|
|
+ <IconLoader2 className='mr-2 h-4 w-4 animate-spin' />
|
|
|
|
|
+ ) : (
|
|
|
|
|
+ <IconBrandGithub className='mr-2 h-4 w-4' />
|
|
|
|
|
+ )}{' '}
|
|
|
|
|
+ GitHub
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ <Button
|
|
|
|
|
+ variant='outline'
|
|
|
|
|
+ className='w-full'
|
|
|
|
|
+ type='button'
|
|
|
|
|
+ disabled={isLoading}
|
|
|
|
|
+ >
|
|
|
|
|
+ {isLoading ? (
|
|
|
|
|
+ <IconLoader2 className='mr-2 h-4 w-4 animate-spin' />
|
|
|
|
|
+ ) : (
|
|
|
|
|
+ <IconBrandFacebook className='mr-2 h-4 w-4' />
|
|
|
|
|
+ )}{' '}
|
|
|
|
|
+ Facebook
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </form>
|
|
|
|
|
+ </Form>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ )
|
|
|
|
|
+}
|