Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Login Page - Component (JobQuest) #8

Merged
merged 3 commits into from
Nov 18, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,265 changes: 3,024 additions & 241 deletions package-lock.json

Large diffs are not rendered by default.

9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,25 @@
"preview": "vite preview"
},
"dependencies": {
"@nextui-org/react": "^2.2.9",
"framer-motion": "^10.16.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.18.0",
"react-tabs": "^6.0.2",
"react-router-dom": "^6.18.0"
"yup": "^1.3.2"
},
"devDependencies": {
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"@vitejs/plugin-react": "^4.0.3",
"autoprefixer": "^10.4.16",
"eslint": "^8.52.0",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
"tailwindcss": "^3.3.3",
"postcss": "^8.4.31",
"tailwindcss": "^3.3.5",
"vite": "^4.4.5"
}
}
6 changes: 6 additions & 0 deletions postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
5 changes: 3 additions & 2 deletions src/App.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#root {

/* #root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
Expand Down Expand Up @@ -39,4 +40,4 @@

.read-the-docs {
color: #888;
}
} */
2 changes: 1 addition & 1 deletion src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import "./App.css";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import { LandingPage } from "./pages/LandingPage";
import { LoginPage } from "./pages/Login";
import { LoginPage } from "./pages/Login/index";

function App() {
return (
Expand Down
Binary file added src/assets/authBackground.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
62 changes: 3 additions & 59 deletions src/index.css
Original file line number Diff line number Diff line change
@@ -1,59 +1,3 @@
:root {
--final-hover: 10px 0px 10px 0px rgba(12, 0, 255, 0.04), 0px 26px 30px 0px rgba(40, 31, 227, 0.08),
-10px 0px 10px 0px rgba(40, 31, 227, 0.04), -4px 7px 4px 0px rgba(25, 16, 165, 1);
--focus-ring-4px-primary-100: 0px 0px 0px 4px rgba(244, 235, 255, 1);
--foundation-bluedark: rgba(29, 36, 45, 1);
--foundation-bluedark-active: rgba(9, 11, 14, 1);
--foundation-bluedark-hover: rgba(21, 26, 32, 1);
--foundation-bluelight-active: rgba(144, 157, 173, 1);
--foundation-bluelight-hover: rgba(163, 173, 187, 1);
--foundation-bluelighter: rgba(178, 187, 198, 1);
--foundation-bluenormal: rgba(84, 104, 129, 1);
--foundation-bluenormal-active: rgba(61, 76, 94, 1);
--foundation-bluenormal-hover: rgba(71, 88, 110, 1);
--foundation-whitedark: rgba(191, 183, 174, 1);
--foundation-whitedark-active: rgba(115, 110, 104, 1);
--foundation-whitedark-hover: rgba(153, 146, 139, 1);
--foundation-whitedarker: rgba(89, 85, 81, 1);
--foundation-whitelight: rgba(255, 254, 253, 1);
--foundation-whitelight-active: rgba(255, 252, 248, 1);
--foundation-whitelight-hover: rgba(255, 253, 252, 1);
--foundation-whitenormal: rgba(255, 244, 232, 1);
--foundation-whitenormal-active: rgba(204, 195, 186, 1);
--foundation-whitenormal-hover: rgba(230, 220, 209, 1);
--text-sm-medium-font-family: "Inter-Medium", Helvetica;
--text-sm-medium-font-size: 14px;
--text-sm-medium-font-style: normal;
--text-sm-medium-font-weight: 500;
--text-sm-medium-letter-spacing: 0px;
--text-sm-medium-line-height: 20px;
--text-md-medium-font-family: "Inter-Medium", Helvetica;
--text-md-medium-font-size: 16px;
--text-md-medium-font-style: normal;
--text-md-medium-font-weight: 500;
--text-md-medium-letter-spacing: 0px;
--text-md-medium-line-height: 24px;
--text-md-regular-font-family: "Inter-Regular", Helvetica;
--text-md-regular-font-size: 16px;
--text-md-regular-font-style: normal;
--text-md-regular-font-weight: 400;
--text-md-regular-letter-spacing: 0px;
--text-md-regular-line-height: 24px;
--text-sm-regular-font-family: "Inter-Regular", Helvetica;
--text-sm-regular-font-size: 14px;
--text-sm-regular-font-style: normal;
--text-sm-regular-font-weight: 400;
--text-sm-regular-letter-spacing: 0px;
--text-sm-regular-line-height: 20px;
--gray-100: rgba(242, 244, 247, 1);
--gray-200: rgba(234, 236, 240, 1);
--gray-50: rgba(249, 250, 251, 1);
--gray-500: rgba(102, 112, 133, 1);
--gray-700: rgba(52, 64, 84, 1);
--primary-100: rgba(244, 235, 255, 1);
--primary-200: rgba(233, 215, 254, 1);
--primary-50: rgba(249, 245, 255, 1);
--primary-600: rgba(127, 86, 217, 1);
--shadow-sm: 0px 1px 2px 0px rgba(16, 24, 40, 0.06), 0px 1px 3px 0px rgba(16, 24, 40, 0.1);
--white: rgba(255,255,255,1);
}
@tailwind base;
@tailwind components;
@tailwind utilities;
11 changes: 7 additions & 4 deletions src/main.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from "./App.jsx";
import './index.css'
import ReactDOM from 'react-dom/client'
import {NextUIProvider} from '@nextui-org/react'
import App from './App'

ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
<NextUIProvider>
<App />
</NextUIProvider>
</React.StrictMode>,
)
)
22 changes: 22 additions & 0 deletions src/pages/Login/EyeFilledIcon.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from "react";
export const EyeFilledIcon = (props) => (
<svg
aria-hidden="true"
fill="none"
focusable="false"
height="1em"
role="presentation"
viewBox="0 0 24 24"
width="1em"
{...props}
>
<path
d="M21.25 9.14969C18.94 5.51969 15.56 3.42969 12 3.42969C10.22 3.42969 8.49 3.94969 6.91 4.91969C5.33 5.89969 3.91 7.32969 2.75 9.14969C1.75 10.7197 1.75 13.2697 2.75 14.8397C5.06 18.4797 8.44 20.5597 12 20.5597C13.78 20.5597 15.51 20.0397 17.09 19.0697C18.67 18.0897 20.09 16.6597 21.25 14.8397C22.25 13.2797 22.25 10.7197 21.25 9.14969ZM12 16.0397C9.76 16.0397 7.96 14.2297 7.96 11.9997C7.96 9.76969 9.76 7.95969 12 7.95969C14.24 7.95969 16.04 9.76969 16.04 11.9997C16.04 14.2297 14.24 16.0397 12 16.0397Z"
fill="currentColor"
/>
<path
d="M11.9984 9.14062C10.4284 9.14062 9.14844 10.4206 9.14844 12.0006C9.14844 13.5706 10.4284 14.8506 11.9984 14.8506C13.5684 14.8506 14.8584 13.5706 14.8584 12.0006C14.8584 10.4306 13.5684 9.14062 11.9984 9.14062Z"
fill="currentColor"
/>
</svg>
);
34 changes: 34 additions & 0 deletions src/pages/Login/EyeSlashFilledIcon.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from "react";
export const EyeSlashFilledIcon = (props) => (
<svg
aria-hidden="true"
fill="none"
focusable="false"
height="1em"
role="presentation"
viewBox="0 0 24 24"
width="1em"
{...props}
>
<path
d="M21.2714 9.17834C20.9814 8.71834 20.6714 8.28834 20.3514 7.88834C19.9814 7.41834 19.2814 7.37834 18.8614 7.79834L15.8614 10.7983C16.0814 11.4583 16.1214 12.2183 15.9214 13.0083C15.5714 14.4183 14.4314 15.5583 13.0214 15.9083C12.2314 16.1083 11.4714 16.0683 10.8114 15.8483C10.8114 15.8483 9.38141 17.2783 8.35141 18.3083C7.85141 18.8083 8.01141 19.6883 8.68141 19.9483C9.75141 20.3583 10.8614 20.5683 12.0014 20.5683C13.7814 20.5683 15.5114 20.0483 17.0914 19.0783C18.7014 18.0783 20.1514 16.6083 21.3214 14.7383C22.2714 13.2283 22.2214 10.6883 21.2714 9.17834Z"
fill="currentColor"
/>
<path
d="M14.0206 9.98062L9.98062 14.0206C9.47062 13.5006 9.14062 12.7806 9.14062 12.0006C9.14062 10.4306 10.4206 9.14062 12.0006 9.14062C12.7806 9.14062 13.5006 9.47062 14.0206 9.98062Z"
fill="currentColor"
/>
<path
d="M18.25 5.74969L14.86 9.13969C14.13 8.39969 13.12 7.95969 12 7.95969C9.76 7.95969 7.96 9.76969 7.96 11.9997C7.96 13.1197 8.41 14.1297 9.14 14.8597L5.76 18.2497H5.75C4.64 17.3497 3.62 16.1997 2.75 14.8397C1.75 13.2697 1.75 10.7197 2.75 9.14969C3.91 7.32969 5.33 5.89969 6.91 4.91969C8.49 3.95969 10.22 3.42969 12 3.42969C14.23 3.42969 16.39 4.24969 18.25 5.74969Z"
fill="currentColor"
/>
<path
d="M14.8581 11.9981C14.8581 13.5681 13.5781 14.8581 11.9981 14.8581C11.9381 14.8581 11.8881 14.8581 11.8281 14.8381L14.8381 11.8281C14.8581 11.8881 14.8581 11.9381 14.8581 11.9981Z"
fill="currentColor"
/>
<path
d="M21.7689 2.22891C21.4689 1.92891 20.9789 1.92891 20.6789 2.22891L2.22891 20.6889C1.92891 20.9889 1.92891 21.4789 2.22891 21.7789C2.37891 21.9189 2.56891 21.9989 2.76891 21.9989C2.96891 21.9989 3.15891 21.9189 3.30891 21.7689L21.7689 3.30891C22.0789 3.00891 22.0789 2.52891 21.7689 2.22891Z"
fill="currentColor"
/>
</svg>
);
182 changes: 181 additions & 1 deletion src/pages/Login/index.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,183 @@
"use client";
import React from "react";
khairahscorner marked this conversation as resolved.
Show resolved Hide resolved
import { useNavigate } from "react-router-dom";

//for field validation
import { object, string } from "yup";

//nextUI components
import { Image, Input, Button } from "@nextui-org/react";
import { EyeFilledIcon } from "./EyeFilledIcon";
import { EyeSlashFilledIcon } from "./EyeSlashFilledIcon";

import authBackground from "../../assets/authBackground.png";

let userSchema = object({
email: string()
.required("Username is required")
.matches(
/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/,
"Invalid Email Format"
)
.label("email"),
password: string().required("Password Is Required").label("password"),
});

export const LoginPage = () => {
return <p>Job Quest Login</p>;
const [userInfo, setUserInfo] = React.useState({
email: "",
password: "",
});

//used to shwo or not show password.
const [isVisible, setIsVisible] = React.useState(false);

//form loading
const [isLoading, setIsLoading] = React.useState(false);
const [errors, setErrors] = React.useState({});

//Router navigation
const navigateTo = useNavigate();

React.useEffect(() => {
if (userInfo.email === "" || userInfo.password === "") {
setErrors((prevErrors) => ({
...prevErrors,
email: "",
password: "",
}));
}
}, [userInfo]);

async function handleSubmit() {
khairahscorner marked this conversation as resolved.
Show resolved Hide resolved
try {
setIsLoading(true);
let result = await userSchema.validate(userInfo, { abortEarly: false });

const email = localStorage.getItem("jobQuestEmail");
const password = localStorage.getItem("jobQuestPassword");

//if the result email and password field are equal to values saved in localStorage, then the user is authenticated.
if (email === result.email && password === result.password) {
navigateTo("../Roadmap");
khairahscorner marked this conversation as resolved.
Show resolved Hide resolved
} else {
const field = "loginFailed";
setErrors((prev) => ({
...prev,
[field]: "Incorrect Username/Password",
}));
}
setIsLoading(false);
} catch (err) {
for (const e of err.inner) {
let field = e.path;
let errmsg = e.errors[0];
setErrors((prev) => ({ ...prev, [field]: errmsg }));
}
setIsLoading(false);
}
}

return (
<div>
<Image
src={authBackground}
style={{
width: "100vw",
height: "99vh",
position: "relative",
zIndex: "1",
}}
/>
<div
className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-10 text-center p-10 "
style={{
width: "550px",
borderRadius: "30px",
backgroundColor: "#EFF2F9",
height: "500px",
}}
>
<Button
className="h-5 inline bg-transparent float-right font-bold text-1xl"
onClick={() => navigateTo("/")}
>
X
</Button>
<br />
<h1
className="text-4xl font-bold"
style={{
color: "#25274D",
}}
>
Welcome Back!
</h1>
<p className="m-4">
Each time you login -
<br />
you are making progress towards your goals.
</p>

<Input
onChange={(e) => {
setUserInfo((prev) => ({ ...prev, email: e.target.value }));
}}
value={userInfo.email}
className="m-3"
isRequired
isInvalid={errors.email && true}
errorMessage={errors.email}
type="email"
label="Email"
/>

<Input
onChange={(e) => {
setUserInfo((prev) => ({ ...prev, password: e.target.value }));
}}
value={userInfo.password}
className="m-3"
isRequired
isInvalid={errors.password && true}
errorMessage={errors.password}
label="Password"
endContent={
<button
className="focus:outline-none"
type="button"
onClick={() => setIsVisible(!isVisible)}
>
{isVisible ? (
<EyeSlashFilledIcon className="text-2xl text-default-400 pointer-events-none" />
) : (
<EyeFilledIcon className="text-2xl text-default-400 pointer-events-none" />
)}
</button>
}
type={isVisible ? "text" : "password"}
/>
{errors.loginFailed && (
<div className="text-red-500 text-right">{errors.loginFailed}</div>
)}
<p className="text-left m-2">
Forgot your password? <span className="text-red-500"> Reset</span>
</p>
<Button
onClick={handleSubmit}
size="lg"
{...(isLoading && { isLoading: true })}
className="text-white mt-5 text-1xl font-bold"
style={{
backgroundColor: "#090459",
}}
>
Log In
</Button>
<p className=" m-2">
Need an account? <span className="text-red-500"> Create Account</span>
</p>
</div>
</div>
);
};
Loading