Kaynağa Gözat

feat: add sign-up page

satnaing 2 yıl önce
ebeveyn
işleme
9f5f5fc1e6

+ 8 - 1
src/data/sidelinks.tsx

@@ -4,6 +4,7 @@ import {
   IconHexagonNumber1,
   IconHexagonNumber2,
   IconHexagonNumber3,
+  IconHexagonNumber4,
   IconLayoutDashboard,
   IconMessages,
   IconRouteAltLeft,
@@ -54,11 +55,17 @@ export const sidelinks: SideLink[] = [
         href: '/sign-in-2',
         icon: <IconHexagonNumber2 size={18} />,
       },
+      {
+        title: 'Sign Up',
+        label: '',
+        href: '/sign-up',
+        icon: <IconHexagonNumber3 size={18} />,
+      },
       {
         title: 'Forgot Password',
         label: '',
         href: '/forgot-password',
-        icon: <IconHexagonNumber3 size={18} />,
+        icon: <IconHexagonNumber4 size={18} />,
       },
     ],
   },

+ 161 - 0
src/pages/auth/components/sign-up-form.tsx

@@ -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>
+  )
+}

+ 64 - 0
src/pages/auth/sign-up.tsx

@@ -0,0 +1,64 @@
+import { Card } from '@/components/ui/card'
+import { SignUpForm } from './components/sign-up-form'
+import { Link } from 'react-router-dom'
+
+export default function SignUp() {
+  return (
+    <>
+      <div className='container grid h-svh flex-col items-center justify-center bg-primary-foreground lg:max-w-none lg:px-0'>
+        <div className='mx-auto flex w-full flex-col justify-center space-y-2 sm:w-[480px] lg:p-8'>
+          <div className='mb-4 flex items-center justify-center'>
+            <svg
+              xmlns='http://www.w3.org/2000/svg'
+              viewBox='0 0 24 24'
+              fill='none'
+              stroke='currentColor'
+              strokeWidth='2'
+              strokeLinecap='round'
+              strokeLinejoin='round'
+              className='mr-2 h-6 w-6'
+            >
+              <path d='M15 6v12a3 3 0 1 0 3-3H6a3 3 0 1 0 3 3V6a3 3 0 1 0-3 3h12a3 3 0 1 0-3-3' />
+            </svg>
+            <h1 className='text-xl font-medium'>Admin Dashboard</h1>
+          </div>
+          <Card className='p-6'>
+            <div className='mb-2 flex flex-col space-y-2 text-left'>
+              <h1 className='text-lg font-semibold tracking-tight'>
+                Create an account
+              </h1>
+              <p className='text-sm text-muted-foreground'>
+                Enter your email and password to create an account. <br />
+                Already have an account?{' '}
+                <Link
+                  to='/sign-in'
+                  className='underline underline-offset-4 hover:text-primary'
+                >
+                  Sign In
+                </Link>
+              </p>
+            </div>
+            <SignUpForm />
+            <p className='mt-4 px-8 text-center text-sm text-muted-foreground'>
+              By creating an account, you agree to our{' '}
+              <a
+                href='/terms'
+                className='underline underline-offset-4 hover:text-primary'
+              >
+                Terms of Service
+              </a>{' '}
+              and{' '}
+              <a
+                href='/privacy'
+                className='underline underline-offset-4 hover:text-primary'
+              >
+                Privacy Policy
+              </a>
+              .
+            </p>
+          </Card>
+        </div>
+      </div>
+    </>
+  )
+}

+ 2 - 0
src/router.tsx

@@ -5,6 +5,7 @@ import Loader from '@/components/loader'
 const Dashboard = lazy(() => import('@/pages/dashboard'))
 const SignIn = lazy(() => import('@/pages/auth/sign-in'))
 const SignIn2 = lazy(() => import('@/pages/auth/sign-in-2'))
+const SignUp = lazy(() => import('@/pages/auth/sign-up'))
 const ForgotPassword = lazy(() => import('@/pages/auth/forgot-password'))
 
 export default function Router() {
@@ -14,6 +15,7 @@ export default function Router() {
         <Route path='/' element={<Dashboard />} />
         <Route path='/sign-in' element={<SignIn />} />
         <Route path='/sign-in-2' element={<SignIn2 />} />
+        <Route path='/sign-up' element={<SignUp />} />
         <Route path='/forgot-password' element={<ForgotPassword />} />
       </Routes>
     </Suspense>