-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement AuthLayout component for authentication routes and update f…
…orms to use it
- Loading branch information
1 parent
99bf75c
commit b02e41f
Showing
7 changed files
with
296 additions
and
322 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { Divider } from '@repo/ui'; | ||
import { Link } from '@tanstack/react-router'; | ||
|
||
import logo from '@/assets/logo.svg'; | ||
|
||
interface AuthLayoutProps { | ||
title: string; | ||
description: string; | ||
children: React.ReactNode; | ||
} | ||
|
||
const AuthLayout = ({ title, description, children }: AuthLayoutProps) => { | ||
return ( | ||
<section className="ring-stroke-soft-200 shadow-regular-sm mt-24 flex w-full max-w-[480px] flex-col items-center justify-center rounded-3xl bg-white p-6 ring-1 ring-inset"> | ||
<div className="relative flex size-24 shrink-0 items-center justify-center rounded-full backdrop-blur-xl before:absolute before:inset-0 before:rounded-full before:bg-gradient-to-b before:from-neutral-500 before:to-transparent before:opacity-10"> | ||
<div className="bg-bg-white-0 shadow-regular-xs ring-stroke-soft-200 relative z-10 flex size-16 items-center justify-center rounded-full ring-1 ring-inset"> | ||
<Link className="cursor-pointer" to="/"> | ||
<img | ||
src={logo} | ||
alt="Logo" | ||
className="size-8 select-none rounded-none" | ||
/> | ||
</Link> | ||
</div> | ||
</div> | ||
|
||
<h1 className="text-title-h4 mt-3">{title}</h1> | ||
<p className="text-paragraph-md mt-2 text-gray-600">{description}</p> | ||
<Divider.Root | ||
style={{ | ||
background: | ||
'linear-gradient(90deg, currentcolor 4px, transparent 4px) 50% 50% / 8px 1px repeat-x', | ||
}} | ||
className="text-stroke-sub-300 my-8 h-1 w-full before:bg-transparent"></Divider.Root> | ||
|
||
{children} | ||
</section> | ||
); | ||
}; | ||
|
||
export default AuthLayout; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,7 +25,6 @@ import { | |
loginWithEmailAndPasswordInputSchema, | ||
} from '../api/login'; | ||
|
||
import logo from '@/assets/logo.svg'; | ||
import { env } from '@/config/env'; | ||
import { useLogin } from '@/lib/react-query-auth'; | ||
|
||
|
@@ -49,128 +48,101 @@ const LoginForm = ({ onSuccess }: LoginFormProps) => { | |
}; | ||
|
||
return ( | ||
<section className="ring-stroke-soft-200 shadow-regular-sm mt-24 flex w-full max-w-[480px] flex-col items-center justify-center rounded-3xl bg-white p-6 ring-1 ring-inset"> | ||
<div className="relative flex size-24 shrink-0 items-center justify-center rounded-full backdrop-blur-xl before:absolute before:inset-0 before:rounded-full before:bg-gradient-to-b before:from-neutral-500 before:to-transparent before:opacity-10"> | ||
<div className="bg-bg-white-0 shadow-regular-xs ring-stroke-soft-200 relative z-10 flex size-16 items-center justify-center rounded-full ring-1 ring-inset"> | ||
<Link className="cursor-pointer" to="/"> | ||
<img | ||
src={logo} | ||
alt="Logo" | ||
className="size-8 select-none rounded-none" | ||
/> | ||
</Link> | ||
</div> | ||
</div> | ||
|
||
<h1 className="text-title-h4 mt-3">Welcome Back</h1> | ||
<p className="text-paragraph-md mt-2 text-gray-600"> | ||
Enter your credentials to continue where you left off. | ||
</p> | ||
<Divider.Root | ||
style={{ | ||
background: | ||
'linear-gradient(90deg, currentcolor 4px, transparent 4px) 50% 50% / 8px 1px repeat-x', | ||
}} | ||
className="text-stroke-sub-300 my-8 h-1 w-full before:bg-transparent"></Divider.Root> | ||
<div className="w-full px-6"> | ||
<SocialButton.Root | ||
className="w-full" | ||
brand="google" | ||
mode="stroke" | ||
onClick={() => window.open(`${env.API_URL}/auth/google`, '_self')}> | ||
<SocialButton.Icon as={Icons.IconGoogle} /> | ||
Continue with Google | ||
</SocialButton.Root> | ||
<SocialButton.Root | ||
className="mt-4 w-full" | ||
brand="facebook" | ||
mode="stroke" | ||
onClick={() => window.open(`${env.API_URL}/auth/linkedin`, '_self')}> | ||
<SocialButton.Icon as={Icons.IconLinkedin} /> | ||
Continue with LinkedIn | ||
</SocialButton.Root> | ||
|
||
<div className="w-full px-6"> | ||
<SocialButton.Root | ||
className="w-full" | ||
brand="google" | ||
mode="stroke" | ||
onClick={() => window.open(`${env.API_URL}/auth/google`, '_self')}> | ||
<SocialButton.Icon as={Icons.IconGoogle} /> | ||
Continue with Google | ||
</SocialButton.Root> | ||
<SocialButton.Root | ||
className="mt-4 w-full" | ||
brand="facebook" | ||
mode="stroke" | ||
onClick={() => window.open(`${env.API_URL}/auth/linkedin`, '_self')}> | ||
<SocialButton.Icon as={Icons.IconLinkedin} /> | ||
Continue with LinkedIn | ||
</SocialButton.Root> | ||
<Divider.Root className="my-6" variant="line-text"> | ||
OR | ||
</Divider.Root> | ||
|
||
<Divider.Root className="my-6" variant="line-text"> | ||
OR | ||
</Divider.Root> | ||
|
||
<form id="login-form" onSubmit={handleSubmit(onSubmit)}> | ||
<div className="flex flex-col gap-1"> | ||
<Label.Root htmlFor="email"> | ||
Email Address <Label.Asterisk /> | ||
<form id="login-form" onSubmit={handleSubmit(onSubmit)}> | ||
<div className="flex flex-col gap-1"> | ||
<Label.Root htmlFor="email"> | ||
Email Address <Label.Asterisk /> | ||
</Label.Root> | ||
<Input.Root hasError={!!errors.email}> | ||
<Input.Wrapper> | ||
<Input.Icon as={RiMailLine} /> | ||
<Input.Input | ||
id="email" | ||
type="text" | ||
placeholder="[email protected]" | ||
{...register('email')} | ||
/> | ||
</Input.Wrapper> | ||
</Input.Root> | ||
{errors.email && ( | ||
<Hint.Root className="mt-px" hasError> | ||
<Hint.Icon as={RiErrorWarningFill} /> | ||
{errors.email?.message} | ||
</Hint.Root> | ||
)} | ||
</div> | ||
<div className="mt-4 flex flex-col gap-1"> | ||
<div className="flex justify-between"> | ||
<Label.Root htmlFor="password"> | ||
Password <Label.Asterisk /> | ||
</Label.Root> | ||
<Input.Root hasError={!!errors.email}> | ||
<Input.Wrapper> | ||
<Input.Icon as={RiMailLine} /> | ||
<Input.Input | ||
id="email" | ||
type="text" | ||
placeholder="[email protected]" | ||
{...register('email')} | ||
/> | ||
</Input.Wrapper> | ||
</Input.Root> | ||
{errors.email && ( | ||
<Hint.Root className="mt-px" hasError> | ||
<Hint.Icon as={RiErrorWarningFill} /> | ||
{errors.email?.message} | ||
</Hint.Root> | ||
)} | ||
<LinkButton.Root variant="gray">Forget password?</LinkButton.Root> | ||
</div> | ||
<div className="mt-4 flex flex-col gap-1"> | ||
<div className="flex justify-between"> | ||
<Label.Root htmlFor="password"> | ||
Password <Label.Asterisk /> | ||
</Label.Root> | ||
<LinkButton.Root variant="gray">Forget password?</LinkButton.Root> | ||
</div> | ||
|
||
<Input.Root hasError={!!errors.password}> | ||
<Input.Wrapper> | ||
<Input.Icon as={RiLock2Line} /> | ||
<Input.Input | ||
id="password" | ||
type={showPassword ? 'text' : 'password'} | ||
placeholder="••••••••••••••••••" | ||
{...register('password')} | ||
/> | ||
<button | ||
type="button" | ||
onClick={() => setShowPassword((s) => !s)}> | ||
{showPassword ? ( | ||
<RiEyeOffLine className="text-text-soft-400 group-has-[disabled]:text-text-disabled-300 size-5" /> | ||
) : ( | ||
<RiEyeLine className="text-text-soft-400 group-has-[disabled]:text-text-disabled-300 size-5" /> | ||
)} | ||
</button> | ||
</Input.Wrapper> | ||
</Input.Root> | ||
{errors.password && ( | ||
<Hint.Root className="mt-px" hasError> | ||
<Hint.Icon as={RiErrorWarningFill} /> | ||
{errors.password?.message} | ||
</Hint.Root> | ||
)} | ||
</div> | ||
<Input.Root hasError={!!errors.password}> | ||
<Input.Wrapper> | ||
<Input.Icon as={RiLock2Line} /> | ||
<Input.Input | ||
id="password" | ||
type={showPassword ? 'text' : 'password'} | ||
placeholder="••••••••••••••••••" | ||
{...register('password')} | ||
/> | ||
<button type="button" onClick={() => setShowPassword((s) => !s)}> | ||
{showPassword ? ( | ||
<RiEyeOffLine className="text-text-soft-400 group-has-[disabled]:text-text-disabled-300 size-5" /> | ||
) : ( | ||
<RiEyeLine className="text-text-soft-400 group-has-[disabled]:text-text-disabled-300 size-5" /> | ||
)} | ||
</button> | ||
</Input.Wrapper> | ||
</Input.Root> | ||
{errors.password && ( | ||
<Hint.Root className="mt-px" hasError> | ||
<Hint.Icon as={RiErrorWarningFill} /> | ||
{errors.password?.message} | ||
</Hint.Root> | ||
)} | ||
</div> | ||
|
||
<FancyButton.Root | ||
className="mt-6 w-full" | ||
variant="primary" | ||
type="submit" | ||
form="login-form"> | ||
{login.isPending ? 'Logging in...' : 'Login'} | ||
</FancyButton.Root> | ||
<FancyButton.Root | ||
className="mt-6 w-full" | ||
variant="primary" | ||
type="submit" | ||
form="login-form"> | ||
{login.isPending ? 'Logging in...' : 'Login'} | ||
</FancyButton.Root> | ||
|
||
<div className="text-paragraph-sm text-text-sub-600 mt-4 flex items-center justify-center gap-1"> | ||
Don't have an account?{' '} | ||
<Link to="/auth/register"> | ||
<LinkButton.Root variant="primary">Register</LinkButton.Root> | ||
</Link> | ||
</div> | ||
</form> | ||
</div> | ||
</section> | ||
<div className="text-paragraph-sm text-text-sub-600 mt-4 flex items-center justify-center gap-1"> | ||
Don't have an account?{' '} | ||
<Link to="/auth/register"> | ||
<LinkButton.Root variant="primary">Register</LinkButton.Root> | ||
</Link> | ||
</div> | ||
</form> | ||
</div> | ||
); | ||
}; | ||
|
||
|
Oops, something went wrong.