Skip to content

Commit

Permalink
[Finshes #187354196] Sign up page
Browse files Browse the repository at this point in the history
  • Loading branch information
Favor-star committed Jun 12, 2024
1 parent 487d2e8 commit 1756b56
Show file tree
Hide file tree
Showing 19 changed files with 499 additions and 23 deletions.
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,19 @@
"build-storybook": "storybook build"
},
"dependencies": {
"@hookform/resolvers": "^3.6.0",
"@reduxjs/toolkit": "^2.2.5",
"@types/react-redux": "^7.1.33",
"clsx": "^2.1.1",
"lucide-react": "^0.387.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.51.5",
"react-redux": "^9.1.2",
"react-router-dom": "^6.23.1",
"sass": "^1.77.2"
"sass": "^1.77.2",
"tailwind-merge": "^2.3.0",
"zod": "^3.23.8"
},
"devDependencies": {
"@chromatic-com/storybook": "^1.4.0",
Expand Down Expand Up @@ -56,7 +61,6 @@
"prettier": "3.2.5",
"storybook": "^8.1.3",
"tailwindcss": "^3.4.3",
"typescript": "^5.2.2",
"typescript-eslint": "^7.10.0",
"vite": "^5.2.0",
"vitest": "^1.6.0"
Expand Down
38 changes: 25 additions & 13 deletions src/App.tsx
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;
6 changes: 6 additions & 0 deletions src/assets/googleIcon.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/Button.tsx
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;
34 changes: 34 additions & 0 deletions src/components/Input.tsx
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;
24 changes: 24 additions & 0 deletions src/components/Select.tsx
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;
27 changes: 27 additions & 0 deletions src/components/register/InfoTip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { RiCloseLargeFill } from '@remixicon/react';

Check failure on line 1 in src/components/register/InfoTip.tsx

View workflow job for this annotation

GitHub Actions / Build (20)

Cannot find module '@remixicon/react' or its corresponding type declarations.
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;
29 changes: 29 additions & 0 deletions src/components/register/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { RiMenu2Fill, RiPhoneLine, RiQuestionFill, RiSearch2Line } from '@remixicon/react';

Check failure on line 1 in src/components/register/Navbar.tsx

View workflow job for this annotation

GitHub Actions / Build (20)

Cannot find module '@remixicon/react' or its corresponding type declarations.

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;
160 changes: 160 additions & 0 deletions src/components/register/RegisterSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
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 { useSelector } from 'react-redux';
import { RootState } from '../../state/store';

const RegisterSection = () => {
const navigate = useNavigate();
const dispatch = useDispatch();
const state = useSelector((state: RootState) => state.register);
const {
register,
setError,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<ExtendedFormFields>({
resolver: zodResolver(extendedSchema),
});
const [registerUser, { data, isLoading, isSuccess, isError, error }] = useRegisterUserMutation();

const onSubmit: SubmitHandler<ExtendedFormFields> = async retrievedData => {
const { password, lastName, firstName, gender, phoneNumber, email } = retrievedData;
await registerUser({ email, password, lastName, firstName, gender, phoneNumber });
dispatch(setUserRegistered({ password, lastName, firstName, gender, phoneNumber, email }));
if (isSuccess) {
console.log('TThe codes are actually reaching here');
}
};
useEffect(() => {
if (isError) {
setError('root', {
message: error.data.error || error.data.message,

Check failure on line 41 in src/components/register/RegisterSection.tsx

View workflow job for this annotation

GitHub Actions / Build (20)

Property 'data' does not exist on type 'FetchBaseQueryError | SerializedError'.

Check failure on line 41 in src/components/register/RegisterSection.tsx

View workflow job for this annotation

GitHub Actions / Build (20)

Property 'data' does not exist on type 'FetchBaseQueryError | SerializedError'.
});
if (/email is already used/gi.test(error.data.error || error.data.message)) {

Check failure on line 43 in src/components/register/RegisterSection.tsx

View workflow job for this annotation

GitHub Actions / Build (20)

Property 'data' does not exist on type 'FetchBaseQueryError | SerializedError'.

Check failure on line 43 in src/components/register/RegisterSection.tsx

View workflow job for this annotation

GitHub Actions / Build (20)

Property 'data' does not exist on type 'FetchBaseQueryError | SerializedError'.
setTimeout(() => {
setError('root', {
message: 'Redirecting you to login page....',
});
setTimeout(() => {
navigate('/login');
}, 3000);
}, 2000);
}
return;
}
if (isSuccess) {
console.log(state);
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;
Loading

0 comments on commit 1756b56

Please sign in to comment.