Skip to content

Commit

Permalink
Merge pull request #85 from BacPacNet/login-flow
Browse files Browse the repository at this point in the history
created v2/login page
  • Loading branch information
coolpinkzz authored Sep 6, 2024
2 parents e1cbf5b + a15faaf commit 9edb5d3
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/app/v2/login/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import LoginBox from '@/components/organism/Login/LoginBox'
import React from 'react'

const Login = () => {
return (
<div className="flex justify-center items-center h-screen bg-neutral-50">
<LoginBox />
</div>
)
}

export default Login
6 changes: 6 additions & 0 deletions src/assets/Logo Circle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions src/components/atoms/Input/InputBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React, { forwardRef } from 'react'

type Props = {
placeholder?: string
className?: string
type: string
}
const InputBox = forwardRef<HTMLInputElement, Props>(({ placeholder, className, type, ...rest }, ref) => {
return (
<input
className={`${className} py-2 px-3 border rounded-lg drop-shadow-sm border-neutral-200 text-neutral-400 h-10 outline-none`}
type={type}
placeholder={placeholder}
ref={ref}
{...rest}
/>
)
})

InputBox.displayName = 'InputBox' // for React DevTools

export default InputBox
13 changes: 13 additions & 0 deletions src/components/atoms/InputWarningText/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React, { ReactNode, HTMLAttributes } from 'react'

interface InputWarningTextProps extends HTMLAttributes<HTMLHeadingElement> {
children: ReactNode
}

export default function InputWarningText({ children, className = '', ...rest }: InputWarningTextProps) {
return (
<span className={`text-red-500 font-normal ${className}`} {...rest}>
{children}
</span>
)
}
24 changes: 24 additions & 0 deletions src/components/atoms/LoginButtons/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react'

interface LoginButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
className?: string
variant?: 'primary' | 'secondary' | 'danger' | 'shade'
}

const LoginButtons: React.FC<LoginButtonProps> = ({ className = '', variant = 'primary', children, ...props }) => {
const variantClasses = {
primary: 'bg-primary-500 text-white',
secondary: 'bg-gray-500 text-white',
danger: 'bg-red-500 text-white',
shade: 'bg-secondary border border-shade-button-border text-primary-500 drop-shadow-sm',
}

const variantClass = variantClasses[variant]
return (
<button className={`${variantClass} py-2 px-4 rounded-md active:scale-95 transition-transform duration-150 ${className}`} {...props}>
{children}
</button>
)
}

export default LoginButtons
13 changes: 13 additions & 0 deletions src/components/atoms/SupportingText/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React, { ReactNode, HTMLAttributes } from 'react'

interface TitleProps extends HTMLAttributes<HTMLHeadingElement> {
children: ReactNode
}

export default function SupportingText({ children, className = '', ...rest }: TitleProps) {
return (
<p className={` font-normal text-neutral-500 font-inter ${className}`} {...rest}>
{children}
</p>
)
}
102 changes: 102 additions & 0 deletions src/components/organism/Login/LoginBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
'use client'
import React, { useEffect, useState } from 'react'
import logo from '@/assets/Logo Circle.svg'
import InputBox from '@/components/atoms/Input/InputBox'
import { AiOutlineEye } from 'react-icons/ai'
import { AiOutlineEyeInvisible } from 'react-icons/ai'
import Title from '@/components/atoms/Title'
import SupportingText from '@/components/atoms/SupportingText'
import LoginButtons from '@/components/atoms/LoginButtons'
import { useForm } from 'react-hook-form'
import { LoginForm } from '@/models/auth'
import InputWarningText from '@/components/atoms/InputWarningText'
import { useHandleLogin } from '@/services/auth'
import { useRouter } from 'next/navigation'

const LoginBox = () => {
const [showPassword, setShowPassword] = useState(false)
const router = useRouter()
const {
register: registerLogin,
handleSubmit: handleSubmitLogin,
formState: { errors: loginErrors },
} = useForm<LoginForm>()

const { mutate: mutateLogin, isSuccess } = useHandleLogin()

const onSubmit = async (data: LoginForm) => {
await mutateLogin(data)
}

useEffect(() => {
if (isSuccess) {
router.push(`/timeline`)
}
}, [isSuccess])

return (
<div className="flex flex-col w-1/3 max-lg:w-1/2 max-md:w-2/3 max-sm:w-11/12">
<div className="flex flex-col gap-8 border border-neutral-300 py-9 px-6 rounded-xl bg-white drop-shadow-md">
<img className="w-14 h-14" src={logo.src} alt="lgog" />
<div>
<Title>Login to your account</Title>
<SupportingText>Enter your details to access your account</SupportingText>
</div>
<form className="flex flex-col gap-5" onSubmit={handleSubmitLogin(onSubmit)}>
<div className="relative w-full flex flex-col gap-2">
<label htmlFor="Email Address" className="font-medium text-neutral-900">
Email Address
</label>

<InputBox
placeholder="[email protected]"
type="email"
{...registerLogin('email', {
required: true,
pattern: {
value: /^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$/i,
message: 'Invalid email format',
},
})}
/>
{loginErrors.email && (
<InputWarningText>{loginErrors.email.message ? loginErrors.email.message : 'Please enter your email!'}</InputWarningText>
)}
</div>
<div className="relative w-full flex flex-col gap-2">
<label htmlFor="Email Address" className="font-medium text-neutral-900">
Password
</label>
<InputBox
placeholder="*******************"
type={showPassword ? 'text' : 'password'}
{...registerLogin('password', { required: true })}
/>
<div className={`absolute right-0 pr-3 flex items-center text-sm ${loginErrors.password ? 'top-1/3' : 'top-[40%]'} `}>
{showPassword ? (
<AiOutlineEyeInvisible className="h-5 w-5 text-gray-700 cursor-pointer" onClick={() => setShowPassword(!showPassword)} />
) : (
<AiOutlineEye className="h-5 w-5 text-gray-700 cursor-pointer" onClick={() => setShowPassword(!showPassword)} />
)}
</div>
{loginErrors.password && <InputWarningText>Please enter your password!</InputWarningText>}
<label className="text-neutral-500 text-xs">Forgot Password?</label>
</div>

<div className="flex gap-2 items-center">
<input className="w-4 h-4 border-neutral-300" type="checkbox" />
<p className="text-neutral-900 text-sm">Remember device for 30 days</p>
</div>
<LoginButtons variant="primary">Log in</LoginButtons>
</form>
</div>
<button className="mt-4 mx-auto">
<p>
No account yet? <span className="text-primary-500">Create an account</span>
</p>
</button>
</div>
)
}

export default LoginBox
1 change: 1 addition & 0 deletions tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ module.exports = {
'gray-light': '#d3dce6',
'gray-1': '#737373',
'border': '#D4D4D4',
'shade-button-border': '#E9E8FF',
},

},
Expand Down

0 comments on commit 9edb5d3

Please sign in to comment.