-
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.
- Loading branch information
1 parent
100a3a2
commit 2760ba1
Showing
19 changed files
with
508 additions
and
23 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
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 |
---|---|---|
@@ -1,26 +1,38 @@ | ||
import { RouterProvider, createBrowserRouter } from "react-router-dom"; | ||
import Login from "./pages/Login"; | ||
import LandingPage from "./pages/LandingPage"; | ||
import Register from './pages/Register'; | ||
import Success from './components/register/Success'; | ||
import RegisterSection from './components/register/RegisterSection'; | ||
import { RouterProvider, createBrowserRouter } from 'react-router-dom'; | ||
import Login from './pages/Login'; | ||
import LandingPage from './pages/LandingPage'; | ||
|
||
const App = () => { | ||
const router = createBrowserRouter([ | ||
{ | ||
path: "/", | ||
path: '/', | ||
children: [ | ||
{ | ||
index: true, | ||
element: <LandingPage /> | ||
element: <LandingPage />, | ||
}, | ||
{ | ||
path: "login", | ||
element: <Login /> | ||
} | ||
] | ||
path: 'login', | ||
element: <Login />, | ||
}, | ||
], | ||
}, | ||
{ | ||
path: 'register', | ||
element: <Register />, | ||
children: [ | ||
{ | ||
path: '/register', | ||
element: <RegisterSection />, | ||
}, | ||
{ path: 'success', element: <Success /> }, | ||
], | ||
}, | ||
]) | ||
return ( | ||
<RouterProvider router={router} /> | ||
); | ||
]); | ||
return <RouterProvider router={router} />; | ||
}; | ||
|
||
export default App; |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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,22 @@ | ||
import { cn } from "../utils"; | ||
|
||
interface ButtonProps { | ||
text: string; | ||
buttonId?: string; | ||
type?: 'submit' | 'reset' | 'button'; | ||
className?: string; | ||
onClick?: () => void; | ||
} | ||
const Button = ({ text, type, className, onClick }: ButtonProps) => { | ||
return ( | ||
<button | ||
type={type} | ||
className={cn('p-2 rounded-lg bg-greenColor hover:bg-darkGreen transition-all text-whiteColor font-bold', className)} | ||
onClick={onClick} | ||
> | ||
{text} | ||
</button> | ||
); | ||
}; | ||
|
||
export default Button; |
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,34 @@ | ||
import { forwardRef } from 'react'; | ||
import { cn } from '../utils'; | ||
|
||
interface InputProps { | ||
type: string; | ||
label: string; | ||
id: string; | ||
placeholder: string; | ||
error?: string; | ||
labelClassName?: string; | ||
inputClasname?: string; | ||
} | ||
|
||
// Use React.forwardRef to forward the ref to the input element | ||
const Input = forwardRef<HTMLInputElement, InputProps>( | ||
({ type, labelClassName, inputClasname, label, id, placeholder, error = '', ...rest }, ref) => { | ||
return ( | ||
<label className={cn('flex flex-col gap-1 font-lg font-medium flex-1 w', labelClassName)}> | ||
{label} | ||
<input | ||
type={type} | ||
id={id} | ||
placeholder={placeholder} | ||
className={cn('p-1 px-3 rounded-lg border border-blackColor outline-none font-normal', inputClasname)} | ||
ref={ref} | ||
{...rest} | ||
/> | ||
{error && <p className='text-redColor text-xs'>{error}</p>} | ||
</label> | ||
); | ||
} | ||
); | ||
|
||
export default Input; |
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,24 @@ | ||
import { forwardRef } from 'react'; | ||
|
||
interface SelectProps { | ||
placeholder: string; | ||
error?: string; | ||
options: string[]; | ||
} | ||
const Select = forwardRef<HTMLSelectElement, SelectProps>(({ placeholder, options,error,...rest }, ref) => { | ||
return ( | ||
<> | ||
<select ref={ref} name='role' id='role' className='p-2 outline-0 border border-blackColor flex-1 rounded-lg' {...rest}> | ||
<option value=''>{placeholder}</option> | ||
{options.map((option, index) => ( | ||
<option key={index} value={option}> | ||
{option[0].toUpperCase() + option.slice(1)} | ||
</option> | ||
))} | ||
</select> | ||
<p className='text-redColor text-xs -mt-3'>{error}</p> | ||
</> | ||
); | ||
}); | ||
|
||
export default Select; |
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,27 @@ | ||
import { RiCloseLargeFill } from '@remixicon/react'; | ||
import { useState } from 'react'; | ||
|
||
const InfoTip = () => { | ||
const [InfoTipClosed, setInfoTipClosed] = useState(false); | ||
|
||
const handleInfoTipClosing = () => { | ||
setInfoTipClosed(true); | ||
}; | ||
return ( | ||
<div className={`bg-blackColor w-full text-whiteColor mt-4 px-4 xl:px-0 ${InfoTipClosed ? 'h-0 hidden opacity-0 transition-all' : 'block'}`}> | ||
<div className='flex flex-row justify-between items-center xl:container w-full py-2 mx-auto'> | ||
<p className='w-8/12 text-lg'> | ||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Aliquid eum accusantium reiciendis Lorem ipsum dolor | ||
sit amet | ||
</p> | ||
<RiCloseLargeFill | ||
size={24} | ||
className='rounded-full p-2 border w-10 h-10 aspect-square' | ||
onClick={handleInfoTipClosing} | ||
/> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default InfoTip; |
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,29 @@ | ||
import { RiMenu2Fill, RiPhoneLine, RiQuestionFill, RiSearch2Line } from '@remixicon/react'; | ||
|
||
const Navbar = () => { | ||
return ( | ||
<div className='flex flex-row items-center justify-between w-full px-4 xl:px-0 xl:container mt-4 '> | ||
<a href='/' className='w-2/12 uppercase font-extrabold text-sm md:text-xl xl:text-3xl flex'> | ||
Mavericks <span>🏀</span> | ||
</a> | ||
|
||
<span className='hidden sm:flex flex-row items-center px-3 border border-blackColor rounded-full sm:w-7/12 md:w-4/12'> | ||
<input type='text' name='' id='' className='outline-0 p-1 text-md w-full outline-none' /> | ||
<RiSearch2Line size={24} className=' grid place-content-center' /> | ||
</span> | ||
<RiMenu2Fill className='sm:block md:hidden' /> | ||
<div className='w-3/12 md:flex flex-row justify-end gap-3 hidden'> | ||
<span className='flex flex-row items-center justify-center gap-1'> | ||
<RiPhoneLine /> | ||
+250 787 922 900 | ||
</span> | ||
<span className='flex flex-row items-center justify-center gap-1'> | ||
<RiQuestionFill /> | ||
Help | ||
</span> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default Navbar; |
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,161 @@ | ||
import { zodResolver } from '@hookform/resolvers/zod'; | ||
import GoogleIcon from '../../assets/googleIcon.svg'; | ||
import Button from '../Button'; | ||
import Input from '../Input'; | ||
import Select from '../Select'; | ||
import { SubmitHandler, useForm } from 'react-hook-form'; | ||
import { extendedSchema, ExtendedFormFields } from '../../utils/schemas'; | ||
import { useNavigate } from 'react-router-dom'; | ||
import { useDispatch } from 'react-redux'; | ||
import { useRegisterUserMutation } from '../../services'; | ||
import { setUserRegistered } from '../../state/register/registerSlice'; | ||
import { useEffect } from 'react'; | ||
import { QueryErrorData } from '../../utils/schemas'; | ||
|
||
const RegisterSection = () => { | ||
const navigate = useNavigate(); | ||
const dispatch = useDispatch(); | ||
|
||
const { | ||
register, | ||
setError, | ||
handleSubmit, | ||
formState: { errors, isSubmitting }, | ||
} = useForm<ExtendedFormFields>({ | ||
resolver: zodResolver(extendedSchema), | ||
}); | ||
const [registerUser, { data, isLoading, isSuccess, isError, error }] = useRegisterUserMutation(); | ||
let loginData: any; | ||
|
||
const onSubmit: SubmitHandler<ExtendedFormFields> = async retrievedData => { | ||
const { password, lastName, firstName, gender, phoneNumber, email } = retrievedData; | ||
|
||
loginData = { password, lastName, firstName, gender, phoneNumber, email }; | ||
await registerUser({ email, password, lastName, firstName, gender, phoneNumber }); | ||
}; | ||
useEffect(() => { | ||
const loginError = error as QueryErrorData; | ||
|
||
if (isError) { | ||
console.log(loginError); | ||
setError('root', { | ||
message: loginError.data?.error || loginError.data?.message, | ||
}); | ||
if (/email is already used/gi.test(loginError.data?.error || (loginError.data?.message as string))) { | ||
setTimeout(() => { | ||
setError('root', { | ||
message: 'Redirecting you to login page....', | ||
}); | ||
setTimeout(() => { | ||
navigate('/login'); | ||
}, 3000); | ||
}, 2000); | ||
} | ||
return; | ||
} | ||
if (isSuccess) { | ||
dispatch(setUserRegistered({ ...loginData })); | ||
navigate('/register/success'); | ||
} | ||
}, [isLoading, isError, error, isSuccess, data]); | ||
|
||
return ( | ||
<> | ||
<div | ||
className={`w-full xl:container flex flex-col-reverse gap-14 md:flex-row px-4 items-center md:gap-5 transition-all | ||
md:justify-between md:px-10 lg:justify-around mt-5 md:mt-10 `} | ||
> | ||
<div className='w-full md:w-7/12 xl:w-4/12 '> | ||
<p className='font-bold text-3xl '>New Customer?</p> | ||
<p>Sign Up to continue</p> | ||
<form action='' className='flex flex-col gap-4 justify-center mt-8 ' onSubmit={handleSubmit(onSubmit)}> | ||
<div className='grid grid-rows-2 md:grid-rows-1 md:grid-cols-2 gap-3'> | ||
<Input | ||
id='firstName' | ||
label='First Name' | ||
type='text' | ||
placeholder='Enter your fisrt name' | ||
{...register('firstName')} | ||
error={errors.firstName && errors.firstName?.message} | ||
/> | ||
<Input | ||
id='lastName' | ||
label='Last Name' | ||
type='text' | ||
placeholder='Enter your last name' | ||
{...register('lastName')} | ||
error={errors.lastName && errors.lastName?.message} | ||
/> | ||
</div> | ||
<Input | ||
id='registerEmail' | ||
label='Email' | ||
type='email' | ||
placeholder='Enter your email' | ||
{...register('email')} | ||
error={errors.email && errors.email?.message} | ||
/> | ||
<Input | ||
type='tel' | ||
label='Phone Number' | ||
id='phoneNumber' | ||
placeholder='Enter your Phone Number' | ||
{...register('phoneNumber')} | ||
error={errors.phoneNumber && errors.phoneNumber.message} | ||
/> | ||
<Input | ||
id='password' | ||
label='Enter your Password' | ||
type='password' | ||
placeholder='Enter your Password' | ||
{...register('password')} | ||
error={errors.password && errors.password?.message} | ||
/> | ||
|
||
<Input | ||
id='confirm__password' | ||
label='Confirm your Password' | ||
type='password' | ||
placeholder='Confirm your password' | ||
{...register('confirmPassword')} | ||
error={errors.confirmPassword && errors.confirmPassword?.message} | ||
/> | ||
<Select | ||
placeholder='Gender' | ||
options={['male', 'female']} | ||
{...register('gender')} | ||
error={errors.gender && errors.gender.message} | ||
/> | ||
<button | ||
type='submit' | ||
className='p-2 rounded-lg bg-greenColor hover:bg-darkGreen transition-all text-whiteColor font-bold' | ||
> | ||
{isSubmitting ? 'Loading..' : 'Sign Up'} | ||
</button> | ||
<p | ||
className={`text-sm bg-redColor text-whiteColor -mt-2 p-1 rounded-lg px-3 ${!errors.root ? 'hidden' : 'block'}`} | ||
> | ||
{errors.root?.message} | ||
</p> | ||
<span className='self-center font-bold text-grayColor'>or</span> | ||
<button | ||
className='p-2 font-bold text-greenColor border-2 border-greenColor rounded-lg flex flex-row | ||
items-center justify-center gap-2 hover:bg-teal-100 transition-all' | ||
> | ||
<img src={GoogleIcon} alt='Google Icon' className='w-5' /> | ||
Continue with Google | ||
</button> | ||
</form> | ||
</div> | ||
<div className='w-full md:w-3/12 mt-0 p-5 border-b-2 md:p-0 md:border-b-0 h-fit flex flex-col gap-3 justify-self-start self-start '> | ||
<p className='font-bold text-3xl'>Existing Customer?</p> | ||
<p>Go to Login Page to sign in</p> | ||
<Button type='submit' text='Sign In' onClick={() => navigate('/login')} /> | ||
</div> | ||
</div> | ||
<Button className={`my-10 mx-auto block`} text='Back to Home Page' onClick={() => navigate('/')} /> | ||
</> | ||
); | ||
}; | ||
|
||
export default RegisterSection; |
Oops, something went wrong.