added tons of features
This commit is contained in:
18
components/auth/auth-header.tsx
Normal file
18
components/auth/auth-header.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import Logo from '@/components/shared/home/logo'
|
||||
|
||||
interface HeaderProps {
|
||||
label: string
|
||||
title: string
|
||||
}
|
||||
|
||||
const AuthHeader = ({title, label}: HeaderProps) => {
|
||||
return (
|
||||
<div className='flex w-full flex-col items-center justify-center gap-y-4'>
|
||||
<Logo />
|
||||
<h1 className='text-3xl font-semibold'>{title}</h1>
|
||||
<p className='text-sm text-muted-foreground'>{label}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AuthHeader
|
||||
16
components/auth/back-button.tsx
Normal file
16
components/auth/back-button.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import Link from 'next/link'
|
||||
|
||||
import {Button} from '@/components/ui/button'
|
||||
|
||||
interface BackButtonProps {
|
||||
label: string
|
||||
href: string
|
||||
}
|
||||
|
||||
export const BackButton = ({label, href}: BackButtonProps) => {
|
||||
return (
|
||||
<Button variant='link' className='w-full font-normal' size='sm' asChild>
|
||||
<Link href={href}>{label}</Link>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
37
components/auth/card-wrapper.tsx
Normal file
37
components/auth/card-wrapper.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import {ReactNode} from 'react'
|
||||
|
||||
import AuthHeader from './auth-header'
|
||||
import {BackButton} from './back-button'
|
||||
import {Card, CardContent, CardFooter, CardHeader} from '@/components/ui/card'
|
||||
|
||||
interface CardWrapperProps {
|
||||
children: ReactNode
|
||||
headerLabel: string
|
||||
backButtonLabel: string
|
||||
title: string
|
||||
showSocial?: boolean
|
||||
backButtonHref: string
|
||||
}
|
||||
|
||||
const CardWrapper = ({
|
||||
children,
|
||||
headerLabel,
|
||||
backButtonLabel,
|
||||
backButtonHref,
|
||||
title,
|
||||
showSocial
|
||||
}: CardWrapperProps) => {
|
||||
return (
|
||||
<Card className='m-auto shadow-md md:w-1/2 xl:w-1/4'>
|
||||
<CardHeader>
|
||||
<AuthHeader label={headerLabel} title={title} />
|
||||
</CardHeader>
|
||||
<CardContent>{children}</CardContent>
|
||||
<CardFooter>
|
||||
<BackButton label={backButtonLabel} href={backButtonHref} />
|
||||
</CardFooter>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default CardWrapper
|
||||
17
components/auth/form-error.tsx
Normal file
17
components/auth/form-error.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import {ShieldAlert} from 'lucide-react'
|
||||
|
||||
interface FormSuccessProps {
|
||||
message?: string
|
||||
}
|
||||
|
||||
export const FormError = ({message}: FormSuccessProps) => {
|
||||
if (!message) return null
|
||||
return (
|
||||
<div className='flex items-center space-x-4 rounded-lg bg-red-500/30 p-2 text-red-500'>
|
||||
<ShieldAlert size={16} />
|
||||
<p>{message}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FormError
|
||||
15
components/auth/form-success.tsx
Normal file
15
components/auth/form-success.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import {CheckCheckIcon} from 'lucide-react'
|
||||
|
||||
interface FormSuccessProps {
|
||||
message?: string
|
||||
}
|
||||
|
||||
export const FormSuccess = ({message}: FormSuccessProps) => {
|
||||
if (!message) return null
|
||||
return (
|
||||
<div className='flex items-center space-x-4 rounded-lg bg-emerald-500/30 p-2 text-emerald-500'>
|
||||
<CheckCheckIcon className='h-4 w-4' />
|
||||
<p>{message}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
104
components/auth/forms/login-form.tsx
Normal file
104
components/auth/forms/login-form.tsx
Normal file
@@ -0,0 +1,104 @@
|
||||
'use client'
|
||||
|
||||
import {zodResolver} from '@hookform/resolvers/zod'
|
||||
import {useState} from 'react'
|
||||
import {useForm} from 'react-hook-form'
|
||||
import {z} from 'zod'
|
||||
|
||||
import {login} from '@/actions/auth/login'
|
||||
import CardWrapper from '@/components/auth/card-wrapper'
|
||||
import {FormError} from '@/components/auth/form-error'
|
||||
import GoogleLogin from '@/components/auth/google-login'
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage
|
||||
} from '@/components/ui/form'
|
||||
import {LoginSchema} from '@/lib/schemas'
|
||||
import {Button} from '@/ui/button'
|
||||
import {Input} from '@/ui/input'
|
||||
|
||||
export default function LoginForm() {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState('')
|
||||
|
||||
const form = useForm<z.infer<typeof LoginSchema>>({
|
||||
resolver: zodResolver(LoginSchema),
|
||||
defaultValues: {
|
||||
email: '',
|
||||
password: ''
|
||||
}
|
||||
})
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof LoginSchema>) => {
|
||||
setLoading(true)
|
||||
login(data).then(res => {
|
||||
if (res?.error) {
|
||||
setError(res?.error)
|
||||
setLoading(false)
|
||||
} else {
|
||||
setError('')
|
||||
setLoading(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<CardWrapper
|
||||
headerLabel='Create an account'
|
||||
title='Register'
|
||||
backButtonHref='/auth/login'
|
||||
backButtonLabel='Already have an account'
|
||||
showSocial
|
||||
>
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className='m-auto space-y-6'
|
||||
>
|
||||
<div className='space-y-4'>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='email'
|
||||
render={({field}) => (
|
||||
<FormItem>
|
||||
<FormLabel>Email</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder='johndoe@email.com'
|
||||
type='email'
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='password'
|
||||
render={({field}) => (
|
||||
<FormItem>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder='******' type='password' />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<FormError message={error} />
|
||||
<Button type='submit' className='w-full' disabled={loading}>
|
||||
{loading ? 'Loading...' : 'Login'}
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
<GoogleLogin />
|
||||
</CardWrapper>
|
||||
)
|
||||
}
|
||||
133
components/auth/forms/register-form.tsx
Normal file
133
components/auth/forms/register-form.tsx
Normal file
@@ -0,0 +1,133 @@
|
||||
'use client'
|
||||
|
||||
import {zodResolver} from '@hookform/resolvers/zod'
|
||||
import {useState} from 'react'
|
||||
import {useForm} from 'react-hook-form'
|
||||
import {z} from 'zod'
|
||||
|
||||
import {register} from '@/actions/auth/register'
|
||||
import CardWrapper from '@/components/auth/card-wrapper'
|
||||
import {FormError} from '@/components/auth/form-error'
|
||||
import {FormSuccess} from '@/components/auth/form-success'
|
||||
import GoogleLogin from '@/components/auth/google-login'
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage
|
||||
} from '@/components/ui/form'
|
||||
import {RegisterSchema} from '@/lib/schemas'
|
||||
import {Button} from '@/ui/button'
|
||||
import {Input} from '@/ui/input'
|
||||
|
||||
export default function RegisterForm() {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState('')
|
||||
const [success, setSuccess] = useState('')
|
||||
|
||||
const form = useForm<z.infer<typeof RegisterSchema>>({
|
||||
resolver: zodResolver(RegisterSchema),
|
||||
defaultValues: {
|
||||
email: '',
|
||||
name: '',
|
||||
password: '',
|
||||
passwordConfirmation: ''
|
||||
}
|
||||
})
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof RegisterSchema>) => {
|
||||
setLoading(true)
|
||||
register(data).then(res => {
|
||||
if (res.error) {
|
||||
setError(res.error)
|
||||
setLoading(false)
|
||||
}
|
||||
if (res.success) {
|
||||
setError('')
|
||||
setSuccess(res.success)
|
||||
setLoading(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<CardWrapper
|
||||
headerLabel='Create an account'
|
||||
title='Register'
|
||||
backButtonHref='/auth/login'
|
||||
backButtonLabel='Already have an account'
|
||||
showSocial
|
||||
>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className='space-y-6'>
|
||||
<div className='space-y-4'>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='email'
|
||||
render={({field}) => (
|
||||
<FormItem>
|
||||
<FormLabel>Email</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder='johndoe@email.com'
|
||||
type='email'
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='name'
|
||||
render={({field}) => (
|
||||
<FormItem>
|
||||
<FormLabel>Name</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder='John Doe' />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='password'
|
||||
render={({field}) => (
|
||||
<FormItem>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder='******' type='password' />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='passwordConfirmation'
|
||||
render={({field}) => (
|
||||
<FormItem>
|
||||
<FormLabel>Confirm Password</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder='******' type='password' />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<FormSuccess message={success} />
|
||||
<FormError message={error} />
|
||||
<Button type='submit' className='w-full' disabled={loading}>
|
||||
{loading ? 'Loading...' : 'Register'}
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
{/*<GoogleLogin />*/}
|
||||
</CardWrapper>
|
||||
)
|
||||
}
|
||||
14
components/auth/forms/sign-out-button.tsx
Normal file
14
components/auth/forms/sign-out-button.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import {signOut} from '@/auth'
|
||||
|
||||
export function SignOutButton() {
|
||||
return (
|
||||
<form
|
||||
action={async () => {
|
||||
'use server'
|
||||
await signOut()
|
||||
}}
|
||||
>
|
||||
<button type='submit'>Sign Out</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
29
components/auth/google-login.tsx
Normal file
29
components/auth/google-login.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
'use client'
|
||||
|
||||
import {useActionState} from 'react'
|
||||
|
||||
import {googleAuthenticate} from '@/actions/auth/google-login'
|
||||
import GoogleIcon from '@/components/shared/icons/google'
|
||||
import {Button} from '@/ui/button'
|
||||
|
||||
const GoogleLogin = () => {
|
||||
const [errorMsgGoogle, dispatchGoogle] = useActionState(
|
||||
googleAuthenticate,
|
||||
undefined
|
||||
) //googleAuthenticate hook
|
||||
|
||||
return (
|
||||
<form className='mt-4 flex' action={dispatchGoogle}>
|
||||
<Button
|
||||
variant={'outline'}
|
||||
className='flex w-full flex-row items-center gap-3'
|
||||
>
|
||||
<GoogleIcon />
|
||||
Google Sign In
|
||||
</Button>
|
||||
<p>{errorMsgGoogle}</p>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
export default GoogleLogin
|
||||
Reference in New Issue
Block a user