Skip to content

Commit

Permalink
feat: create auth page for login and register
Browse files Browse the repository at this point in the history
  • Loading branch information
andostronaut committed Sep 12, 2024
1 parent dc85666 commit 7a20181
Show file tree
Hide file tree
Showing 21 changed files with 346 additions and 176 deletions.
File renamed without changes.
11 changes: 11 additions & 0 deletions app/(auth)/auth/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { AuthLayout } from '@/components/layouts/auth-layout'

const Layout = ({
children
}: Readonly<{
children: React.ReactNode
}>) => {
return <AuthLayout>{children}</AuthLayout>
}

export default Layout
187 changes: 187 additions & 0 deletions app/(auth)/auth/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
'use client'

import { useState } from 'react'
import { AlertCircle } from 'lucide-react'
import { useForm } from 'react-hook-form'

import { z } from 'zod'
import { zodResolver } from '@hookform/resolvers/zod'

import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Switch } from '@/components/ui/switch'
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle
} from '@/components/ui/card'

const loginSchema = z.object({
email: z.string().email({ message: 'Invalid email address' }),
password: z.string().min(8, { message: 'Password must be at least 8 characters long' })
})

const registerSchema = loginSchema
.extend({
name: z.string().min(2, { message: 'Name must be at least 2 characters long' }),
username: z.string().min(3, { message: 'Username must be at least 3 characters long' }),
confirmPassword: z.string().min(8, { message: 'Confirm Password is required' })
})
.refine((data) => data.password === data.confirmPassword, {
message: "Passwords don't match",
path: ['confirmPassword']
})

type LoginFormData = z.infer<typeof loginSchema>
type RegisterFormData = z.infer<typeof registerSchema>

const Page = () => {
const [isLogin, setIsLogin] = useState(true)

const {
register,
handleSubmit,
formState: { errors },
reset
} = useForm<RegisterFormData>({
resolver: zodResolver(isLogin ? loginSchema : registerSchema)
})

const onSubmit = (data: LoginFormData | RegisterFormData) => {
// Here you would typically handle the login or registration logic
console.log(isLogin ? 'Logging in...' : 'Registering...', data)
// Reset the form after submission
reset()
}

return (
<div>
<section className='w-full h-full py-12 md:py-24 lg:py-32 xl:py-48'>
<div className='container px-4 md:px-6'>
<div className='flex justify-center items-center'>
<Card className='w-full max-w-md'>
<CardHeader>
<CardTitle>{isLogin ? 'Login' : 'Register'}</CardTitle>
<CardDescription>
{isLogin ? 'Enter your credentials to login' : 'Create a new account'}
</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit(onSubmit)} className='space-y-4'>
{!isLogin && (
<>
<div className='space-y-2'>
<Label htmlFor='name'>Name</Label>
<Input
id='name'
placeholder='Enter your name'
{...register('name')}
aria-invalid={errors.name ? 'true' : 'false'}
/>
{errors.name && (
<p className='text-red-500 text-sm flex items-center mt-1'>
<AlertCircle className='w-4 h-4 mr-2' />
{errors.name.message}
</p>
)}
</div>
<div className='space-y-2'>
<Label htmlFor='username'>Username</Label>
<Input
id='username'
placeholder='Choose a username'
{...register('username')}
aria-invalid={errors.username ? 'true' : 'false'}
/>
{errors.username && (
<p className='text-red-500 text-sm flex items-center mt-1'>
<AlertCircle className='w-4 h-4 mr-2' />
{errors.username.message}
</p>
)}
</div>
</>
)}
<div className='space-y-2'>
<Label htmlFor='email'>Email</Label>
<Input
id='email'
type='email'
placeholder='Enter your email'
{...register('email')}
aria-invalid={errors.email ? 'true' : 'false'}
/>
{errors.email && (
<p className='text-red-500 text-sm flex items-center mt-1'>
<AlertCircle className='w-4 h-4 mr-2' />
{errors.email.message}
</p>
)}
</div>
<div className='space-y-2'>
<Label htmlFor='password'>Password</Label>
<Input
id='password'
type='password'
placeholder='Enter your password'
{...register('password')}
aria-invalid={errors.password ? 'true' : 'false'}
/>
{errors.password && (
<p className='text-red-500 text-sm flex items-center mt-1'>
<AlertCircle className='w-4 h-4 mr-2' />
{errors.password.message}
</p>
)}
</div>
{!isLogin && (
<div className='space-y-2'>
<Label htmlFor='name'>Confirm Password</Label>
<Input
id='confirmPassword'
type='password'
placeholder='Enter your confirm password'
{...register('confirmPassword')}
aria-invalid={errors.confirmPassword ? 'true' : 'false'}
/>
{errors.confirmPassword && (
<p className='text-red-500 text-sm flex items-center mt-1'>
<AlertCircle className='w-4 h-4 mr-2' />
{errors.confirmPassword.message}
</p>
)}
</div>
)}
<div className='flex items-center space-x-2'>
<Switch
id='login-register'
checked={!isLogin}
onCheckedChange={() => {
setIsLogin(!isLogin)
reset()
}}
/>
<Label htmlFor='login-register'>
{isLogin ? 'Switch to Register' : 'Switch to Login'}
</Label>
</div>
</form>
</CardContent>
<CardFooter>
<Button className='w-full' onClick={handleSubmit(onSubmit)}>
{isLogin ? 'Login' : 'Register'}
</Button>
</CardFooter>
</Card>
</div>
</div>
</section>
</div>
)
}

export default Page
6 changes: 3 additions & 3 deletions app/(dashboard)/dashboard/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { Metadata } from 'next'

import { Header } from '@/components/dashboard/header'
import { Sidebar } from '@/components/dashboard/sidebar'
import { Footer } from '@/components/dashboard/footer'
import { Header } from '@/components/modules/dashboard/header'
import { Sidebar } from '@/components/modules/dashboard/sidebar'
import { Footer } from '@/components/modules/dashboard/footer'
import { Toaster } from '@/components/ui/sonner'

export const metadata: Metadata = {
Expand Down
20 changes: 0 additions & 20 deletions app/(root)/layout.tsx

This file was deleted.

89 changes: 0 additions & 89 deletions app/(root)/page.tsx

This file was deleted.

76 changes: 76 additions & 0 deletions components/layouts/auth-layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
'use client'

import Link from 'next/link'
import { GithubIcon } from 'lucide-react'

import { DrowserStudioLogo } from '@/components/svg/drowser-studio-logo'
import { ToggleTheme } from '@/components/shared/toogle-theme'
import { KinotioLogo } from '@/components/svg/kinotio-logo'

import { Toaster } from '@/components/ui/sonner'

import { getCurrentYear } from '@/lib/utils'

const AuthLayout = ({
children
}: Readonly<{
children: React.ReactNode
}>) => {
return (
<>
<div className='fixed top-0 w-full'>
<Header />
</div>
<main className='mt-[100px] mb-[100px]'>{children}</main>
<Footer />
<Toaster position='bottom-center' />
</>
)
}

const Header = () => {
return (
<header className='w-full pt-4 pb-2 px-6 flex items-center justify-between bg-white dark:dark:bg-slate-950'>
<Link className='flex items-center justify-center' href='/'>
<DrowserStudioLogo width={200} height={50} />
</Link>
<nav className='ml-auto gap-3 sm:gap-3 flex justify-center items-center'>
<div className='ml-auto gap-3 sm:gap-3 flex justify-center items-center'>
<ToggleTheme />
<Link href={'https://github.com/kinotio/gelda'} target='_blank'>
<GithubIcon size={20} />
</Link>
</div>
</nav>
</header>
)
}

const Footer = () => {
return (
<footer className='fixed bottom-0 flex flex-col gap-2 sm:flex-row py-2 w-full shrink-0 items-center px-4 md:px-6 border-t bg-white dark:dark:bg-slate-950'>
<div>
<h3>
Copyright
{` © ${getCurrentYear()} `}
<Link
target='_blank'
href='https://kinotio.io'
className='text-primary transition-all border-primary hover:border-b-2 ml-1'
>
Developed by Kinotio. All rights reserved.
</Link>
</h3>
</div>

<nav className='sm:ml-auto flex gap-2 items-center'>
<span className='text-sm font-semibold'>By</span>
<Link href={'https://github.com/kinotio'}>
<KinotioLogo width={100} height={50} />
</Link>
</nav>
</footer>
)
}

export { AuthLayout }
Loading

0 comments on commit 7a20181

Please sign in to comment.