From 9001fa56d47582f4e76785d0ac6965c6afda9b84 Mon Sep 17 00:00:00 2001 From: Nathan King Date: Fri, 12 Jul 2024 10:08:18 +0100 Subject: [PATCH 001/120] update readme --- README.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 60494ec4..bd6a39c5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,3 @@ -### Team Dev Client - -Client repository for team dev project. - ### Set up 1. Copy the `.env.example` file to a new file named `.env` (NOTE: Make sure to copy the file, don't remove the original .env.example) @@ -11,8 +7,8 @@ Client repository for team dev project. ### Project Management -https://github.com/orgs/boolean-uk/projects/10/views/1 +https://github.com/orgs/boolean-uk/projects/13/views/1 ### Contributing -- Pull requests should be made from branches following the naming convention: `--`, e.g. `vherus-#1-user_registration` +- Pull requests MUST be made from branches following the naming convention: `--`, e.g. `vherus-#1-user_registration` \ No newline at end of file From 5387d5a52a43806ce826a51dd34376460b891643 Mon Sep 17 00:00:00 2001 From: Hamada Abdelaal Date: Tue, 16 Jul 2024 14:13:48 +0200 Subject: [PATCH 002/120] add line space --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bd6a39c5..dba8de8b 100644 --- a/README.md +++ b/README.md @@ -11,4 +11,4 @@ https://github.com/orgs/boolean-uk/projects/13/views/1 ### Contributing -- Pull requests MUST be made from branches following the naming convention: `--`, e.g. `vherus-#1-user_registration` \ No newline at end of file +- Pull requests MUST be made from branches following the naming convention: `--`, e.g. `vherus-#1-user_registration` From 89585e7114d2d4fd83740dc21535981406504b42 Mon Sep 17 00:00:00 2001 From: Myrthe Dullaart Date: Wed, 17 Jul 2024 11:36:03 +0200 Subject: [PATCH 003/120] change step 2 to step 4 --- src/pages/welcome/index.js | 2 ++ src/pages/welcome/stepFour/index.js | 19 +++++++++++++++++++ src/pages/welcome/stepTwo/index.js | 10 +--------- 3 files changed, 22 insertions(+), 9 deletions(-) create mode 100644 src/pages/welcome/stepFour/index.js diff --git a/src/pages/welcome/index.js b/src/pages/welcome/index.js index 8df35b81..58c8dca6 100644 --- a/src/pages/welcome/index.js +++ b/src/pages/welcome/index.js @@ -3,6 +3,7 @@ import Stepper from "../../components/stepper"; import useAuth from "../../hooks/useAuth"; import StepOne from "./stepOne"; import StepTwo from "./stepTwo"; +import StepFour from "./stepFour"; import "./style.css"; const Welcome = () => { @@ -38,6 +39,7 @@ const Welcome = () => { } onComplete={onComplete}> + ); diff --git a/src/pages/welcome/stepFour/index.js b/src/pages/welcome/stepFour/index.js new file mode 100644 index 00000000..331a55de --- /dev/null +++ b/src/pages/welcome/stepFour/index.js @@ -0,0 +1,19 @@ +import Form from "../../../components/form" + +const StepFour = ({ data, setData }) => { + return ( + <> +
+

Bio

+
+
+
+ +

*Required

+
+
+ + ) +} + +export default StepFour \ No newline at end of file diff --git a/src/pages/welcome/stepTwo/index.js b/src/pages/welcome/stepTwo/index.js index ad695688..967785af 100644 --- a/src/pages/welcome/stepTwo/index.js +++ b/src/pages/welcome/stepTwo/index.js @@ -3,15 +3,7 @@ import Form from "../../../components/form" const StepTwo = ({ data, setData }) => { return ( <> -
-

Bio

-
-
-
- -

*Required

-
-
+ ) } From 5fd78501a32bc833493d89d00512ae83440ec895 Mon Sep 17 00:00:00 2001 From: Myrthe Dullaart Date: Wed, 17 Jul 2024 11:52:48 +0200 Subject: [PATCH 004/120] add step 2 form --- src/components/form/textInput/index.js | 2 +- src/pages/welcome/index.js | 5 ++++- src/pages/welcome/stepTwo/index.js | 18 +++++++++++++++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/components/form/textInput/index.js b/src/components/form/textInput/index.js index 4d8507d5..bfce4ce5 100644 --- a/src/components/form/textInput/index.js +++ b/src/components/form/textInput/index.js @@ -1,6 +1,6 @@ import { useState } from "react"; -const TextInput = ({ value, onChange, name, label, icon, type = "text" }) => { +const TextInput = ({ value, onChange, name, label, icon, type = "text"}) => { const [input, setInput] = useState(""); const [showpassword, setShowpassword] = useState(false); if (type === "password") { diff --git a/src/pages/welcome/index.js b/src/pages/welcome/index.js index 58c8dca6..9f1c998d 100644 --- a/src/pages/welcome/index.js +++ b/src/pages/welcome/index.js @@ -14,6 +14,9 @@ const Welcome = () => { lastName: "", githubUsername: "", bio: "", + email: "", + mobile: "", + password: "" }); const onChange = (event) => { @@ -26,7 +29,7 @@ const Welcome = () => { }; const onComplete = () => { - onCreateProfile(profile.firstName, profile.lastName, profile.githubUsername, profile.bio); + onCreateProfile(profile.firstName, profile.lastName, profile.githubUsername, profile.bio, profile.email, profile.mobile, profile.password); }; return ( diff --git a/src/pages/welcome/stepTwo/index.js b/src/pages/welcome/stepTwo/index.js index 967785af..fc907c1b 100644 --- a/src/pages/welcome/stepTwo/index.js +++ b/src/pages/welcome/stepTwo/index.js @@ -1,9 +1,25 @@ import Form from "../../../components/form" +import TextInput from "../../../components/form/textInput" const StepTwo = ({ data, setData }) => { return ( <> - +
+

Contact info

+
+
+
+ + + +

*Required

+
+
) } From 6cc68c8574f8f31b904078f61142140c96e1d943 Mon Sep 17 00:00:00 2001 From: Hamada Abdelaal Date: Wed, 17 Jul 2024 14:16:06 +0200 Subject: [PATCH 005/120] handle login error messages --- src/components/form/textInput/index.js | 125 +++++++++++++----------- src/context/auth.js | 129 +++++++++++++------------ src/pages/login/index.js | 81 ++++++++-------- src/service/apiClient.js | 60 ++++++------ src/styles/_form.css | 69 +++++++------ 5 files changed, 241 insertions(+), 223 deletions(-) diff --git a/src/components/form/textInput/index.js b/src/components/form/textInput/index.js index 4d8507d5..7298b648 100644 --- a/src/components/form/textInput/index.js +++ b/src/components/form/textInput/index.js @@ -1,68 +1,75 @@ import { useState } from "react"; const TextInput = ({ value, onChange, name, label, icon, type = "text" }) => { - const [input, setInput] = useState(""); - const [showpassword, setShowpassword] = useState(false); - if (type === "password") { - return ( -
- - { - onChange(e); - setInput(e.target.value); - }} - /> - {showpassword && ( - - )} - -
- ); - } else { - return ( -
- {label && } - - {icon && {icon}} -
- ); - } + const [input, setInput] = useState(""); + const [showpassword, setShowpassword] = useState(false); + if (type === "password") { + return ( +
+ + { + onChange(e); + setInput(e.target.value); + }} + /> + {showpassword && ( + + )} + + +
+ ); + } else { + return ( +
+ {label && } + + {icon && {icon}} +
+ ); + } }; const EyeLogo = () => { - return ( - - - - ); + return ( + + + + ); }; export default TextInput; diff --git a/src/context/auth.js b/src/context/auth.js index a7b9462d..b44630dd 100644 --- a/src/context/auth.js +++ b/src/context/auth.js @@ -5,85 +5,90 @@ import Modal from "../components/modal"; import Navigation from "../components/navigation"; import useAuth from "../hooks/useAuth"; import { createProfile, login, register } from "../service/apiClient"; -import jwt_decode from "jwt-decode" +import jwt_decode from "jwt-decode"; -const AuthContext = createContext() +const AuthContext = createContext(); const AuthProvider = ({ children }) => { - const navigate = useNavigate() - const location = useLocation() - const [token, setToken] = useState(null) + const navigate = useNavigate(); + const location = useLocation(); + const [token, setToken] = useState(null); + const [error, setError] = useState(""); - useEffect(() => { - const storedToken = localStorage.getItem('token') + useEffect(() => { + const storedToken = localStorage.getItem("token"); - if (storedToken) { - setToken(storedToken) - navigate(location.state?.from?.pathname || "/") - } - }, [location.state?.from?.pathname, navigate]) + if (storedToken) { + setToken(storedToken); + navigate(location.state?.from?.pathname || "/"); + } + }, [location.state?.from?.pathname, navigate]); - const handleLogin = async (email, password) => { - const res = await login(email, password) + const handleLogin = async (email, password) => { + const res = await login(email, password); - if (!res.data.token) { - return navigate("/login") - } + if (res.data.error) { + setError(res.data.error); + } - localStorage.setItem('token', res.data.token) + if (!res.data.token) { + return navigate("/login"); + } - setToken(res.token) - navigate(location.state?.from?.pathname || "/") - }; + localStorage.setItem("token", res.data.token); - const handleLogout = () => { - localStorage.removeItem('token') - setToken(null) - }; + setToken(res.data.token); + navigate(location.state?.from?.pathname || "/"); + }; - const handleRegister = async (email, password) => { - const res = await register(email, password) - setToken(res.data.token) + const handleLogout = () => { + localStorage.removeItem("token"); + setToken(null); + }; - navigate("/verification") - } + const handleRegister = async (email, password) => { + const res = await register(email, password); + setToken(res.data.token); + navigate("/verification"); + }; - const handleCreateProfile = async (firstName, lastName, githubUrl, bio) => { - const { userId } = jwt_decode(token) + const handleCreateProfile = async (firstName, lastName, githubUrl, bio) => { + const { userId } = jwt_decode(token); - await createProfile(userId, firstName, lastName, githubUrl, bio) + await createProfile(userId, firstName, lastName, githubUrl, bio); - localStorage.setItem('token', token) - navigate('/') - } + localStorage.setItem("token", token); // Isn't it redundant, as the token is already stored in localStorage during login or registration? + navigate("/"); + }; - const value = { - token, - onLogin: handleLogin, - onLogout: handleLogout, - onRegister: handleRegister, - onCreateProfile: handleCreateProfile - }; + const value = { + token, + onLogin: handleLogin, + onLogout: handleLogout, + onRegister: handleRegister, + onCreateProfile: handleCreateProfile, + error: error, + }; - return {children} + return {children}; }; const ProtectedRoute = ({ children }) => { - const { token } = useAuth() - const location = useLocation() - - if (!token) { - return - } - - return ( -
-
- - - {children} -
- ) -} - -export { AuthContext, AuthProvider, ProtectedRoute } + const { token } = useAuth(); + const location = useLocation(); + + if (!token) { + return ; + } + + return ( +
+
+ + + {children} +
+ ); +}; + +export { AuthContext, AuthProvider, ProtectedRoute }; diff --git a/src/pages/login/index.js b/src/pages/login/index.js index 60566866..a0214822 100644 --- a/src/pages/login/index.js +++ b/src/pages/login/index.js @@ -6,48 +6,49 @@ import CredentialsCard from "../../components/credentials"; import "./login.css"; const Login = () => { - const { onLogin } = useAuth(); - const [formData, setFormData] = useState({ email: "", password: "" }); + const { onLogin, error } = useAuth(); + const [formData, setFormData] = useState({ email: "", password: "" }); - const onChange = (e) => { - const { name, value } = e.target; - setFormData({ ...formData, [name]: value }); - }; + const onChange = (e) => { + const { name, value } = e.target; + setFormData({ ...formData, [name]: value }); + }; - return ( -
- -
-
- - - -
-
-
- ); + return ( +
+ +
+
+ + + +

{error}

+
+
+
+ ); }; export default Login; diff --git a/src/service/apiClient.js b/src/service/apiClient.js index ae4d87cf..50c7e0be 100644 --- a/src/service/apiClient.js +++ b/src/service/apiClient.js @@ -1,59 +1,59 @@ -import { API_URL } from "./constants" +import { API_URL } from "./constants"; async function login(email, password) { - return await post('login', { email, password }, false) + return await post("login", { email, password }, false); } async function register(email, password) { - await post('users', { email, password }, false) - return await login(email, password) + await post("users", { email, password }, false); + return await login(email, password); } async function createProfile(userId, firstName, lastName, githubUrl, bio) { - return await patch(`users/${userId}`, { firstName, lastName, githubUrl, bio }) + return await patch(`users/${userId}`, { + firstName, + lastName, + githubUrl, + bio, + }); } async function getPosts() { - const res = await get('posts') - return res.data.posts + const res = await get("posts"); + return res.data.posts; } async function post(endpoint, data, auth = true) { - return await request('POST', endpoint, data, auth) + return await request("POST", endpoint, data, auth); } async function patch(endpoint, data, auth = true) { - return await request('PATCH', endpoint, data, auth) + return await request("PATCH", endpoint, data, auth); } async function get(endpoint, auth = true) { - return await request('GET', endpoint, null, auth) + return await request("GET", endpoint, null, auth); } async function request(method, endpoint, data, auth = true) { - const opts = { - headers: { - 'Content-Type': 'application/json' - }, - method - } + const opts = { + headers: { + "Content-Type": "application/json", + }, + method, + }; - if (method.toUpperCase() !== 'GET') { - opts.body = JSON.stringify(data) - } + if (method.toUpperCase() !== "GET") { + opts.body = JSON.stringify(data); + } - if (auth) { - opts.headers['Authorization'] = `Bearer ${localStorage.getItem('token')}` - } + if (auth) { + opts.headers["Authorization"] = `Bearer ${localStorage.getItem("token")}`; + } - const response = await fetch(`${API_URL}/${endpoint}`, opts) + const response = await fetch(`${API_URL}/${endpoint}`, opts); - return response.json() + return response.json(); } -export { - login, - getPosts, - register, - createProfile -} \ No newline at end of file +export { login, getPosts, register, createProfile }; diff --git a/src/styles/_form.css b/src/styles/_form.css index 40fc45d7..2a888c30 100644 --- a/src/styles/_form.css +++ b/src/styles/_form.css @@ -1,56 +1,61 @@ form input { - background: var(--color-blue5); - border: 1px solid transparent; - border-radius: 8px; - padding: 16px; - font-size: 18px; - line-height: 1.33; - width: 100%; + background: var(--color-blue5); + border: 1px solid transparent; + border-radius: 8px; + padding: 16px; + font-size: 18px; + line-height: 1.33; + width: 100%; } form input:focus { - border: 1px solid var(--color-blue2); + border: 1px solid var(--color-blue2); } form label { - color: var(--color-blue1); - line-height: 1.5; - margin-bottom: 2px; - padding-left: 15px; + color: var(--color-blue1); + line-height: 1.5; + margin-bottom: 2px; + padding-left: 15px; } .inputwrapper { - position: relative; + position: relative; } .input-icon { - position: absolute; - left: 16px; - padding-top: 15px; + position: absolute; + left: 16px; + padding-top: 15px; } .input-has-icon { - padding-left: 56px; - padding-right: 10px; + padding-left: 56px; + padding-right: 10px; } .showpasswordbutton { - position: absolute; - bottom: 28px; - right: 16px; - z-index: 2; - background: none; - transform: translateY(19px); - transition: all 0.2s ease; + position: absolute; + bottom: 28px; + right: 16px; + z-index: 2; + background: none; + transform: translateY(19px); + transition: all 0.2s ease; } .showpasswordbutton:hover { - opacity: 0.7; + opacity: 0.7; } .showpasswordbutton.__faded { - opacity: 0.4; + opacity: 0.4; } .passwordreveal { - position: absolute; - bottom: 0; - right: 0; - width: 100%; - z-index: 1; + position: absolute; + bottom: 0; + right: 0; + width: 100%; + z-index: 1; +} + +.error-message { + color: red; + margin-bottom: 10px; } From dd8d7c5371c2ba6024397119d83eaced8f9de325 Mon Sep 17 00:00:00 2001 From: Leonardo Lodi Date: Wed, 17 Jul 2024 13:28:09 +0100 Subject: [PATCH 006/120] add step 3 for create profile form --- src/App.css | 9 ++-- src/assets/icons/lockIcon.js | 12 +++++ src/components/form/textInput/index.js | 4 +- src/components/stepper/index.js | 2 +- src/pages/welcome/index.js | 2 + src/pages/welcome/stepThree/index.js | 61 ++++++++++++++++++++++++++ src/styles/_form.css | 4 +- 7 files changed, 84 insertions(+), 10 deletions(-) create mode 100644 src/assets/icons/lockIcon.js create mode 100644 src/pages/welcome/stepThree/index.js diff --git a/src/App.css b/src/App.css index d8dd9fc4..800c59d8 100644 --- a/src/App.css +++ b/src/App.css @@ -3,11 +3,10 @@ } .container { - display: grid; - grid-template-columns: 151px 2fr 1fr; - grid-template-rows: 96px auto; - background-color: #F0F5FA; - height: 100vh; + display: grid; + grid-template-columns: 151px 2fr 1fr; + grid-template-rows: 96px auto; + background-color: #F0F5FA; } .ReactModal__Body--open, diff --git a/src/assets/icons/lockIcon.js b/src/assets/icons/lockIcon.js new file mode 100644 index 00000000..4ff58b63 --- /dev/null +++ b/src/assets/icons/lockIcon.js @@ -0,0 +1,12 @@ +const LockIcon = () => { + return ( + + + + ) +} + +export default LockIcon \ No newline at end of file diff --git a/src/components/form/textInput/index.js b/src/components/form/textInput/index.js index 4d8507d5..a405abb4 100644 --- a/src/components/form/textInput/index.js +++ b/src/components/form/textInput/index.js @@ -1,6 +1,6 @@ import { useState } from "react"; -const TextInput = ({ value, onChange, name, label, icon, type = "text" }) => { +const TextInput = ({ value, onChange, name, label, icon, type = "text", readOnly }) => { const [input, setInput] = useState(""); const [showpassword, setShowpassword] = useState(false); if (type === "password") { @@ -41,7 +41,7 @@ const TextInput = ({ value, onChange, name, label, icon, type = "text" }) => { return (
{label && } - + {icon && {icon}}
); diff --git a/src/components/stepper/index.js b/src/components/stepper/index.js index 36091af8..2a90c142 100644 --- a/src/components/stepper/index.js +++ b/src/components/stepper/index.js @@ -33,7 +33,7 @@ const Stepper = ({ header, children, onComplete }) => {
); diff --git a/src/pages/welcome/index.js b/src/pages/welcome/index.js index 8df35b81..452990ee 100644 --- a/src/pages/welcome/index.js +++ b/src/pages/welcome/index.js @@ -4,6 +4,7 @@ import useAuth from "../../hooks/useAuth"; import StepOne from "./stepOne"; import StepTwo from "./stepTwo"; import "./style.css"; +import StepThree from "./stepThree"; const Welcome = () => { const { onCreateProfile } = useAuth(); @@ -38,6 +39,7 @@ const Welcome = () => { } onComplete={onComplete}> + ); diff --git a/src/pages/welcome/stepThree/index.js b/src/pages/welcome/stepThree/index.js new file mode 100644 index 00000000..c0e270d9 --- /dev/null +++ b/src/pages/welcome/stepThree/index.js @@ -0,0 +1,61 @@ +import Form from "../../../components/form"; +import TextInput from "../../../components/form/textInput"; +import LockIcon from "../../../assets/icons/lockIcon"; + +const StepThree = ({ data }) => { + return ( + <> +
+

Training info

+
+ +
+
+ } + /> + + } + /> + + } + /> + + } + /> + + } + /> + +

*Required

+
+
+ + ); +}; + +export default StepThree; diff --git a/src/styles/_form.css b/src/styles/_form.css index 40fc45d7..071fae9d 100644 --- a/src/styles/_form.css +++ b/src/styles/_form.css @@ -23,8 +23,8 @@ form label { .input-icon { position: absolute; - left: 16px; - padding-top: 15px; + right: 16px; + padding-top: 8px; } .input-has-icon { padding-left: 56px; From 0f85b67bd95b9db95a02da92fd5214386b5ee187 Mon Sep 17 00:00:00 2001 From: Myrthe Dullaart Date: Wed, 17 Jul 2024 15:49:25 +0200 Subject: [PATCH 007/120] add * for required fields --- src/pages/welcome/stepTwo/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/welcome/stepTwo/index.js b/src/pages/welcome/stepTwo/index.js index fc907c1b..4fe8ed54 100644 --- a/src/pages/welcome/stepTwo/index.js +++ b/src/pages/welcome/stepTwo/index.js @@ -9,13 +9,13 @@ const StepTwo = ({ data, setData }) => {
- +

*Required

From d2b7307566d7150b03a468ea85bd2a4e8186c5e6 Mon Sep 17 00:00:00 2001 From: Leonardo Lodi Date: Wed, 17 Jul 2024 15:26:53 +0100 Subject: [PATCH 008/120] fix: add default value to readOnly and variable for the ternary Button component text --- src/components/form/textInput/index.js | 4 ++-- src/components/stepper/index.js | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/form/textInput/index.js b/src/components/form/textInput/index.js index a405abb4..74bd1e83 100644 --- a/src/components/form/textInput/index.js +++ b/src/components/form/textInput/index.js @@ -1,6 +1,6 @@ import { useState } from "react"; -const TextInput = ({ value, onChange, name, label, icon, type = "text", readOnly }) => { +const TextInput = ({ value, onChange, name, label, icon, type = "text", readOnly = false }) => { const [input, setInput] = useState(""); const [showpassword, setShowpassword] = useState(false); if (type === "password") { @@ -41,7 +41,7 @@ const TextInput = ({ value, onChange, name, label, icon, type = "text", readOnly return (
{label && } - + {icon && {icon}}
); diff --git a/src/components/stepper/index.js b/src/components/stepper/index.js index 2a90c142..b13256a5 100644 --- a/src/components/stepper/index.js +++ b/src/components/stepper/index.js @@ -22,6 +22,8 @@ const Stepper = ({ header, children, onComplete }) => { setCurrentStep(currentStep+1) } + const btnText = currentStep === children.length-1 ? 'Create profile' : 'Next' + return ( {header} @@ -33,7 +35,7 @@ const Stepper = ({ header, children, onComplete }) => {
); From 4e704b4460b55aa31370014f2ce8c9609433ddba Mon Sep 17 00:00:00 2001 From: Myrthe Dullaart Date: Wed, 17 Jul 2024 16:47:50 +0200 Subject: [PATCH 009/120] edit stepper order --- src/pages/welcome/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/welcome/index.js b/src/pages/welcome/index.js index 7d6cddda..0143affd 100644 --- a/src/pages/welcome/index.js +++ b/src/pages/welcome/index.js @@ -43,8 +43,8 @@ const Welcome = () => { } onComplete={onComplete}> - + ); From 2f933f47839c0d6142d662c5903afdef2a5e3254 Mon Sep 17 00:00:00 2001 From: Hamada Abdelaal Date: Wed, 17 Jul 2024 19:06:16 +0200 Subject: [PATCH 010/120] handle registration error messages-c --- src/components/credentials/index.js | 63 +++++++++++---------- src/context/auth.js | 12 +++- src/pages/login/index.js | 5 +- src/pages/register/index.js | 85 +++++++++++++++-------------- src/service/apiClient.js | 4 +- 5 files changed, 94 insertions(+), 75 deletions(-) diff --git a/src/components/credentials/index.js b/src/components/credentials/index.js index 0e43e202..8197b325 100644 --- a/src/components/credentials/index.js +++ b/src/components/credentials/index.js @@ -5,36 +5,41 @@ import "./credentials.css"; import Card from "../card"; const CredentialsCard = ({ - title, - socialLinksTitle, - altButtonTitle, - altButtonLink, - altButtonText, - children, + title, + socialLinksTitle, + altButtonTitle, + altButtonLink, + altButtonText, + setError, + children, }) => { - return ( -
- -
- -
-

{title && title}

- {children} -
-

{socialLinksTitle && socialLinksTitle}

-
- -
-
-
-

{altButtonTitle && altButtonTitle}

- - {altButtonText} - -
-
-
- ); + return ( +
+ +
+ +
+

{title && title}

+ {children} +
+

{socialLinksTitle && socialLinksTitle}

+
+ +
+
+
+

{altButtonTitle && altButtonTitle}

+ setError("")} + > + {altButtonText} + +
+
+
+ ); }; export default CredentialsCard; diff --git a/src/context/auth.js b/src/context/auth.js index b44630dd..44f05373 100644 --- a/src/context/auth.js +++ b/src/context/auth.js @@ -39,17 +39,24 @@ const AuthProvider = ({ children }) => { setToken(res.data.token); navigate(location.state?.from?.pathname || "/"); + setError(""); }; const handleLogout = () => { localStorage.removeItem("token"); setToken(null); + setError(""); }; const handleRegister = async (email, password) => { const res = await register(email, password); - setToken(res.data.token); - navigate("/verification"); + if (res.data.error) { + setError(res.data.error); + } else { + setToken(res.data.token); + navigate("/verification"); + setError(""); + } }; const handleCreateProfile = async (firstName, lastName, githubUrl, bio) => { @@ -68,6 +75,7 @@ const AuthProvider = ({ children }) => { onRegister: handleRegister, onCreateProfile: handleCreateProfile, error: error, + setError: setError, }; return {children}; diff --git a/src/pages/login/index.js b/src/pages/login/index.js index a0214822..31bbcad8 100644 --- a/src/pages/login/index.js +++ b/src/pages/login/index.js @@ -6,7 +6,7 @@ import CredentialsCard from "../../components/credentials"; import "./login.css"; const Login = () => { - const { onLogin, error } = useAuth(); + const { onLogin, error, setError } = useAuth(); const [formData, setFormData] = useState({ email: "", password: "" }); const onChange = (e) => { @@ -22,6 +22,7 @@ const Login = () => { altButtonTitle="Need an account?" altButtonLink="/register" altButtonText="Sign up" + setError={setError} >
@@ -39,7 +40,7 @@ const Login = () => { type={"password"} /> -

{error}

+ {error &&

{error}

}
- - - ); + return ( +
+ +
+
+ + + +

{error}

+
+
+
+ ); }; export default Register; diff --git a/src/service/apiClient.js b/src/service/apiClient.js index 50c7e0be..ba9b6a98 100644 --- a/src/service/apiClient.js +++ b/src/service/apiClient.js @@ -5,7 +5,9 @@ async function login(email, password) { } async function register(email, password) { - await post("users", { email, password }, false); + const res = await post("users", { email, password }, false); + + if (res.data.error) return res; return await login(email, password); } From f7a4251e6883df03e842cf57fb1c23f4ccb72960 Mon Sep 17 00:00:00 2001 From: Hamada Abdelaal Date: Thu, 18 Jul 2024 13:06:49 +0200 Subject: [PATCH 011/120] remove comments/ use {} --- src/context/auth.js | 2 +- src/service/apiClient.js | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/context/auth.js b/src/context/auth.js index 44f05373..f8b3857a 100644 --- a/src/context/auth.js +++ b/src/context/auth.js @@ -64,7 +64,7 @@ const AuthProvider = ({ children }) => { await createProfile(userId, firstName, lastName, githubUrl, bio); - localStorage.setItem("token", token); // Isn't it redundant, as the token is already stored in localStorage during login or registration? + localStorage.setItem("token", token); navigate("/"); }; diff --git a/src/service/apiClient.js b/src/service/apiClient.js index ba9b6a98..8f9f6c50 100644 --- a/src/service/apiClient.js +++ b/src/service/apiClient.js @@ -7,7 +7,9 @@ async function login(email, password) { async function register(email, password) { const res = await post("users", { email, password }, false); - if (res.data.error) return res; + if (res.data.error) { + return res; + } return await login(email, password); } From 803e730c201c77166f607662d0f53b59124a2f14 Mon Sep 17 00:00:00 2001 From: Hamada Abdelaal Date: Thu, 18 Jul 2024 14:21:39 +0200 Subject: [PATCH 012/120] resolve conflict --- src/components/form/textInput/index.js | 61 +++++--------------------- src/styles/_form.css | 9 +--- 2 files changed, 12 insertions(+), 58 deletions(-) diff --git a/src/components/form/textInput/index.js b/src/components/form/textInput/index.js index a31e80f6..92780b33 100644 --- a/src/components/form/textInput/index.js +++ b/src/components/form/textInput/index.js @@ -1,7 +1,14 @@ import { useState } from "react"; -<<<<<<< HEAD -const TextInput = ({ value, onChange, name, label, icon, type = "text" }) => { +const TextInput = ({ + value, + onChange, + name, + label, + icon, + type = "text", + readOnly = false, +}) => { const [input, setInput] = useState(""); const [showpassword, setShowpassword] = useState(false); if (type === "password") { @@ -25,7 +32,6 @@ const TextInput = ({ value, onChange, name, label, icon, type = "text" }) => { className="passwordreveal" /> )} - - - ); - } else { - return ( -
- {label && } - - {icon && {icon}} -
- ); - } ->>>>>>> main }; const EyeLogo = () => { diff --git a/src/styles/_form.css b/src/styles/_form.css index 5bc054c2..d0e3af1a 100644 --- a/src/styles/_form.css +++ b/src/styles/_form.css @@ -22,16 +22,11 @@ form label { } .input-icon { -<<<<<<< HEAD position: absolute; left: 16px; - padding-top: 15px; -======= - position: absolute; - right: 16px; - padding-top: 8px; ->>>>>>> main + padding-top: 8px; } + .input-has-icon { padding-left: 56px; padding-right: 10px; From 8ea78d4ea1eafc5994ff9a5c589e44a6b762a396 Mon Sep 17 00:00:00 2001 From: Leonardo Lodi Date: Thu, 18 Jul 2024 14:07:12 +0100 Subject: [PATCH 013/120] fix: step 2 from create profile and improve token logic --- src/context/auth.js | 7 ++++--- src/pages/welcome/index.js | 7 +++---- src/pages/welcome/stepOne/index.js | 5 +++-- src/pages/welcome/stepTwo/index.js | 8 -------- src/service/apiClient.js | 4 ++-- 5 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/context/auth.js b/src/context/auth.js index a7b9462d..b2593696 100644 --- a/src/context/auth.js +++ b/src/context/auth.js @@ -48,12 +48,13 @@ const AuthProvider = ({ children }) => { navigate("/verification") } - const handleCreateProfile = async (firstName, lastName, githubUrl, bio) => { + const handleCreateProfile = async (firstName, lastName, username, githubUrl, mobile, bio) => { const { userId } = jwt_decode(token) - await createProfile(userId, firstName, lastName, githubUrl, bio) + localStorage.setItem('token', token) + + await createProfile(userId, firstName, lastName, username, githubUrl, mobile, bio) - localStorage.setItem('token', token) navigate('/') } diff --git a/src/pages/welcome/index.js b/src/pages/welcome/index.js index 0143affd..fe0fd4a9 100644 --- a/src/pages/welcome/index.js +++ b/src/pages/welcome/index.js @@ -13,11 +13,10 @@ const Welcome = () => { const [profile, setProfile] = useState({ firstName: "", lastName: "", + username: "", githubUsername: "", bio: "", - email: "", - mobile: "", - password: "" + mobile: "" }); const onChange = (event) => { @@ -30,7 +29,7 @@ const Welcome = () => { }; const onComplete = () => { - onCreateProfile(profile.firstName, profile.lastName, profile.githubUsername, profile.bio, profile.email, profile.mobile, profile.password); + onCreateProfile(profile.firstName, profile.lastName, profile.username, profile.githubUsername, profile.bio, profile.mobile); }; return ( diff --git a/src/pages/welcome/stepOne/index.js b/src/pages/welcome/stepOne/index.js index edf11e95..3e488a8a 100644 --- a/src/pages/welcome/stepOne/index.js +++ b/src/pages/welcome/stepOne/index.js @@ -20,8 +20,9 @@ const StepOne = ({ data, setData }) => {

- - + + + {
- - -

*Required

diff --git a/src/service/apiClient.js b/src/service/apiClient.js index ae4d87cf..59f8ce9e 100644 --- a/src/service/apiClient.js +++ b/src/service/apiClient.js @@ -9,8 +9,8 @@ async function register(email, password) { return await login(email, password) } -async function createProfile(userId, firstName, lastName, githubUrl, bio) { - return await patch(`users/${userId}`, { firstName, lastName, githubUrl, bio }) +async function createProfile(userId, firstName, lastName, username, githubUrl, mobile, bio) { + return await patch(`users/${userId}`, { firstName, lastName, username, githubUrl, mobile, bio }) } async function getPosts() { From 2d64fa72bfa40d8f2032b755d02096d902820e30 Mon Sep 17 00:00:00 2001 From: Leonardo Lodi Date: Thu, 18 Jul 2024 14:23:22 +0100 Subject: [PATCH 014/120] style: formatting changes --- src/context/auth.js | 161 ++++++++++++++++++++++++-------------------- 1 file changed, 88 insertions(+), 73 deletions(-) diff --git a/src/context/auth.js b/src/context/auth.js index b2593696..a5241b4b 100644 --- a/src/context/auth.js +++ b/src/context/auth.js @@ -1,90 +1,105 @@ -import { createContext, useEffect, useState } from "react"; -import { useNavigate, useLocation, Navigate } from "react-router-dom"; -import Header from "../components/header"; -import Modal from "../components/modal"; -import Navigation from "../components/navigation"; -import useAuth from "../hooks/useAuth"; -import { createProfile, login, register } from "../service/apiClient"; +import { createContext, useEffect, useState } from "react" +import { useNavigate, useLocation, Navigate } from "react-router-dom" +import Header from "../components/header" +import Modal from "../components/modal" +import Navigation from "../components/navigation" +import useAuth from "../hooks/useAuth" +import { createProfile, login, register } from "../service/apiClient" import jwt_decode from "jwt-decode" const AuthContext = createContext() const AuthProvider = ({ children }) => { - const navigate = useNavigate() - const location = useLocation() - const [token, setToken] = useState(null) + const navigate = useNavigate() + const location = useLocation() + const [token, setToken] = useState(null) - useEffect(() => { - const storedToken = localStorage.getItem('token') + useEffect(() => { + const storedToken = localStorage.getItem("token") - if (storedToken) { - setToken(storedToken) - navigate(location.state?.from?.pathname || "/") - } - }, [location.state?.from?.pathname, navigate]) - - const handleLogin = async (email, password) => { - const res = await login(email, password) - - if (!res.data.token) { - return navigate("/login") - } - - localStorage.setItem('token', res.data.token) - - setToken(res.token) - navigate(location.state?.from?.pathname || "/") - }; - - const handleLogout = () => { - localStorage.removeItem('token') - setToken(null) - }; - - const handleRegister = async (email, password) => { - const res = await register(email, password) - setToken(res.data.token) - - navigate("/verification") + if (storedToken) { + setToken(storedToken) + navigate(location.state?.from?.pathname || "/") } + }, [location.state?.from?.pathname, navigate]) - const handleCreateProfile = async (firstName, lastName, username, githubUrl, mobile, bio) => { - const { userId } = jwt_decode(token) - - localStorage.setItem('token', token) - - await createProfile(userId, firstName, lastName, username, githubUrl, mobile, bio) + const handleLogin = async (email, password) => { + const res = await login(email, password) - navigate('/') + if (!res.data.token) { + return navigate("/login") } - const value = { - token, - onLogin: handleLogin, - onLogout: handleLogout, - onRegister: handleRegister, - onCreateProfile: handleCreateProfile - }; - - return {children} -}; + localStorage.setItem("token", res.data.token) + + setToken(res.token) + navigate(location.state?.from?.pathname || "/") + } + + const handleLogout = () => { + localStorage.removeItem("token") + setToken(null) + } + + const handleRegister = async (email, password) => { + const res = await register(email, password) + setToken(res.data.token) + + navigate("/verification") + } + + const handleCreateProfile = async ( + firstName, + lastName, + username, + githubUrl, + mobile, + bio + ) => { + const { userId } = jwt_decode(token) + + localStorage.setItem("token", token) + + await createProfile( + userId, + firstName, + lastName, + username, + githubUrl, + mobile, + bio + ) + + navigate("/") + } + + const value = { + token, + onLogin: handleLogin, + onLogout: handleLogout, + onRegister: handleRegister, + onCreateProfile: handleCreateProfile, + } + + return {children} +} const ProtectedRoute = ({ children }) => { - const { token } = useAuth() - const location = useLocation() - - if (!token) { - return - } - - return ( -
-
- - - {children} -
- ) + const { token } = useAuth() + const location = useLocation() + + if (!token) { + return + } + + return ( +
+
+ + + {children} +
+ ) } export { AuthContext, AuthProvider, ProtectedRoute } From 2c19a248020c454706792f18bd292e9810e126fa Mon Sep 17 00:00:00 2001 From: Leonardo Lodi Date: Thu, 18 Jul 2024 17:04:08 +0100 Subject: [PATCH 015/120] fix: required first and last names at create profile and remove back btn from step one --- src/components/form/textInput/index.js | 6 ++++-- src/components/stepper/index.js | 14 +++++++++++--- src/components/stepper/style.css | 21 ++++++++++++++++----- src/pages/welcome/index.js | 2 +- src/pages/welcome/stepOne/index.js | 4 ++-- 5 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/components/form/textInput/index.js b/src/components/form/textInput/index.js index 74bd1e83..c7f484f9 100644 --- a/src/components/form/textInput/index.js +++ b/src/components/form/textInput/index.js @@ -1,8 +1,10 @@ import { useState } from "react"; -const TextInput = ({ value, onChange, name, label, icon, type = "text", readOnly = false }) => { +const TextInput = ({ value, onChange, name, label, icon, type = "text", readOnly = false, required = false }) => { const [input, setInput] = useState(""); const [showpassword, setShowpassword] = useState(false); + const inputHasIcon = icon ? "input-has-icon" : "" + if (type === "password") { return (
@@ -41,7 +43,7 @@ const TextInput = ({ value, onChange, name, label, icon, type = "text", readOnly return (
{label && } - + {icon && {icon}}
); diff --git a/src/components/stepper/index.js b/src/components/stepper/index.js index b13256a5..398030c4 100644 --- a/src/components/stepper/index.js +++ b/src/components/stepper/index.js @@ -4,7 +4,7 @@ import Button from "../button"; import "./style.css"; import { useState } from "react"; -const Stepper = ({ header, children, onComplete }) => { +const Stepper = ({ header, children, onComplete, data }) => { const [currentStep, setCurrentStep] = useState(0) const onBackClick = () => { @@ -14,6 +14,10 @@ const Stepper = ({ header, children, onComplete }) => { } const onNextClick = () => { + if (data.firstName.length === 0 || data.lastName.length === 0) { + return + } + if (currentStep === children.length-1) { onComplete() return @@ -34,8 +38,12 @@ const Stepper = ({ header, children, onComplete }) => { {children[currentStep]}
-
); diff --git a/src/components/stepper/style.css b/src/components/stepper/style.css index ab403c21..df119c64 100644 --- a/src/components/stepper/style.css +++ b/src/components/stepper/style.css @@ -1,9 +1,20 @@ .stepper-buttons { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 20px; - justify-content: space-between; + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-areas: "back next"; + gap: 20px; + justify-content: space-between; } .stepper-buttons button:nth-of-type(2) { - justify-self: end; + justify-self: end; } + +.back { + grid-area: back; +} + +.next { + grid-area: next; + display: grid; + justify-items: right; +} \ No newline at end of file diff --git a/src/pages/welcome/index.js b/src/pages/welcome/index.js index fe0fd4a9..888e42f7 100644 --- a/src/pages/welcome/index.js +++ b/src/pages/welcome/index.js @@ -39,7 +39,7 @@ const Welcome = () => {

Create your profile to get started

- } onComplete={onComplete}> + } onComplete={onComplete} data={profile}> diff --git a/src/pages/welcome/stepOne/index.js b/src/pages/welcome/stepOne/index.js index 3e488a8a..c63480bc 100644 --- a/src/pages/welcome/stepOne/index.js +++ b/src/pages/welcome/stepOne/index.js @@ -20,8 +20,8 @@ const StepOne = ({ data, setData }) => {

- - + + Date: Fri, 19 Jul 2024 10:40:33 +0100 Subject: [PATCH 016/120] fix: remove data from stepper --- src/components/stepper/index.js | 6 +++--- src/pages/welcome/index.js | 8 +++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/components/stepper/index.js b/src/components/stepper/index.js index 398030c4..c4156a4f 100644 --- a/src/components/stepper/index.js +++ b/src/components/stepper/index.js @@ -4,7 +4,7 @@ import Button from "../button"; import "./style.css"; import { useState } from "react"; -const Stepper = ({ header, children, onComplete, data }) => { +const Stepper = ({ header, children, onComplete, missingFields }) => { const [currentStep, setCurrentStep] = useState(0) const onBackClick = () => { @@ -14,10 +14,10 @@ const Stepper = ({ header, children, onComplete, data }) => { } const onNextClick = () => { - if (data.firstName.length === 0 || data.lastName.length === 0) { + if (missingFields()) { return } - + if (currentStep === children.length-1) { onComplete() return diff --git a/src/pages/welcome/index.js b/src/pages/welcome/index.js index 888e42f7..a1e82c9e 100644 --- a/src/pages/welcome/index.js +++ b/src/pages/welcome/index.js @@ -32,6 +32,12 @@ const Welcome = () => { onCreateProfile(profile.firstName, profile.lastName, profile.username, profile.githubUsername, profile.bio, profile.mobile); }; + const missingFields = () => { + if (profile.firstName.length === 0 || profile.lastName.length === 0) { + return true + } + } + return (
@@ -39,7 +45,7 @@ const Welcome = () => {

Create your profile to get started

- } onComplete={onComplete} data={profile}> + } onComplete={onComplete} missingFields={missingFields}> From 8cb81173d0eb972bdedef5adb45e1f35171988fc Mon Sep 17 00:00:00 2001 From: Leonardo Lodi Date: Fri, 19 Jul 2024 10:55:09 +0100 Subject: [PATCH 017/120] fix: adjust can progress at step one --- src/components/stepper/index.js | 4 ++-- src/pages/welcome/index.js | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/components/stepper/index.js b/src/components/stepper/index.js index c4156a4f..346408d5 100644 --- a/src/components/stepper/index.js +++ b/src/components/stepper/index.js @@ -4,7 +4,7 @@ import Button from "../button"; import "./style.css"; import { useState } from "react"; -const Stepper = ({ header, children, onComplete, missingFields }) => { +const Stepper = ({ header, children, onComplete, canProgress }) => { const [currentStep, setCurrentStep] = useState(0) const onBackClick = () => { @@ -14,7 +14,7 @@ const Stepper = ({ header, children, onComplete, missingFields }) => { } const onNextClick = () => { - if (missingFields()) { + if (!canProgress) { return } diff --git a/src/pages/welcome/index.js b/src/pages/welcome/index.js index a1e82c9e..0422b438 100644 --- a/src/pages/welcome/index.js +++ b/src/pages/welcome/index.js @@ -32,11 +32,7 @@ const Welcome = () => { onCreateProfile(profile.firstName, profile.lastName, profile.username, profile.githubUsername, profile.bio, profile.mobile); }; - const missingFields = () => { - if (profile.firstName.length === 0 || profile.lastName.length === 0) { - return true - } - } + const canProgress = profile.firstName.length !== 0 && profile.lastName.length !== 0 return (
@@ -45,7 +41,7 @@ const Welcome = () => {

Create your profile to get started

- } onComplete={onComplete} missingFields={missingFields}> + } onComplete={onComplete} canProgress={canProgress}> From c2c50e5ab625ee3a54593c4ccc7dfe2c3e730f97 Mon Sep 17 00:00:00 2001 From: Hamada Abdelaal Date: Fri, 19 Jul 2024 12:06:59 +0200 Subject: [PATCH 018/120] merge main --- src/service/apiClient.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/service/apiClient.js b/src/service/apiClient.js index 8269397b..9b446600 100644 --- a/src/service/apiClient.js +++ b/src/service/apiClient.js @@ -13,18 +13,23 @@ async function register(email, password) { return await login(email, password); } -<<<<<<< HEAD -async function createProfile(userId, firstName, lastName, githubUrl, bio) { +async function createProfile( + userId, + firstName, + lastName, + username, + githubUrl, + mobile, + bio +) { return await patch(`users/${userId}`, { firstName, lastName, + username, githubUrl, + mobile, bio, }); -======= -async function createProfile(userId, firstName, lastName, username, githubUrl, mobile, bio) { - return await patch(`users/${userId}`, { firstName, lastName, username, githubUrl, mobile, bio }) ->>>>>>> main } async function getPosts() { From 2a8968802137a9da79cfcb1c5dafe271a7fc2517 Mon Sep 17 00:00:00 2001 From: Leonardo Lodi Date: Fri, 19 Jul 2024 11:13:59 +0100 Subject: [PATCH 019/120] fix: set can progress boolean to true --- src/components/stepper/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/stepper/index.js b/src/components/stepper/index.js index 346408d5..d39e63dc 100644 --- a/src/components/stepper/index.js +++ b/src/components/stepper/index.js @@ -4,7 +4,7 @@ import Button from "../button"; import "./style.css"; import { useState } from "react"; -const Stepper = ({ header, children, onComplete, canProgress }) => { +const Stepper = ({ header, children, onComplete, canProgress = true }) => { const [currentStep, setCurrentStep] = useState(0) const onBackClick = () => { From 8c7616556d535e877c2e43e95582238f8f39ecb7 Mon Sep 17 00:00:00 2001 From: Hamada Abdelaal Date: Fri, 19 Jul 2024 12:15:39 +0200 Subject: [PATCH 020/120] remove the debug log --- src/components/posts/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/posts/index.js b/src/components/posts/index.js index afd0d6e7..9c843fd8 100644 --- a/src/components/posts/index.js +++ b/src/components/posts/index.js @@ -4,7 +4,6 @@ import { getPosts } from "../../service/apiClient"; const Posts = () => { const [posts, setPosts] = useState([]); - console.log(posts); useEffect(() => { getPosts().then(setPosts); }, []); From b4188a35765758ebf74d8f07f55a908731987aca Mon Sep 17 00:00:00 2001 From: Leonardo Lodi Date: Fri, 19 Jul 2024 11:44:26 +0100 Subject: [PATCH 021/120] style: adjust position of icon --- src/styles/_form.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/_form.css b/src/styles/_form.css index d0e3af1a..c4af0ea6 100644 --- a/src/styles/_form.css +++ b/src/styles/_form.css @@ -23,7 +23,7 @@ form label { .input-icon { position: absolute; - left: 16px; + right: 16px; padding-top: 8px; } From 15cf7324fa3de9a37ad684c9c73e8467bf005a72 Mon Sep 17 00:00:00 2001 From: Myrthe Dullaart Date: Fri, 19 Jul 2024 14:27:36 +0200 Subject: [PATCH 022/120] fix navigation on create profile --- src/context/auth.js | 11 +++++++---- src/service/apiClient.js | 4 ++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/context/auth.js b/src/context/auth.js index f8b3857a..3266ee39 100644 --- a/src/context/auth.js +++ b/src/context/auth.js @@ -59,13 +59,16 @@ const AuthProvider = ({ children }) => { } }; - const handleCreateProfile = async (firstName, lastName, githubUrl, bio) => { + const handleCreateProfile = async (firstName, lastName, username, githubUsername, mobile, bio) => { const { userId } = jwt_decode(token); - await createProfile(userId, firstName, lastName, githubUrl, bio); - localStorage.setItem("token", token); - navigate("/"); + + const res= await createProfile(userId, firstName, lastName, username, githubUsername, mobile, bio); + + if (res.status === "succes") { + navigate("/") + } }; const value = { diff --git a/src/service/apiClient.js b/src/service/apiClient.js index 9b446600..ab9183cc 100644 --- a/src/service/apiClient.js +++ b/src/service/apiClient.js @@ -18,7 +18,7 @@ async function createProfile( firstName, lastName, username, - githubUrl, + githubUsername, mobile, bio ) { @@ -26,7 +26,7 @@ async function createProfile( firstName, lastName, username, - githubUrl, + githubUsername, mobile, bio, }); From 10d17f4cbb60dda49e3c95307aa0eec1086145ba Mon Sep 17 00:00:00 2001 From: Leonardo Lodi Date: Fri, 19 Jul 2024 15:20:52 +0100 Subject: [PATCH 023/120] fix: password bug not changing value --- src/components/form/textInput/index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/form/textInput/index.js b/src/components/form/textInput/index.js index 68b7448b..4c240fec 100644 --- a/src/components/form/textInput/index.js +++ b/src/components/form/textInput/index.js @@ -32,6 +32,10 @@ const TextInput = ({ type="text" name={name} value={input} + onChange={(e) => { + onChange(e); + setInput(e.target.value); + }} className="passwordreveal" /> )} From edd8af4543b6563d5719187243d9878173fe88b6 Mon Sep 17 00:00:00 2001 From: Myrthe Dullaart Date: Fri, 19 Jul 2024 16:43:30 +0200 Subject: [PATCH 024/120] fix typo --- src/context/auth.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/context/auth.js b/src/context/auth.js index 3266ee39..8fbbd2dd 100644 --- a/src/context/auth.js +++ b/src/context/auth.js @@ -66,7 +66,7 @@ const AuthProvider = ({ children }) => { const res= await createProfile(userId, firstName, lastName, username, githubUsername, mobile, bio); - if (res.status === "succes") { + if (res.status === "success") { navigate("/") } }; From 5715862a112144edbd04c005fcc7dbd9b2481f2c Mon Sep 17 00:00:00 2001 From: Angus Townsley Date: Mon, 22 Jul 2024 13:21:22 +0100 Subject: [PATCH 025/120] Locate hardcoded values --- src/components/createPostModal/index.js | 2 + src/components/editPostModal/index.js | 1 + src/components/header/index.js | 3 + src/pages/dashboard/index.js | 113 ++++++++++++------------ 4 files changed, 64 insertions(+), 55 deletions(-) diff --git a/src/components/createPostModal/index.js b/src/components/createPostModal/index.js index d77d996f..fb2d7241 100644 --- a/src/components/createPostModal/index.js +++ b/src/components/createPostModal/index.js @@ -23,6 +23,8 @@ const CreatePostModal = () => { }, 2000); }; + //TODO:Fix Hardcoded Text + return ( <>
diff --git a/src/components/editPostModal/index.js b/src/components/editPostModal/index.js index b21e5f86..19c884bf 100644 --- a/src/components/editPostModal/index.js +++ b/src/components/editPostModal/index.js @@ -20,6 +20,7 @@ const EditPostModal = () => { closeModal() }, 2000) } + //TODO: Fix hardcoded text return ( <> diff --git a/src/components/header/index.js b/src/components/header/index.js index 7b541381..7dea66ce 100644 --- a/src/components/header/index.js +++ b/src/components/header/index.js @@ -19,8 +19,11 @@ const Header = () => { if (!token) { return null; } + //TODO Fix hardcoded text return ( + +
diff --git a/src/pages/dashboard/index.js b/src/pages/dashboard/index.js index 8a65b832..d622767b 100644 --- a/src/pages/dashboard/index.js +++ b/src/pages/dashboard/index.js @@ -1,65 +1,68 @@ -import { useState } from "react"; -import SearchIcon from "../../assets/icons/searchIcon"; -import Button from "../../components/button"; -import Card from "../../components/card"; -import CreatePostModal from "../../components/createPostModal"; -import TextInput from "../../components/form/textInput"; -import Posts from "../../components/posts"; -import useModal from "../../hooks/useModal"; -import "./style.css"; +import { useState } from 'react' +import SearchIcon from '../../assets/icons/searchIcon' +import Button from '../../components/button' +import Card from '../../components/card' +import CreatePostModal from '../../components/createPostModal' +import TextInput from '../../components/form/textInput' +import Posts from '../../components/posts' +import useModal from '../../hooks/useModal' +import './style.css' const Dashboard = () => { - const [searchVal, setSearchVal] = useState(''); + const [searchVal, setSearchVal] = useState('') - const onChange = (e) => { - setSearchVal(e.target.value); - }; + const onChange = (e) => { + setSearchVal(e.target.value) + } - // Use the useModal hook to get the openModal and setModal functions - const { openModal, setModal } = useModal(); + // Use the useModal hook to get the openModal and setModal functions + const { openModal, setModal } = useModal() - // Create a function to run on user interaction - const showModal = () => { - // Use setModal to set the header of the modal and the component the modal should render - setModal("Create a post", ); // CreatePostModal is just a standard React component, nothing special + // Create a function to run on user interaction + const showModal = () => { + // Use setModal to set the header of the modal and the component the modal should render + setModal('Create a post', ) // CreatePostModal is just a standard React component, nothing special - // Open the modal! - openModal(); - }; + // Open the modal! + openModal() + } + //TODO Fix hardcoded text + return ( + <> +
+ +
+
+

AJ

+
+
+
- return ( - <> -
- -
-
-

AJ

-
-
-
+ +
- -
+ - - ); -}; - -export default Dashboard; +export default Dashboard From c9fa90906ed94dbecfcf1bfac012c4902e506af4 Mon Sep 17 00:00:00 2001 From: Angus Townsley Date: Mon, 22 Jul 2024 14:59:44 +0100 Subject: [PATCH 026/120] Create current user context and replace hardcoded data --- src/App.js | 90 ++++++++-------- src/components/createPostModal/index.js | 118 ++++++++++----------- src/components/editPostModal/index.js | 27 +++-- src/components/header/index.js | 131 ++++++++++++------------ src/components/profileCircle/index.js | 7 +- src/context/currentUser.js | 71 +++++++++++++ src/hooks/useUser.js | 8 ++ src/pages/dashboard/index.js | 6 +- src/service/apiClient.js | 6 +- 9 files changed, 285 insertions(+), 179 deletions(-) create mode 100644 src/context/currentUser.js create mode 100644 src/hooks/useUser.js diff --git a/src/App.js b/src/App.js index 4fe66c60..7d975ee9 100644 --- a/src/App.js +++ b/src/App.js @@ -1,46 +1,52 @@ -import "./App.css"; -import { Routes, Route } from "react-router-dom"; -import Dashboard from "./pages/dashboard"; -import Login from "./pages/login"; -import Register from "./pages/register"; -import Loading from "./pages/loading"; -import Verification from "./pages/verification"; -import { AuthProvider, ProtectedRoute } from "./context/auth"; -import { ModalProvider } from "./context/modal"; -import Welcome from "./pages/welcome"; +import './App.css' +import { Routes, Route } from 'react-router-dom' +import Dashboard from './pages/dashboard' +import Login from './pages/login' +import Register from './pages/register' +import Loading from './pages/loading' +import Verification from './pages/verification' +import { AuthProvider, ProtectedRoute } from './context/auth' +import { ModalProvider } from './context/modal' +import { CurrentUserProvider } from './context/currentUser' +import Welcome from './pages/welcome' const App = () => { - return ( - <> - - - - } /> - } /> - } /> - } /> + return ( + <> + + + + + } /> + } /> + } /> + } + /> - - - - } - /> - - - - } - /> - - - - - ); -}; + + + + } + /> + + + + } + /> + + + + + + ) +} -export default App; +export default App diff --git a/src/components/createPostModal/index.js b/src/components/createPostModal/index.js index fb2d7241..a6019343 100644 --- a/src/components/createPostModal/index.js +++ b/src/components/createPostModal/index.js @@ -1,61 +1,61 @@ -import { useState } from "react"; -import useModal from "../../hooks/useModal"; -import "./style.css"; -import Button from "../button"; +import { useState } from 'react' +import useModal from '../../hooks/useModal' +import './style.css' +import Button from '../button' +import useUser from '../../hooks/useUser' const CreatePostModal = () => { - // Use the useModal hook to get the closeModal function so we can close the modal on user interaction - const { closeModal } = useModal(); - - const [message, setMessage] = useState(null); - const [text, setText] = useState(""); - - const onChange = (e) => { - setText(e.target.value); - }; - - const onSubmit = () => { - setMessage("Submit button was clicked! Closing modal in 2 seconds..."); - - setTimeout(() => { - setMessage(null); - closeModal(); - }, 2000); - }; - - //TODO:Fix Hardcoded Text - - return ( - <> -
-
-

AJ

-
-
-

Alex J

-
-
- -
- -
- -
-
- - {message &&

{message}

} - - ); -}; - -export default CreatePostModal; + // Use the useModal hook to get the closeModal function so we can close the modal on user interaction + const { closeModal } = useModal() + const { userInitials, userFirstNameAndInital } = useUser() + + const [message, setMessage] = useState(null) + const [text, setText] = useState('') + + const onChange = (e) => { + setText(e.target.value) + } + + const onSubmit = () => { + setMessage('Submit button was clicked! Closing modal in 2 seconds...') + + setTimeout(() => { + setMessage(null) + closeModal() + }, 2000) + } + + return ( + <> +
+
+

{userInitials}

+
+
+

{userFirstNameAndInital}

+
+
+ +
+ +
+ +
+
+ + {message &&

{message}

} + + ) +} + +export default CreatePostModal diff --git a/src/components/editPostModal/index.js b/src/components/editPostModal/index.js index 19c884bf..efa167fe 100644 --- a/src/components/editPostModal/index.js +++ b/src/components/editPostModal/index.js @@ -1,12 +1,14 @@ -import { useState } from "react" -import useModal from "../../hooks/useModal" +import { useState } from 'react' +import useModal from '../../hooks/useModal' import './style.css' import Button from '../button' +import useUser from '../../hooks/useUser' const EditPostModal = () => { const { closeModal } = useModal() const [message, setMessage] = useState(null) const [text, setText] = useState('') + const { userInitials, userFirstNameAndInital } = useUser() const onChange = (e) => { setText(e.target.value) @@ -20,24 +22,31 @@ const EditPostModal = () => { closeModal() }, 2000) } - //TODO: Fix hardcoded text return ( <>
-

AJ

-

Alex J

+
+

{userInitials}

+
+
+

{userFirstNameAndInital}

+
- +
@@ -47,4 +56,4 @@ const EditPostModal = () => { ) } -export default EditPostModal \ No newline at end of file +export default EditPostModal diff --git a/src/components/header/index.js b/src/components/header/index.js index 7dea66ce..2daec5df 100644 --- a/src/components/header/index.js +++ b/src/components/header/index.js @@ -1,74 +1,77 @@ -import FullLogo from "../../assets/fullLogo"; -import useAuth from "../../hooks/useAuth"; -import "./style.css"; -import Card from "../card"; -import ProfileIcon from "../../assets/icons/profileIcon"; -import CogIcon from "../../assets/icons/cogIcon"; -import LogoutIcon from "../../assets/icons/logoutIcon"; -import { NavLink } from "react-router-dom"; -import { useState } from "react"; +import FullLogo from '../../assets/fullLogo' +import useAuth from '../../hooks/useAuth' +import './style.css' +import Card from '../card' +import ProfileIcon from '../../assets/icons/profileIcon' +import CogIcon from '../../assets/icons/cogIcon' +import LogoutIcon from '../../assets/icons/logoutIcon' +import { NavLink } from 'react-router-dom' +import { useState } from 'react' +import useUser from '../../hooks/useUser' const Header = () => { - const { token, onLogout } = useAuth(); - const [isMenuVisible, setIsMenuVisible] = useState(false); + const { token, onLogout } = useAuth() + const [isMenuVisible, setIsMenuVisible] = useState(false) + const { currentUser, userFullName, userInitials, userCohort } = useUser() - const onClickProfileIcon = () => { - setIsMenuVisible(!isMenuVisible); - }; + const onClickProfileIcon = () => { + setIsMenuVisible(!isMenuVisible) + } - if (!token) { - return null; - } - //TODO Fix hardcoded text + if (!token) { + return null + } - return ( - - -
- + return ( +
+ -
-

AJ

-
+
+

{userInitials}

+
- {isMenuVisible && ( -
- -
-
-

AJ

-
+ {isMenuVisible && ( +
+ +
+
+

{userInitials}

+
-
-

Alex Jameson

- Software Developer, Cohort 3 -
-
+
+

{userFullName}

+ + {currentUser.role},{' '} + {userCohort} + +
+
-
-
    -
  • - -

    Profile

    -
    -
  • -
  • - -

    Settings & Privacy

    -
    -
  • -
  • - -

    Log out

    -
    -
  • -
-
-
-
- )} -
- ); -}; +
+
    +
  • + +

    Profile

    +
    +
  • +
  • + + {' '} +

    Settings & Privacy

    +
    +
  • +
  • + +

    Log out

    +
    +
  • +
+
+ + + )} +
+ ) +} -export default Header; +export default Header diff --git a/src/components/profileCircle/index.js b/src/components/profileCircle/index.js index fa5237ef..5a812bd4 100644 --- a/src/components/profileCircle/index.js +++ b/src/components/profileCircle/index.js @@ -12,13 +12,16 @@ import './style.css' const ProfileCircle = ({ initials }) => { const [isMenuVisible, setIsMenuVisible] = useState(false) - + const uppercaseInitals = initials.map((element) => { + return element.toUpperCase() + }) + return (
setIsMenuVisible(!isMenuVisible)}> {isMenuVisible && }
-

{initials}

+

{uppercaseInitals}

diff --git a/src/context/currentUser.js b/src/context/currentUser.js new file mode 100644 index 00000000..d5defd8b --- /dev/null +++ b/src/context/currentUser.js @@ -0,0 +1,71 @@ +import { createContext, useEffect, useState } from 'react' +import useAuth from '../hooks/useAuth' +import jwt_decode from 'jwt-decode' +import { getUser } from '../service/apiClient' + +export const CurrentUserContext = createContext() + +export const CurrentUserProvider = ({ children }) => { + const { token } = useAuth() + const [currentUser, setCurrentUser] = useState({ empty: true }) + const [userInitials, setUserInitals] = useState('') + const [userFullName, setUserFullName] = useState('') + const [userCohort, setUserCohort] = useState('') + const [userFirstNameAndInital, setUserFirstNameAndInital] = useState('') + + useEffect(() => { + async function getUserFromToken() { + if (token) { + const { userId } = jwt_decode(token) + + const userDetails = await getUser(userId) + userDetails.status === 'success' + ? setCurrentUser({ ...userDetails.data.user }) + : setCurrentUser({ empty: true }) + return + } + setCurrentUser({ empty: true }) + return + } + getUserFromToken() + }, [token]) + + useEffect(() => { + if (!currentUser.empty) { + setUserInitals( + currentUser + ? `${currentUser.firstName[0].toUpperCase()}${currentUser.lastName[0].toUpperCase()}` + : '' + ) + setUserFullName( + currentUser + ? `${currentUser.firstName} ${currentUser.lastName}` + : '' + ) + setUserCohort( + currentUser && currentUser.cohort + ? 'Cohort' + currentUser.cohort + : '' + ) + setUserFirstNameAndInital( + currentUser + ? `${currentUser.firstName} ${currentUser.lastName[0]}` + : '' + ) + } + }, [currentUser]) + + return ( + + {children} + + ) +} diff --git a/src/hooks/useUser.js b/src/hooks/useUser.js new file mode 100644 index 00000000..a4035a0c --- /dev/null +++ b/src/hooks/useUser.js @@ -0,0 +1,8 @@ +import { useContext } from "react" +import { CurrentUserContext } from "../context/currentUser" + +const useUser = () => { + return useContext(CurrentUserContext) +} + +export default useUser \ No newline at end of file diff --git a/src/pages/dashboard/index.js b/src/pages/dashboard/index.js index d622767b..1a6a0cc1 100644 --- a/src/pages/dashboard/index.js +++ b/src/pages/dashboard/index.js @@ -7,9 +7,11 @@ import TextInput from '../../components/form/textInput' import Posts from '../../components/posts' import useModal from '../../hooks/useModal' import './style.css' +import useUser from '../../hooks/useUser' const Dashboard = () => { const [searchVal, setSearchVal] = useState('') + const { userInitials } = useUser() const onChange = (e) => { setSearchVal(e.target.value) @@ -26,14 +28,14 @@ const Dashboard = () => { // Open the modal! openModal() } - //TODO Fix hardcoded text + return ( <>
-

AJ

+

{userInitials}

+
@@ -88,33 +107,47 @@ const AllSearchResults = () => { )} {results.length > 0 && (
    - {results.map((user) => ( + {results.map((user) => (
  • -
    - {`${user.firstName} ${user.lastName}`} -

    Software Developer

    -
    + {currentUser.role === 'STUDENT' && ( + <> +
    + {`${user.firstName} ${user.lastName}`} +

    Software Developer

    +
    -
    -

    Profile

    -
    +
    +

    Profile

    +
    -
    onClickStudent(cohort.id)}> - +

    test

    +

    test2

    + + )} + + {selectedProfileId === user.id && ( + + } text="Profile" /> + + )} + +
    onClickStudentMenu(user.id)}> +
  • ))}
)} - +
- -
+ + ); diff --git a/src/pages/allSearchResults/style.css b/src/pages/allSearchResults/style.css index e246b748..f3e3a526 100644 --- a/src/pages/allSearchResults/style.css +++ b/src/pages/allSearchResults/style.css @@ -51,9 +51,9 @@ .found-user-card { display: grid; gap: 1rem; - grid-template-columns: 1fr 5fr 1fr 1fr; + grid-template-columns: 1fr 5fr 1fr 1fr 1fr 1fr; align-items: center; - justify-content: center; + justify-items: stretch } .found-user-details p { diff --git a/src/pages/dashboard/index.jsx b/src/pages/dashboard/index.jsx index f0fdbf9f..502ae45a 100644 --- a/src/pages/dashboard/index.jsx +++ b/src/pages/dashboard/index.jsx @@ -36,8 +36,6 @@ const Dashboard = () => { setSearchVal(e.target.value) } - // console.log(cohorts); - const result = cohorts.filter((cohort) => { if (cohort.firstName && cohort.lastName) { const fullName = `${cohort.firstName || ''} ${ From 057db56ed4171492418df40d2e347492706627d1 Mon Sep 17 00:00:00 2001 From: Periklis Date: Fri, 26 Jul 2024 23:20:29 +0300 Subject: [PATCH 100/120] some formating in the return block --- src/pages/allSearchResults/index.jsx | 142 +++++++++++++-------------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/src/pages/allSearchResults/index.jsx b/src/pages/allSearchResults/index.jsx index dc184eb4..bbdfeb20 100644 --- a/src/pages/allSearchResults/index.jsx +++ b/src/pages/allSearchResults/index.jsx @@ -73,81 +73,81 @@ const AllSearchResults = () => { return ( <>
-
- -
-
-
-
- - - -

Search results

-
-
- -
- - -
-
-
-
+
+ +
+
+
+
+ + + +

Search results

+
+
+ +
+ + +
+
+
+
-
- - {results.length === 0 && ( -

No results found.

- )} - {results.length > 0 && ( -
    - {results.map((user) => ( -
  • - +
    + + {results.length === 0 && ( +

    No results found.

    + )} + {results.length > 0 && ( +
      + {results.map((user) => ( +
    • + - {currentUser.role === 'STUDENT' && ( - <> -
      - {`${user.firstName} ${user.lastName}`} -

      Software Developer

      -
      + {currentUser.role === 'STUDENT' && ( + <> +
      + {`${user.firstName} ${user.lastName}`} +

      Software Developer

      +
      -
      -

      Profile

      -
      +
      +

      Profile

      +
      -

      test

      -

      test2

      - - )} - - {selectedProfileId === user.id && ( - - } text="Profile" /> - - )} - -
      onClickStudentMenu(user.id)}> - -
      -
    • - ))} -
    - )} -
    -
    -
-
+

test

+

test2

+ + )} + + {selectedProfileId === user.id && ( + + } text="Profile" /> + + )} + +
onClickStudentMenu(user.id)}> + +
+ + ))} + + )} + +
+
+ ); From 92f9d123d23e2d8e6a614e97dc05d1711eea017c Mon Sep 17 00:00:00 2001 From: Periklis Date: Fri, 26 Jul 2024 23:22:30 +0300 Subject: [PATCH 101/120] more formating in the return block --- src/pages/allSearchResults/index.jsx | 132 +++++++++++++-------------- 1 file changed, 66 insertions(+), 66 deletions(-) diff --git a/src/pages/allSearchResults/index.jsx b/src/pages/allSearchResults/index.jsx index bbdfeb20..5279938b 100644 --- a/src/pages/allSearchResults/index.jsx +++ b/src/pages/allSearchResults/index.jsx @@ -76,77 +76,77 @@ const AllSearchResults = () => {
-
-
-
- - - -

Search results

-
-
- -
- - -
-
-
-
+
+
+
+ + + +

Search results

+
+
+ +
+ + +
+
+
+
-
- - {results.length === 0 && ( -

No results found.

- )} - {results.length > 0 && ( -
    - {results.map((user) => ( -
  • - +
    + + {results.length === 0 && ( +

    No results found.

    + )} + {results.length > 0 && ( +
      + {results.map((user) => ( +
    • + - {currentUser.role === 'STUDENT' && ( - <> -
      - {`${user.firstName} ${user.lastName}`} -

      Software Developer

      -
      + {currentUser.role === 'STUDENT' && ( + <> +
      + {`${user.firstName} ${user.lastName}`} +

      Software Developer

      +
      -
      -

      Profile

      -
      +
      +

      Profile

      +
      -

      test

      -

      test2

      - - )} - - {selectedProfileId === user.id && ( - - } text="Profile" /> - - )} - -
      onClickStudentMenu(user.id)}> - -
      -
    • - ))} -
    - )} -
    -
    +

    test

    +

    test2

    + + )} + + {selectedProfileId === user.id && ( + + } text="Profile" /> + + )} + +
    onClickStudentMenu(user.id)}> + +
    +
  • + ))} +
+ )} +
+
From ad1b0d35974e67a6d0e05d94ea812944a45e1fa6 Mon Sep 17 00:00:00 2001 From: Periklis Date: Sat, 27 Jul 2024 14:04:28 +0300 Subject: [PATCH 102/120] create separate classes for li elements according to current user role --- src/pages/allSearchResults/index.jsx | 41 ++++++++++++++-------------- src/pages/allSearchResults/style.css | 18 ++++++++++-- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/src/pages/allSearchResults/index.jsx b/src/pages/allSearchResults/index.jsx index 5279938b..6c6461ed 100644 --- a/src/pages/allSearchResults/index.jsx +++ b/src/pages/allSearchResults/index.jsx @@ -108,27 +108,21 @@ const AllSearchResults = () => { {results.length > 0 && (
    {results.map((user) => ( -
  • - +
  • + - {currentUser.role === 'STUDENT' && ( - <> -
    - {`${user.firstName} ${user.lastName}`} -

    Software Developer

    -
    - -
    -

    Profile

    -
    - -

    test

    -

    test2

    - - )} +
    + {`${user.firstName} ${user.lastName}`} +

    Software Developer

    +
    +
    +

    Profile

    +
    {selectedProfileId === user.id && ( @@ -136,6 +130,13 @@ const AllSearchResults = () => { )} + {currentUser.role === 'TEACHER' && ( + <> +

    test

    +

    test2

    + + )} +
    onClickStudentMenu(user.id)}> diff --git a/src/pages/allSearchResults/style.css b/src/pages/allSearchResults/style.css index f3e3a526..6dd514cc 100644 --- a/src/pages/allSearchResults/style.css +++ b/src/pages/allSearchResults/style.css @@ -47,13 +47,24 @@ grid-template-rows: auto; } +.found-user-card-stdnt { + display: grid; + gap: 1rem; + grid-template-columns: 1fr 5fr 1fr 1fr; + align-items: center; + justify-items: stretch; +} -.found-user-card { +.found-user-card-tchr { display: grid; gap: 1rem; - grid-template-columns: 1fr 5fr 1fr 1fr 1fr 1fr; + grid-template-columns: 1fr 4fr 1fr 1fr 1fr 1fr; align-items: center; - justify-items: stretch + justify-items: stretch; +} + +.found-user-details p { + color: #64648c } .found-user-details p { @@ -64,6 +75,7 @@ display: grid; align-items: center; justify-content: center; + justify-self: end; background-color: #f0f5fa; width: 2.5rem; height: 2.5rem; From 8ab666e93e67556e702e49502e4ac546aeccabd2 Mon Sep 17 00:00:00 2001 From: Periklis Date: Sat, 27 Jul 2024 17:41:12 +0300 Subject: [PATCH 103/120] change header logout navlink to /login --- src/components/header/index.jsx | 4 ++-- src/pages/allSearchResults/index.jsx | 36 ++++++++++++++-------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/components/header/index.jsx b/src/components/header/index.jsx index 5f52dd8e..0930bed8 100644 --- a/src/components/header/index.jsx +++ b/src/components/header/index.jsx @@ -11,7 +11,7 @@ import UserProfileIcon from '../UserProfileIcon' import UserDetails from '../UserDetails' const Header = () => { - const { token, onLogout, useClickOutside } = useAuth() + const { token, onLogout,onLogin, useClickOutside } = useAuth() const [isMenuVisible, setIsMenuVisible] = useState(false) const profileIconRef = useRef(null) @@ -52,7 +52,7 @@ const Header = () => {
  • - +

    Log out

  • diff --git a/src/pages/allSearchResults/index.jsx b/src/pages/allSearchResults/index.jsx index 6c6461ed..6f3593e5 100644 --- a/src/pages/allSearchResults/index.jsx +++ b/src/pages/allSearchResults/index.jsx @@ -1,4 +1,4 @@ -import {useLocation, Link } from 'react-router-dom'; +import {useLocation, Link, NavLink } from 'react-router-dom'; import { useState, useEffect, useRef } from 'react'; import useUser from '../../hooks/useUser'; import EllipsisIcon from '../../assets/icons/ellipsisIcon' @@ -110,7 +110,7 @@ const AllSearchResults = () => { {results.map((user) => (
  • + }`}> {

    Profile

    - {selectedProfileId === user.id && ( - - } text="Profile" /> - - )} + {selectedProfileId === user.id && ( + + } text="Profile" /> + + )} - {currentUser.role === 'TEACHER' && ( - <> -

    test

    -

    test2

    - - )} - -
    onClickStudentMenu(user.id)}> - -
    + {currentUser.role === 'TEACHER' && ( + <> +

    test

    }>
    +

    test

    }>
    + + )} + +
    onClickStudentMenu(user.id)}> + +
  • ))}
From 608964e4aa9ef6bf328499e449dda4d195824ba0 Mon Sep 17 00:00:00 2001 From: Periklis Date: Sat, 27 Jul 2024 19:24:46 +0300 Subject: [PATCH 104/120] almost done -a css problem --- src/components/header/index.jsx | 2 +- src/pages/allSearchResults/index.jsx | 13 +++++----- src/pages/allSearchResults/style.css | 37 +++++++++++++++++++++------- 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/components/header/index.jsx b/src/components/header/index.jsx index 0930bed8..218c8f6d 100644 --- a/src/components/header/index.jsx +++ b/src/components/header/index.jsx @@ -11,7 +11,7 @@ import UserProfileIcon from '../UserProfileIcon' import UserDetails from '../UserDetails' const Header = () => { - const { token, onLogout,onLogin, useClickOutside } = useAuth() + const { token, onLogout, useClickOutside } = useAuth() const [isMenuVisible, setIsMenuVisible] = useState(false) const profileIconRef = useRef(null) diff --git a/src/pages/allSearchResults/index.jsx b/src/pages/allSearchResults/index.jsx index 6f3593e5..78787549 100644 --- a/src/pages/allSearchResults/index.jsx +++ b/src/pages/allSearchResults/index.jsx @@ -22,7 +22,7 @@ const AllSearchResults = () => { const [searchVal, setSearchVal] = useState(initialSearchVal); const [results, setResults] = useState(initialResults); const [selectedProfileId, setSelectedProfileId] = useState(null) - + useEffect(() => { getUsers() window.scrollTo(0, 0); @@ -68,6 +68,7 @@ const AllSearchResults = () => { const onClickStudentMenu = (id) => { setSelectedProfileId(prevId => prevId === id ? null : id); + console.log(id); }; return ( @@ -120,7 +121,7 @@ const AllSearchResults = () => { {`${user.firstName} ${user.lastName}`}

Software Developer

-
+

Profile

@@ -131,10 +132,10 @@ const AllSearchResults = () => { )} {currentUser.role === 'TEACHER' && ( - <> -

test

}>
-

test

}>
- +
+ Add Note + Move to Cohort +
)}
Date: Sat, 27 Jul 2024 20:17:07 +0300 Subject: [PATCH 105/120] css sketchy but seems to work --- src/pages/allSearchResults/style.css | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/pages/allSearchResults/style.css b/src/pages/allSearchResults/style.css index a2cb79f5..ee2f8637 100644 --- a/src/pages/allSearchResults/style.css +++ b/src/pages/allSearchResults/style.css @@ -79,10 +79,16 @@ color: #64648c; } +.found-user-profile-link { + display: flex; + align-items: right; +} + .found-user-profile-link p { - display: block; color: #64648c; cursor: pointer; + margin-left: auto; + padding-right: 7rem; } .teacher-links span { @@ -90,6 +96,10 @@ cursor: pointer; } +a { + text-decoration: none; +} + .link-to-profile { display: grid; align-items: center; From 1ef51289891e47b69c4d21ffc92827381ed7a40e Mon Sep 17 00:00:00 2001 From: Periklis Date: Sun, 28 Jul 2024 01:48:23 +0300 Subject: [PATCH 106/120] create three dots menu component for teacher view --- src/components/threeDotsMenu/index.jsx | 75 ++++++++++++++++++++++++++ src/components/threeDotsMenu/style.css | 8 +++ src/pages/allSearchResults/index.jsx | 55 ++++++++++++------- src/pages/allSearchResults/style.css | 58 ++++++++++++++++++-- 4 files changed, 172 insertions(+), 24 deletions(-) create mode 100644 src/components/threeDotsMenu/index.jsx create mode 100644 src/components/threeDotsMenu/style.css diff --git a/src/components/threeDotsMenu/index.jsx b/src/components/threeDotsMenu/index.jsx new file mode 100644 index 00000000..68b8a168 --- /dev/null +++ b/src/components/threeDotsMenu/index.jsx @@ -0,0 +1,75 @@ +import { useRef, useState } from 'react' +import EllipsisIcon from '../../assets/icons/ellipsisIcon' +import AddIcon from '../../assets/icons/addIcon' +import CohortIcon from '../../assets/icons/cohortIcon' +import CohortIconFill from '../../assets/icons/cohortIcon-fill' +import DeleteIcon from '../../assets/icons/deleteIcon' +import MonitorIcon from '../../assets/icons/monitorIcon' +import ProfileIcon from '../../assets/icons/profileIcon' +import SquareBracketsIcon from '../../assets/icons/squareBracketsIcon' +import Menu from '../menu' +import MenuItem from '../menu/menuItem' +import './style.css' +import useAuth from '../../hooks/useAuth' + +const ThreeDotsMenu = ({ id, hasCascadingMenu = true }) => { + const { useClickOutside } = useAuth() + const [isMenuVisible, setIsMenuVisible] = useState(false) + const profileRef = useRef(null) + + useClickOutside(profileRef, () => { + setIsMenuVisible(false) + }) + + const cursor = hasCascadingMenu ? 'pointer' : 'default' + + const renderCascadingMenu = () => { + if (isMenuVisible && hasCascadingMenu) { + return + } + return null + } + + return ( +
+
{setIsMenuVisible(!isMenuVisible)}} + style={{ cursor: cursor }} + > + {renderCascadingMenu()} + +
+ +
+
+
+ ) +} + +const CascadingMenu = () => { + return ( + + } text="Profile" /> + } text="Add note" /> + + } text="Move to cohort"> + } text="Software Development"> + } text="Cohort 1" /> + } text="Cohort 2" /> + } text="Cohort 3" /> + + + } text="Frontend Development"> + } text="Cohort 1" /> + } text="Cohort 2" /> + } text="Cohort 3" /> + + + + } text="Delete student" /> + + ) +} + +export default ThreeDotsMenu \ No newline at end of file diff --git a/src/components/threeDotsMenu/style.css b/src/components/threeDotsMenu/style.css new file mode 100644 index 00000000..fb3323ef --- /dev/null +++ b/src/components/threeDotsMenu/style.css @@ -0,0 +1,8 @@ +.profile-circle { + position: relative; + cursor: pointer; +} + +.profile-circle-menu { + margin-left: 65px; +} \ No newline at end of file diff --git a/src/pages/allSearchResults/index.jsx b/src/pages/allSearchResults/index.jsx index 78787549..713bb1da 100644 --- a/src/pages/allSearchResults/index.jsx +++ b/src/pages/allSearchResults/index.jsx @@ -2,6 +2,7 @@ import {useLocation, Link, NavLink } from 'react-router-dom'; import { useState, useEffect, useRef } from 'react'; import useUser from '../../hooks/useUser'; import EllipsisIcon from '../../assets/icons/ellipsisIcon' +import ThreeDotsMenu from '../../components/threeDotsMenu'; import SearchIcon from '../../assets/icons/searchIcon'; import ArrowLeftIcon from '../../assets/icons/arrowLeftIcon'; import ProfileCircle from '../../components/profileCircle'; @@ -22,6 +23,7 @@ const AllSearchResults = () => { const [searchVal, setSearchVal] = useState(initialSearchVal); const [results, setResults] = useState(initialResults); const [selectedProfileId, setSelectedProfileId] = useState(null) + const [menuPosition, setMenuPosition] = useState({ top: 0, right: 0 }); useEffect(() => { getUsers() @@ -54,9 +56,9 @@ const AllSearchResults = () => { }; const handleClickOutside = (event) => { - if (menuRef.current && !menuRef.current.contains(event.target) && !event.target.closest('.link-to-profile')) { - setSelectedProfileId(null); - } + if (menuRef.current && !menuRef.current.contains(event.target) && !event.target.closest('.link-to-profile')) { + setSelectedProfileId(null); + } }; useEffect(() => { @@ -66,18 +68,24 @@ const AllSearchResults = () => { } }, []) - const onClickStudentMenu = (id) => { - setSelectedProfileId(prevId => prevId === id ? null : id); - console.log(id); - }; + // const onClickStudentMenu = (id, event) => { + // event.stopPropagation(); + // const rect = event.currentTarget.getBoundingClientRect(); + // setMenuPosition({ + // top: rect.top + window.scrollY, + // right: window.innerWidth - rect.left, + // }); + // setSelectedProfileId(prevId => prevId === id ? null : id); + // console.log(id); + // }; return ( <> -
-
+
+
-
+
@@ -125,23 +133,30 @@ const AllSearchResults = () => {

Profile

- {selectedProfileId === user.id && ( - - } text="Profile" /> - - )} - {currentUser.role === 'TEACHER' && (
Add Note Move to Cohort
)} - -
onClickStudentMenu(user.id)}> + + {/*
onClickStudentMenu(user.id, event)}> -
+ {selectedProfileId === user.id && ( + + } text="Profile" /> + + )} +
*/} ))} diff --git a/src/pages/allSearchResults/style.css b/src/pages/allSearchResults/style.css index ee2f8637..e8a54562 100644 --- a/src/pages/allSearchResults/style.css +++ b/src/pages/allSearchResults/style.css @@ -1,7 +1,33 @@ +.search-page-header { + grid-column: 1 / -1; + position: sticky; + top: 0; + z-index: 10; +} + +.left-bar { + position: sticky; + top: 0; + height: 100vh; + overflow-y: auto; +} + +.search-page-container { + display: grid; + grid-template-columns: auto 1fr; + grid-template-rows: auto 1fr; + height: 100vh; + background-color: #f0f5fa; +} + .search-results-container { - display: inline; - margin-left: 6rem; - /* width: full%; */ + margin-left: 4rem; + width: full; + overflow-y: auto; +} + +.search-content { + width:70% } .title { @@ -100,7 +126,8 @@ a { text-decoration: none; } -.link-to-profile { +.asr-link-to-profile { + position: relative; display: grid; align-items: center; justify-content: center; @@ -110,4 +137,27 @@ a { height: 2.5rem; border-radius: 1.5rem; cursor: pointer; +} + +.allSearchResults-profile-circle-menu { + position: absolute; + right: 100%; + top: 50%; + transform: translateY(-50%); + margin-right: 10px; + background-color: white; + border: 1px solid #ccc; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + z-index: 1000; +} +.search-three-dots-menu { + position: absolute; + right: 100%; + top: 50%; + transform: translateY(-50%); + margin-right: 10px; + background-color: white; + border: 1px solid #ccc; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + z-index: 1000; } \ No newline at end of file From 0bc6ee8ec1e01919f4ae8f4fc74bcc4f123b1822 Mon Sep 17 00:00:00 2001 From: Periklis Date: Sun, 28 Jul 2024 02:35:24 +0300 Subject: [PATCH 107/120] add conditional rendering and proper styling for 3 dots menu --- src/components/threeDotsMenu/index.jsx | 16 +++---- src/components/threeDotsMenu/style.css | 4 +- src/pages/allSearchResults/index.jsx | 63 ++++++++++++++------------ src/pages/allSearchResults/style.css | 22 +++++---- 4 files changed, 55 insertions(+), 50 deletions(-) diff --git a/src/components/threeDotsMenu/index.jsx b/src/components/threeDotsMenu/index.jsx index 68b8a168..1eb2e628 100644 --- a/src/components/threeDotsMenu/index.jsx +++ b/src/components/threeDotsMenu/index.jsx @@ -9,7 +9,7 @@ import ProfileIcon from '../../assets/icons/profileIcon' import SquareBracketsIcon from '../../assets/icons/squareBracketsIcon' import Menu from '../menu' import MenuItem from '../menu/menuItem' -import './style.css' +// import './style.css' import useAuth from '../../hooks/useAuth' const ThreeDotsMenu = ({ id, hasCascadingMenu = true }) => { @@ -21,11 +21,11 @@ const ThreeDotsMenu = ({ id, hasCascadingMenu = true }) => { setIsMenuVisible(false) }) - const cursor = hasCascadingMenu ? 'pointer' : 'default' + // const cursor = hasCascadingMenu ? 'pointer' : 'default' const renderCascadingMenu = () => { - if (isMenuVisible && hasCascadingMenu) { - return + if (isMenuVisible && hasCascadingMenu) { + return } return null } @@ -33,15 +33,11 @@ const ThreeDotsMenu = ({ id, hasCascadingMenu = true }) => { return (
{setIsMenuVisible(!isMenuVisible)}} - style={{ cursor: cursor }} > {renderCascadingMenu()} - -
- -
+
) diff --git a/src/components/threeDotsMenu/style.css b/src/components/threeDotsMenu/style.css index fb3323ef..e8f9afaf 100644 --- a/src/components/threeDotsMenu/style.css +++ b/src/components/threeDotsMenu/style.css @@ -1,8 +1,8 @@ -.profile-circle { +/* .profile-circle { position: relative; cursor: pointer; } .profile-circle-menu { margin-left: 65px; -} \ No newline at end of file +} */ \ No newline at end of file diff --git a/src/pages/allSearchResults/index.jsx b/src/pages/allSearchResults/index.jsx index 713bb1da..b69f2ef4 100644 --- a/src/pages/allSearchResults/index.jsx +++ b/src/pages/allSearchResults/index.jsx @@ -68,16 +68,16 @@ const AllSearchResults = () => { } }, []) - // const onClickStudentMenu = (id, event) => { - // event.stopPropagation(); - // const rect = event.currentTarget.getBoundingClientRect(); - // setMenuPosition({ - // top: rect.top + window.scrollY, - // right: window.innerWidth - rect.left, - // }); - // setSelectedProfileId(prevId => prevId === id ? null : id); - // console.log(id); - // }; + const onClickStudentMenu = (id, event) => { + event.stopPropagation(); + const rect = event.currentTarget.getBoundingClientRect(); + setMenuPosition({ + top: rect.top + window.scrollY, + right: window.innerWidth - rect.left, + }); + setSelectedProfileId(prevId => prevId === id ? null : id); + console.log(id); + }; return ( <> @@ -134,29 +134,36 @@ const AllSearchResults = () => {
{currentUser.role === 'TEACHER' && ( + <>
Add Note Move to Cohort
+
+ +
+ + )} + + {currentUser.role === 'STUDENT' && ( +
onClickStudentMenu(user.id, event)}> + + {selectedProfileId === user.id && ( + + } text="Profile" /> + + )} +
)} - - {/*
onClickStudentMenu(user.id, event)}> - - {selectedProfileId === user.id && ( - - } text="Profile" /> - - )} -
*/} ))} diff --git a/src/pages/allSearchResults/style.css b/src/pages/allSearchResults/style.css index e8a54562..6bbb9551 100644 --- a/src/pages/allSearchResults/style.css +++ b/src/pages/allSearchResults/style.css @@ -27,7 +27,7 @@ } .search-content { - width:70% + width: 70% } .title { @@ -150,14 +150,16 @@ a { box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); z-index: 1000; } + .search-three-dots-menu { - position: absolute; - right: 100%; - top: 50%; - transform: translateY(-50%); - margin-right: 10px; - background-color: white; - border: 1px solid #ccc; - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); - z-index: 1000; + position: relative; + display: grid; + align-items: center; + justify-content: center; + justify-self: end; + background-color: #f0f5fa; + width: 2.5rem; + height: 2.5rem; + border-radius: 1.5rem; + cursor: pointer; } \ No newline at end of file From 3b3ddeb2e89a3e3cbda83cfcfef2d734f1930edb Mon Sep 17 00:00:00 2001 From: Periklis Date: Sun, 28 Jul 2024 03:06:19 +0300 Subject: [PATCH 108/120] add cohort id in found user details -styling closer to design --- src/pages/allSearchResults/index.jsx | 4 ++-- src/pages/allSearchResults/style.css | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/pages/allSearchResults/index.jsx b/src/pages/allSearchResults/index.jsx index b69f2ef4..885920da 100644 --- a/src/pages/allSearchResults/index.jsx +++ b/src/pages/allSearchResults/index.jsx @@ -127,10 +127,10 @@ const AllSearchResults = () => {
{`${user.firstName} ${user.lastName}`} -

Software Developer

+

{`Software Developer, Cohort ${user.cohortId}`}

-

Profile

+

Profile

{currentUser.role === 'TEACHER' && ( diff --git a/src/pages/allSearchResults/style.css b/src/pages/allSearchResults/style.css index 6bbb9551..3a39e34d 100644 --- a/src/pages/allSearchResults/style.css +++ b/src/pages/allSearchResults/style.css @@ -83,7 +83,7 @@ .found-user-card-tchr { display: grid; - grid-template-columns: auto 0.5fr 1fr 1fr 0.25fr; + grid-template-columns: auto 1fr 1fr 1fr 0.25fr; align-items: center; } @@ -97,6 +97,8 @@ } .found-user-details { + /* display: grid; + grid-template-rows: 1fr 1fr; */ color: #64648c; padding-left: 1rem; } From 00e91d08342e6d98cc384260d374fa93c36b887f Mon Sep 17 00:00:00 2001 From: Periklis Date: Sun, 28 Jul 2024 13:54:39 +0300 Subject: [PATCH 109/120] done except menu -it must pop-up left --- src/components/simpleThreeDotsMenu/index.jsx | 44 ++++ src/components/threeDotsMenu/index.jsx | 19 +- src/components/threeDotsMenu/style.css | 8 - src/pages/allSearchResults/index.jsx | 217 +++++++++---------- src/pages/allSearchResults/style.css | 89 ++++---- 5 files changed, 204 insertions(+), 173 deletions(-) create mode 100644 src/components/simpleThreeDotsMenu/index.jsx diff --git a/src/components/simpleThreeDotsMenu/index.jsx b/src/components/simpleThreeDotsMenu/index.jsx new file mode 100644 index 00000000..c2e82805 --- /dev/null +++ b/src/components/simpleThreeDotsMenu/index.jsx @@ -0,0 +1,44 @@ +import { useRef, useState } from 'react' +import EllipsisIcon from '../../assets/icons/ellipsisIcon' +import ProfileIcon from '../../assets/icons/profileIcon' +import Menu from '../menu' +import MenuItem from '../menu/menuItem' +import useAuth from '../../hooks/useAuth' + +const SimpleThreeDotsMenu = ({ id, hasCascadingMenu = true }) => { + const { useClickOutside } = useAuth() + const [isMenuVisible, setIsMenuVisible] = useState(false) + const profileRef = useRef(null) + + useClickOutside(profileRef, () => { + setIsMenuVisible(false) + }) + + const renderCascadingMenu = () => { + if (isMenuVisible && hasCascadingMenu) { + return + } + return null + } + + return ( +
+
{setIsMenuVisible(!isMenuVisible)}} + > + {renderCascadingMenu()} + +
+
+ ) +} + +const CascadingMenu = () => { + return ( + + } text="Profile" /> + + ) +} + +export default SimpleThreeDotsMenu \ No newline at end of file diff --git a/src/components/threeDotsMenu/index.jsx b/src/components/threeDotsMenu/index.jsx index 1eb2e628..a5a7601c 100644 --- a/src/components/threeDotsMenu/index.jsx +++ b/src/components/threeDotsMenu/index.jsx @@ -9,10 +9,9 @@ import ProfileIcon from '../../assets/icons/profileIcon' import SquareBracketsIcon from '../../assets/icons/squareBracketsIcon' import Menu from '../menu' import MenuItem from '../menu/menuItem' -// import './style.css' import useAuth from '../../hooks/useAuth' -const ThreeDotsMenu = ({ id, hasCascadingMenu = true }) => { +const ThreeDotsMenu = ({ id, hasCascadingMenu = true, position }) => { const { useClickOutside } = useAuth() const [isMenuVisible, setIsMenuVisible] = useState(false) const profileRef = useRef(null) @@ -21,11 +20,9 @@ const ThreeDotsMenu = ({ id, hasCascadingMenu = true }) => { setIsMenuVisible(false) }) - // const cursor = hasCascadingMenu ? 'pointer' : 'default' - const renderCascadingMenu = () => { if (isMenuVisible && hasCascadingMenu) { - return + return } return null } @@ -33,19 +30,23 @@ const ThreeDotsMenu = ({ id, hasCascadingMenu = true }) => { return (
{setIsMenuVisible(!isMenuVisible)}} > {renderCascadingMenu()} - +
) } -const CascadingMenu = () => { +const CascadingMenu = ({ position }) => { return ( - + } text="Profile" /> } text="Add note" /> diff --git a/src/components/threeDotsMenu/style.css b/src/components/threeDotsMenu/style.css index e8f9afaf..e69de29b 100644 --- a/src/components/threeDotsMenu/style.css +++ b/src/components/threeDotsMenu/style.css @@ -1,8 +0,0 @@ -/* .profile-circle { - position: relative; - cursor: pointer; -} - -.profile-circle-menu { - margin-left: 65px; -} */ \ No newline at end of file diff --git a/src/pages/allSearchResults/index.jsx b/src/pages/allSearchResults/index.jsx index 885920da..6752b374 100644 --- a/src/pages/allSearchResults/index.jsx +++ b/src/pages/allSearchResults/index.jsx @@ -1,33 +1,33 @@ -import {useLocation, Link, NavLink } from 'react-router-dom'; +import { useLocation, Link, NavLink } from 'react-router-dom'; import { useState, useEffect, useRef } from 'react'; import useUser from '../../hooks/useUser'; -import EllipsisIcon from '../../assets/icons/ellipsisIcon' import ThreeDotsMenu from '../../components/threeDotsMenu'; +import SimpleThreeDotsMenu from '../../components/simpleThreeDotsMenu'; import SearchIcon from '../../assets/icons/searchIcon'; import ArrowLeftIcon from '../../assets/icons/arrowLeftIcon'; import ProfileCircle from '../../components/profileCircle'; import Header from '../../components/header'; import Navigation from '../../components/navigation'; import Card from '../../components/card'; -import ProfileIcon from '../../assets/icons/profileIcon' -import Menu from '../../components/menu' -import MenuItem from '../../components/menu/menuItem' +import ProfileIcon from '../../assets/icons/profileIcon'; +import Menu from '../../components/menu'; +import MenuItem from '../../components/menu/menuItem'; import { getUsers } from '../../service/apiClient'; import './style.css'; const AllSearchResults = () => { const location = useLocation(); - const menuRef = useRef(null) - const {currentUser} = useUser() + const menuRef = useRef(null); + const { currentUser } = useUser(); const { results: initialResults, searchVal: initialSearchVal } = location.state || { results: [], searchVal: '' }; const [searchVal, setSearchVal] = useState(initialSearchVal); const [results, setResults] = useState(initialResults); - const [selectedProfileId, setSelectedProfileId] = useState(null) - const [menuPosition, setMenuPosition] = useState({ top: 0, right: 0 }); - + const [selectedProfileId, setSelectedProfileId] = useState(null); + const [menuPosition, setMenuPosition] = useState({ top: 0, left: 0 }); + useEffect(() => { - getUsers() - window.scrollTo(0, 0); + getUsers(); + window.scrollTo(0, 0); }, []); const onChange = async (e) => { @@ -48,131 +48,122 @@ const AllSearchResults = () => { console.error('Error fetching users:', error); } }; - + const getInitials = (firstName, lastName) => { const firstInitial = firstName ? firstName.charAt(0).toUpperCase() : ''; const secondInitial = lastName ? lastName.charAt(0).toUpperCase() : ''; - return [firstInitial, secondInitial] + return [firstInitial, secondInitial]; }; const handleClickOutside = (event) => { if (menuRef.current && !menuRef.current.contains(event.target) && !event.target.closest('.link-to-profile')) { setSelectedProfileId(null); } -}; + }; useEffect(() => { - document.addEventListener('click', handleClickOutside) + document.addEventListener('click', handleClickOutside); return () => { - document.removeEventListener('click', handleClickOutside) - } - }, []) - - const onClickStudentMenu = (id, event) => { + document.removeEventListener('click', handleClickOutside); + }; + }, []); + + const onClickMenu = (id, event) => { event.stopPropagation(); const rect = event.currentTarget.getBoundingClientRect(); + const menuWidth = 308; setMenuPosition({ top: rect.top + window.scrollY, - right: window.innerWidth - rect.left, + left: rect.left - menuWidth, }); - setSelectedProfileId(prevId => prevId === id ? null : id); - console.log(id); + setSelectedProfileId(prevId => (prevId === id ? null : id)); }; - return ( - <> -
-
- -
-
-
-
- - - -

Search results

-
-
- -
- - -
-
-
-
- -
- - {results.length === 0 && ( -

No results found.

- )} - {results.length > 0 && ( -
    - {results.map((user) => ( -
  • - + const renderTeacherContent = (user) => ( + <> +
    + Add Note + Move to Cohort +
    +
    + onClickMenu(user.id, event)} id={user.id} hasCascadingMenu={true} position={menuPosition}/> +
    + + ); -
    - {`${user.firstName} ${user.lastName}`} -

    {`Software Developer, Cohort ${user.cohortId}`}

    -
    -
    -

    Profile

    -
    - - {currentUser.role === 'TEACHER' && ( - <> -
    - Add Note - Move to Cohort -
    -
    - -
    - - )} + const renderStudentContent = (user) => ( +
    + onClickMenu(user.id, event)} id={user.id}/> +
    + ); - {currentUser.role === 'STUDENT' && ( -
    onClickStudentMenu(user.id, event)}> - - {selectedProfileId === user.id && ( - - } text="Profile" /> - - )} -
    - )} -
  • - ))} -
- )} + return ( + <> +
+
+ +
+
+
+
+ + + +

Search results

+
+
+ +
+ + +
-
-
+
+ + {results.length === 0 && ( +

No results found.

+ )} + {results.length > 0 && ( +
    + {results.map((user) => ( +
  • + +
    + {`${user.firstName} ${user.lastName}`} +

    {`Software Developer, Cohort ${user.cohortId}`}

    +
    +
    +

    Profile

    +
    + {currentUser.role === 'TEACHER' + ? renderTeacherContent(user) + : renderStudentContent(user)} +
  • + ))} +
+ )} +
+
+
+
+
); }; diff --git a/src/pages/allSearchResults/style.css b/src/pages/allSearchResults/style.css index 3a39e34d..beb880a1 100644 --- a/src/pages/allSearchResults/style.css +++ b/src/pages/allSearchResults/style.css @@ -1,3 +1,5 @@ +/* Common items */ + .search-page-header { grid-column: 1 / -1; position: sticky; @@ -73,32 +75,7 @@ grid-template-rows: auto; } -.found-user-card-stdnt { - display: grid; - gap: 1rem; - grid-template-columns: auto 5fr 1fr 1fr; - align-items: center; - justify-items: stretch; -} - -.found-user-card-tchr { - display: grid; - grid-template-columns: auto 1fr 1fr 1fr 0.25fr; - align-items: center; -} - -.teacher-links { - display: grid; - gap: 1rem; - grid-template-columns: 1fr 1fr; - align-items: center; - justify-content: stretch; - cursor: pointer; -} - .found-user-details { - /* display: grid; - grid-template-rows: 1fr 1fr; */ color: #64648c; padding-left: 1rem; } @@ -119,15 +96,22 @@ padding-right: 7rem; } -.teacher-links span { - color: #64648c; - cursor: pointer; -} - a { text-decoration: none; } +.allSearchResults-profile-circle-menu { + position: absolute; + right: 100%; + top: 50%; + transform: translateY(-50%); + margin-right: 10px; + background-color: white; + border: 1px solid #ccc; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + z-index: 1000; +} + .asr-link-to-profile { position: relative; display: grid; @@ -141,18 +125,6 @@ a { cursor: pointer; } -.allSearchResults-profile-circle-menu { - position: absolute; - right: 100%; - top: 50%; - transform: translateY(-50%); - margin-right: 10px; - background-color: white; - border: 1px solid #ccc; - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); - z-index: 1000; -} - .search-three-dots-menu { position: relative; display: grid; @@ -164,4 +136,35 @@ a { height: 2.5rem; border-radius: 1.5rem; cursor: pointer; -} \ No newline at end of file +} + +/* Conditionally rendered items */ + +.found-user-card-stdnt { + display: grid; + gap: 1rem; + grid-template-columns: auto 5fr 1fr 1fr; + align-items: center; + justify-items: stretch; +} + +.found-user-card-tchr { + display: grid; + grid-template-columns: auto 1fr 1fr 1fr 0.25fr; + align-items: center; +} + +.teacher-links { + display: grid; + gap: 1rem; + grid-template-columns: 1fr 1fr; + align-items: center; + justify-content: stretch; + cursor: pointer; +} + + +.teacher-links span { + color: #64648c; + cursor: pointer; +} From 9fce6e843f39db6938d5db2cb304c7fa6aa97e6d Mon Sep 17 00:00:00 2001 From: Periklis Date: Mon, 29 Jul 2024 01:00:06 +0300 Subject: [PATCH 110/120] some cleanup -menu still not left --- src/pages/allSearchResults/index.jsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/pages/allSearchResults/index.jsx b/src/pages/allSearchResults/index.jsx index 6752b374..27915110 100644 --- a/src/pages/allSearchResults/index.jsx +++ b/src/pages/allSearchResults/index.jsx @@ -9,9 +9,6 @@ import ProfileCircle from '../../components/profileCircle'; import Header from '../../components/header'; import Navigation from '../../components/navigation'; import Card from '../../components/card'; -import ProfileIcon from '../../assets/icons/profileIcon'; -import Menu from '../../components/menu'; -import MenuItem from '../../components/menu/menuItem'; import { getUsers } from '../../service/apiClient'; import './style.css'; @@ -82,6 +79,7 @@ const AllSearchResults = () => { const renderTeacherContent = (user) => ( <>
+ {/* Profile */} Add Note Move to Cohort
@@ -92,9 +90,14 @@ const AllSearchResults = () => { ); const renderStudentContent = (user) => ( + <> +
+

Profile

+
onClickMenu(user.id, event)} id={user.id}/>
+ ); return ( @@ -149,9 +152,9 @@ const AllSearchResults = () => { {`${user.firstName} ${user.lastName}`}

{`Software Developer, Cohort ${user.cohortId}`}

-
+ {/*

Profile

-
+
*/} {currentUser.role === 'TEACHER' ? renderTeacherContent(user) : renderStudentContent(user)} From 0c8739aef80d1f4c92609f696440cafb5203d4f9 Mon Sep 17 00:00:00 2001 From: Periklis Date: Mon, 29 Jul 2024 01:02:50 +0300 Subject: [PATCH 111/120] little more cleanup --- src/pages/allSearchResults/index.jsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pages/allSearchResults/index.jsx b/src/pages/allSearchResults/index.jsx index 27915110..63c69f1a 100644 --- a/src/pages/allSearchResults/index.jsx +++ b/src/pages/allSearchResults/index.jsx @@ -152,9 +152,6 @@ const AllSearchResults = () => { {`${user.firstName} ${user.lastName}`}

{`Software Developer, Cohort ${user.cohortId}`}

- {/*
-

Profile

-
*/} {currentUser.role === 'TEACHER' ? renderTeacherContent(user) : renderStudentContent(user)} From d3f71d9875d3081bc49ee659e0722b47122431d2 Mon Sep 17 00:00:00 2001 From: Periklis Date: Mon, 29 Jul 2024 13:39:50 +0300 Subject: [PATCH 112/120] corrected teachers view user card styling --- src/pages/allSearchResults/index.jsx | 2 +- src/pages/allSearchResults/style.css | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/allSearchResults/index.jsx b/src/pages/allSearchResults/index.jsx index 63c69f1a..a898e47a 100644 --- a/src/pages/allSearchResults/index.jsx +++ b/src/pages/allSearchResults/index.jsx @@ -79,7 +79,7 @@ const AllSearchResults = () => { const renderTeacherContent = (user) => ( <>
- {/* Profile */} + Profile Add Note Move to Cohort
diff --git a/src/pages/allSearchResults/style.css b/src/pages/allSearchResults/style.css index beb880a1..0d13baa8 100644 --- a/src/pages/allSearchResults/style.css +++ b/src/pages/allSearchResults/style.css @@ -150,16 +150,16 @@ a { .found-user-card-tchr { display: grid; - grid-template-columns: auto 1fr 1fr 1fr 0.25fr; + grid-template-columns: auto 1fr 1fr 0.25fr; align-items: center; } .teacher-links { display: grid; gap: 1rem; - grid-template-columns: 1fr 1fr; + grid-template-columns: 1fr 1fr 1fr; align-items: center; - justify-content: stretch; + justify-content: right; cursor: pointer; } From f0312f91dbf5cc62d9ad3be90e05846cf68c3bcc Mon Sep 17 00:00:00 2001 From: Will Baxter Date: Mon, 29 Jul 2024 11:45:56 +0100 Subject: [PATCH 113/120] Refactor --- src/context/currentUser.jsx | 4 ++-- src/pages/profile/index.jsx | 44 +++++++++++++++++++++++++++++++++++ src/pages/profile/profile.css | 2 +- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/context/currentUser.jsx b/src/context/currentUser.jsx index 7737d33a..03662a11 100644 --- a/src/context/currentUser.jsx +++ b/src/context/currentUser.jsx @@ -11,7 +11,7 @@ export const CurrentUserProvider = ({ children }) => { useEffect(() => { if (user) { - setCurrentUser({ ...user.user }); + setCurrentUser({ ...user }); return; } @@ -21,7 +21,7 @@ export const CurrentUserProvider = ({ children }) => { const userDetails = await getUser(userId); if (userDetails.status === "success") { - setCurrentUser({ ...userDetails.data.user.user }); + setCurrentUser({ ...userDetails.data.user }); return; } } diff --git a/src/pages/profile/index.jsx b/src/pages/profile/index.jsx index be49af70..9e4fdefc 100644 --- a/src/pages/profile/index.jsx +++ b/src/pages/profile/index.jsx @@ -2,9 +2,39 @@ import Card from "../../components/card"; import UserDetails from "../../components/UserDetails"; import UserProfileIcon from "../../components/UserProfileIcon"; import TextInput from "../../components/form/textInput"; +import Form from "../../components/form"; +import Button from "../../components/button"; import "./profile.css"; +import useUser from "../../hooks/useUser"; +import { useState, useEffect } from "react"; const Profile = () => { + const [formData, setFormData] = useState({ + firstName: "", + lastName: "", + username: "", + githubUsername: "", + role: "", + cohortId: "", + email: "", + bio: "", + }); + + const { currentUser } = useUser(); + // let inputsToMap = Object.keys(formData); + + useEffect(() => { + setFormData(currentUser) + }, []) + + + + console.log(inputsToMap); + + const handleSubmit = (e) => {}; + + const handleChange = (e) => {}; + return (

Profile

@@ -13,6 +43,20 @@ const Profile = () => {
+
+ {inputsToMap.map((input, index) => { + return ( + + ); + })} +
- onClickMenu(user.id, event)} id={user.id} hasCascadingMenu={true} position={menuPosition}/> + onClickMenu(user.id)} id={user.id} hasCascadingMenu={true} />
); @@ -94,8 +86,8 @@ const AllSearchResults = () => {

Profile

-
- onClickMenu(user.id, event)} id={user.id}/> +
+ onClickMenu(user.id)} id={user.id} hasCascadingMenu={true}/>
); diff --git a/src/pages/allSearchResults/style.css b/src/pages/allSearchResults/style.css index 0d13baa8..31470fbc 100644 --- a/src/pages/allSearchResults/style.css +++ b/src/pages/allSearchResults/style.css @@ -138,8 +138,213 @@ a { cursor: pointer; } +.search-three-dots-menu ul { + position: absolute; + transform: translate(-135%, +0%); + z-index: 10; +} +.search-three-dots-menu { + position: relative; + display: grid; + align-items: center; + justify-content: center; + justify-self: end; + background-color: #f0f5fa; + width: 2.5rem; + height: 2.5rem; + border-radius: 1.5rem; + cursor: pointer; +} + +.search-three-dots-menu ul { + position: absolute; + transform: translate(-135%, +0%); + z-index: 10; +} +.search-three-dots-menu { + position: relative; + display: grid; + align-items: center; + justify-content: center; + justify-self: end; + background-color: #f0f5fa; + width: 2.5rem; + height: 2.5rem; + border-radius: 1.5rem; + cursor: pointer; +} + +.search-three-dots-menu ul { + position: absolute; + transform: translate(-135%, +0%); + z-index: 10; +} +.search-three-dots-menu { + position: relative; + display: grid; + align-items: center; + justify-content: center; + justify-self: end; + background-color: #f0f5fa; + width: 2.5rem; + height: 2.5rem; + border-radius: 1.5rem; + cursor: pointer; +} + +.search-three-dots-menu ul { + position: absolute; + transform: translate(-135%, +0%); + z-index: 10; +} +.search-three-dots-menu { + position: relative; + display: grid; + align-items: center; + justify-content: center; + justify-self: end; + background-color: #f0f5fa; + width: 2.5rem; + height: 2.5rem; + border-radius: 1.5rem; + cursor: pointer; +} + +.search-three-dots-menu ul { + position: absolute; + transform: translate(-135%, +0%); + z-index: 10; +} +.search-three-dots-menu { + position: relative; + display: grid; + align-items: center; + justify-content: center; + justify-self: end; + background-color: #f0f5fa; + width: 2.5rem; + height: 2.5rem; + border-radius: 1.5rem; + cursor: pointer; +} + +.search-three-dots-menu ul { + position: absolute; + transform: translate(-135%, +0%); + z-index: 10; +} +.search-three-dots-menu { + position: relative; + display: grid; + align-items: center; + justify-content: center; + justify-self: end; + background-color: #f0f5fa; + width: 2.5rem; + height: 2.5rem; + border-radius: 1.5rem; + cursor: pointer; +} + +.search-three-dots-menu ul { + position: absolute; + transform: translate(-135%, +0%); + z-index: 10; +} +.search-three-dots-menu { + position: relative; + display: grid; + align-items: center; + justify-content: center; + justify-self: end; + background-color: #f0f5fa; + width: 2.5rem; + height: 2.5rem; + border-radius: 1.5rem; + cursor: pointer; +} + +.search-three-dots-menu ul { + position: absolute; + transform: translate(-135%, +0%); + z-index: 10; +} +.search-three-dots-menu { + position: relative; + display: grid; + align-items: center; + justify-content: center; + justify-self: end; + background-color: #f0f5fa; + width: 2.5rem; + height: 2.5rem; + border-radius: 1.5rem; + cursor: pointer; +} + +.search-three-dots-menu ul { + position: absolute; + transform: translate(-135%, +0%); + z-index: 10; +} +.search-three-dots-menu { + position: relative; + display: grid; + align-items: center; + justify-content: center; + justify-self: end; + background-color: #f0f5fa; + width: 2.5rem; + height: 2.5rem; + border-radius: 1.5rem; + cursor: pointer; +} + +.search-three-dots-menu ul { + position: absolute; + transform: translate(-135%, +0%); + z-index: 10; +} +.search-three-dots-menu { + position: relative; + display: grid; + align-items: center; + justify-content: center; + justify-self: end; + background-color: #f0f5fa; + width: 2.5rem; + height: 2.5rem; + border-radius: 1.5rem; + cursor: pointer; +} + +.search-three-dots-menu ul { + position: absolute; + transform: translate(-135%, +0%); + z-index: 10; +} + /* Conditionally rendered items */ +.search-three-dots-menu { + position: relative; + display: grid; + align-items: center; + justify-content: center; + justify-self: end; + background-color: #f0f5fa; + width: 2.5rem; + height: 2.5rem; + border-radius: 1.5rem; + cursor: pointer; +} + +.search-three-dots-menu ul { + position: absolute; + transform: translate(-135%, +0%); + z-index: 10; +} + .found-user-card-stdnt { display: grid; gap: 1rem; From bb187cb73ab8cfa8bfdd16c1d06c016fb37f590f Mon Sep 17 00:00:00 2001 From: Will Baxter Date: Mon, 29 Jul 2024 14:01:20 +0100 Subject: [PATCH 115/120] Input fields populating --- src/pages/profile/index.jsx | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/pages/profile/index.jsx b/src/pages/profile/index.jsx index 9e4fdefc..730bcae2 100644 --- a/src/pages/profile/index.jsx +++ b/src/pages/profile/index.jsx @@ -21,15 +21,12 @@ const Profile = () => { }); const { currentUser } = useUser(); - // let inputsToMap = Object.keys(formData); useEffect(() => { - setFormData(currentUser) - }, []) - - + setFormData(currentUser); + }, [currentUser]); + - console.log(inputsToMap); const handleSubmit = (e) => {}; @@ -44,17 +41,19 @@ const Profile = () => {
- {inputsToMap.map((input, index) => { - return ( - - ); - })} + {formData && + Object.keys(formData).map((input, index) => { + return ( + + ); + })}
-
+ {formData && Object.keys(formData).map((input, index) => { + if (input === "id") { + return; + } return ( ); })} -
- + {message &&

{message}

} diff --git a/src/components/navigation/index.jsx b/src/components/navigation/index.jsx index 4a932f7e..30cc24e4 100644 --- a/src/components/navigation/index.jsx +++ b/src/components/navigation/index.jsx @@ -1,26 +1,69 @@ -import { NavLink } from "react-router-dom" -import CohortIcon from "../../assets/icons/cohortIcon" -import HomeIcon from "../../assets/icons/homeIcon" -import ProfileIcon from "../../assets/icons/profileIcon" -import useAuth from "../../hooks/useAuth" +import { NavLink, useLocation } from 'react-router-dom' +import CohortIcon from '../../assets/icons/cohortIcon' +import HomeIcon from '../../assets/icons/homeIcon' +import ProfileIcon from '../../assets/icons/profileIcon' +import useAuth from '../../hooks/useAuth' import './style.css' -const Navigation = () => { - const { token } = useAuth(); +const Navigation = ({ disabledNav = false }) => { + const { token } = useAuth() + const location = useLocation() - if (!token) { - return null; - } + if (!token) { + return null + } + + if (disabledNav) { + return ( + + ) + } return ( ) } -export default Navigation; +export default Navigation diff --git a/src/context/auth.jsx b/src/context/auth.jsx index 91fc5668..0e766042 100644 --- a/src/context/auth.jsx +++ b/src/context/auth.jsx @@ -1,4 +1,4 @@ -import { createContext, useEffect, useRef, useState } from "react"; +import { createContext, useEffect, useState } from "react"; import { useNavigate, useLocation, Navigate } from "react-router-dom"; import jwt_decode from "jwt-decode"; import useAuth from "../hooks/useAuth"; @@ -6,6 +6,7 @@ import { createProfile, login, register } from "../service/apiClient"; import Navigation from "../components/navigation"; import Header from "../components/header"; import Modal from "../components/modal"; +import ERR from "../service/errors.js"; const AuthContext = createContext(); @@ -13,37 +14,45 @@ const AuthProvider = ({ children }) => { const navigate = useNavigate(); const location = useLocation(); const [token, setToken] = useState(null); - const [error, setError] = useState(""); + const [error, setError] = useState(null); const [user, setUser] = useState(null); const useClickOutside = (ref, onClickOutside) => { useEffect(() => { - const handleClickOutside = (e) => { if (ref.current && !ref.current.contains(e.target)) { - onClickOutside() + onClickOutside(); } - } + }; - document.addEventListener("mousedown", handleClickOutside) + document.addEventListener("mousedown", handleClickOutside); return () => { - document.removeEventListener("mousedown", handleClickOutside) - } - }, [ref, onClickOutside]) - } + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [ref, onClickOutside]); + }; useEffect(() => { const storedToken = localStorage.getItem("token"); if (storedToken) { setToken(storedToken); - navigate(location?.pathname || "/"); + navigate(location?.pathname || "/"); } }, [location?.pathname]); const handleLogin = async (email, password) => { try { + if (!email || !password) { + throw new Error(ERR.ENTER_EMAIL_PASSWORD); + } + if (!validateEmail(email)) { + throw new Error(ERR.EMAIL_ERROR_MESSAGE); + } + if (!validatePassword(password)) { + throw new Error(ERR.PASSWORD_REQUIRMENTS); + } const res = await login(email, password); if (res.data.error) { @@ -55,37 +64,47 @@ const AuthProvider = ({ children }) => { localStorage.setItem("token", res.data.token); setToken(res.data.token); navigate(location.state?.from?.pathname || "/"); - setError(""); + setError(null); return; } - setUser({...res.data.user}) - setError("Login failed"); + setUser({ ...res.data.user }); + setError(ERR.LOGIN_FAILED); navigate("/login"); } catch (error) { - setError("An error occurred during login"); + setError(error.message); } }; const handleLogout = () => { localStorage.removeItem("token"); setToken(null); - setUser(null) - setError(""); + setUser(null); + setError(null); }; const handleRegister = async (email, password) => { try { + if (!email || !password) { + throw new Error(ERR.ENTER_EMAIL_PASSWORD); + } + if (!validateEmail(email)) { + throw new Error(ERR.EMAIL_ERROR_MESSAGE); + } + if (!validatePassword(password)) { + throw new Error(ERR.PASSWORD_REQUIRMENTS); + } const res = await register(email, password); if (res.data.error) { setError(res.data.error); return; } localStorage.setItem("token", res.data.token); + setUser({ ...res.data.user }); setToken(res.data.token); navigate("/verification"); - setError(""); + setError(ERR.REGISTRATION_FAILED); } catch (error) { - setError("An error occurred during registration"); + setError(error.message); } }; @@ -114,9 +133,9 @@ const AuthProvider = ({ children }) => { navigate("/"); return; } - setError("Failed to create profile"); + setError(ERR.PROFILE_CREATION_FAILED); } catch (error) { - setError("An error occurred during profile creation"); + setError(ERR.PROFILE_CREATION_FAILED); } }; @@ -129,13 +148,13 @@ const AuthProvider = ({ children }) => { onCreateProfile: handleCreateProfile, error, setError, - useClickOutside + useClickOutside, }; return {children}; }; -const ProtectedRoute = ({ children }) => { +const ProtectedRoute = ({ children, disabledNav = false }) => { const { token } = useAuth(); const location = useLocation(); @@ -143,6 +162,17 @@ const ProtectedRoute = ({ children }) => { return ; } + if (disabledNav) { + return ( +
+
+ + + {children} +
+ ); + } + return (
@@ -153,4 +183,18 @@ const ProtectedRoute = ({ children }) => { ); }; +function validateEmail(email) { + const emailPattern = /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/; + + return !email || !emailPattern.test(email) ? false : true; +} + +function validatePassword(password) { + const minLength = 8; + const hasUppercase = /[A-Z]/.test(password); + const hasNumber = /\d/.test(password); + const hasSpecialCharacter = /[!@#$%^&*(),.?":{}|<>]/.test(password); + return password.length >= minLength && hasUppercase && hasNumber && hasSpecialCharacter +} + export { AuthContext, AuthProvider, ProtectedRoute }; diff --git a/src/context/currentUser.jsx b/src/context/currentUser.jsx index 03662a11..9e3628c0 100644 --- a/src/context/currentUser.jsx +++ b/src/context/currentUser.jsx @@ -1,38 +1,39 @@ -import { createContext, useEffect, useState } from "react"; -import useAuth from "../hooks/useAuth"; -import jwt_decode from "jwt-decode"; -import { getUser } from "../service/apiClient"; +import { createContext, useEffect, useState } from 'react' +import useAuth from '../hooks/useAuth' +import jwt_decode from 'jwt-decode' +import { getUser } from '../service/apiClient' +import { useLocation, useNavigate, redirect } from 'react-router-dom' export const CurrentUserContext = createContext(); export const CurrentUserProvider = ({ children }) => { - const { token, user } = useAuth(); - const [currentUser, setCurrentUser] = useState(null); + const { token, user } = useAuth() + const [currentUser, setCurrentUser] = useState(null) - useEffect(() => { - if (user) { - setCurrentUser({ ...user }); - return; - } + useEffect(() => { + if (user) { + setCurrentUser({ ...user }) + return + } - async function getUserFromToken() { - if (token) { - const { userId } = jwt_decode(token); - const userDetails = await getUser(userId); + async function getUserFromToken() { + if (token) { + const { userId } = jwt_decode(token) + const userDetails = await getUser(userId) - if (userDetails.status === "success") { - setCurrentUser({ ...userDetails.data.user }); - return; + if (userDetails.status === 'success') { + setCurrentUser({ ...userDetails.data.user }) + return + } + } + setCurrentUser(null) + return + } + if (!currentUser) { + getUserFromToken() } - } - setCurrentUser(null); - return; - } - if (!currentUser) { - getUserFromToken(); - } - return; - }, [token, user]); + return + }, [token, user]) return ( { + const location = useLocation(); + const { results: initialResults, searchVal: initialSearchVal } = location.state || { results: [], searchVal: '' }; + const [searchVal, setSearchVal] = useState(initialSearchVal); + const [results, setResults] = useState(initialResults); + + useEffect(() => { + getUsers() + window.scrollTo(0, 0); + }, []); + + const onChange = async (e) => { + const searchValue = e.target.value; + setSearchVal(searchValue); + + try { + const allUsers = await getUsers(); + const filteredResults = allUsers.filter((user) => { + if (user.firstName && user.lastName) { + const fullName = `${user.firstName} ${user.lastName}`.toLowerCase(); + return fullName.includes(searchValue.toLowerCase()); + } + return false; + }); + setResults(filteredResults); + } catch (error) { + console.error('Error fetching users:', error); + } + }; + + const onClickStudent = (id) => { + setSelectedProfileId(id); + setIsStudentModalVisible(true); + }; + + const getInitials = (firstName, lastName) => { + const firstInitial = firstName ? firstName.charAt(0).toUpperCase() : ''; + const secondInitial = lastName ? lastName.charAt(0).toUpperCase() : ''; + return [firstInitial, secondInitial] + }; + + return ( + <> +
+
+ +
+
+
+
+ + + +

Search results

+
+
+ +
+ + +
+
+
+
+ +
+ + {results.length === 0 && ( +

No results found.

+ )} + {results.length > 0 && ( +
    + {results.map((user) => ( +
  • + + +
    + {`${user.firstName} ${user.lastName}`} +

    Software Developer

    +
    + +
    +

    Profile

    +
    + +
    onClickStudent(cohort.id)}> + +
    +
  • + ))} +
+ )} +
+
+
+
+
+ + ); +}; + +export default AllSearchResults; diff --git a/src/pages/allSearchResults/style.css b/src/pages/allSearchResults/style.css new file mode 100644 index 00000000..e246b748 --- /dev/null +++ b/src/pages/allSearchResults/style.css @@ -0,0 +1,72 @@ +.search-results-container { + display: inline; + margin-left: 6rem; + width: 75%; +} + +.title { + display: grid; + grid-template-columns: 50px 1fr; + justify-items: start; + align-items: center; + margin-bottom: 2rem; +} + +.title h2{ + padding-left: 0.7rem; +} + +.search-bar-container { + margin-bottom: 2rem; + margin-left: 3rem; +} + +.search-page-search-bar { + display: grid; + height: 4rem; + grid-template-columns: max-content 1fr; + gap: 0.5rem; + align-items: center; + background-color: #e6ebf5; + padding: 0.5rem 1rem 0.5rem 1rem; + border-radius: 0.5rem; +} + +.search-page-input{ + font-size: large; + background-color: #e6ebf5; +} + +.search-results { + margin-left: 3rem; +} + +.search-results-list { + display: grid; + gap: 2rem; + grid-template-rows: auto; +} + + +.found-user-card { + display: grid; + gap: 1rem; + grid-template-columns: 1fr 5fr 1fr 1fr; + align-items: center; + justify-content: center; +} + +.found-user-details p { + color: #64648c +} + +.link-to-profile { + display: grid; + align-items: center; + justify-content: center; + background-color: #f0f5fa; + width: 2.5rem; + height: 2.5rem; + border-radius: 1.5rem; + cursor: pointer; +} \ No newline at end of file diff --git a/src/pages/dashboard/index.jsx b/src/pages/dashboard/index.jsx index 38367c88..f0fdbf9f 100644 --- a/src/pages/dashboard/index.jsx +++ b/src/pages/dashboard/index.jsx @@ -1,4 +1,5 @@ import { useEffect, useState, useRef } from 'react' +import { useNavigate } from 'react-router-dom'; import SearchIcon from '../../assets/icons/searchIcon' import Button from '../../components/button' import Card from '../../components/card' @@ -21,6 +22,7 @@ const Dashboard = () => { const [isStudentModalVisible, setIsStudentModalVisible] = useState(false) const [selectedProfileId, setSelectedProfileId] = useState(null) const menuRef = useRef(null) + const navigate = useNavigate() useEffect(() => { getUsers().then(setCohorts) @@ -34,6 +36,8 @@ const Dashboard = () => { setSearchVal(e.target.value) } + // console.log(cohorts); + const result = cohorts.filter((cohort) => { if (cohort.firstName && cohort.lastName) { const fullName = `${cohort.firstName || ''} ${ @@ -73,6 +77,8 @@ const Dashboard = () => { openModal() } + const allSearchResults = () => navigate('/search-results', { state: { results: result, searchVal } }) + return ( <>
@@ -152,7 +158,10 @@ const Dashboard = () => { ))} - {result.length >= 10 && } + {result.length >= 10 && + } )} diff --git a/src/pages/dashboard/style.css b/src/pages/dashboard/style.css index 297e3bda..915129b5 100644 --- a/src/pages/dashboard/style.css +++ b/src/pages/dashboard/style.css @@ -99,4 +99,4 @@ aside { .card.create-post, .card.dashboard-card-post { margin-bottom: 1.563rem; -} +} \ No newline at end of file diff --git a/src/service/apiClient.js b/src/service/apiClient.js index 35cf89d5..eb8dfbad 100644 --- a/src/service/apiClient.js +++ b/src/service/apiClient.js @@ -46,6 +46,10 @@ const getPosts = async () => { return res.data.posts } +async function createPost(content) { + return await post('posts', { content }) +} + async function post(endpoint, data, auth = true) { return await request('POST', endpoint, data, auth) } @@ -79,4 +83,4 @@ async function request(method, endpoint, data, auth = true) { return response.json() } -export { login, getUsers, getPosts, register, createProfile, getUser } +export { login, getUsers, getPosts, register, createProfile, getUser, createPost } diff --git a/src/service/errors.js b/src/service/errors.js new file mode 100644 index 00000000..0d48d704 --- /dev/null +++ b/src/service/errors.js @@ -0,0 +1,7 @@ +export default { + LOGIN_FAILED: "An error occurred during login", + REGISTRATION_FAILED: "An error occurred during registration", + PASSWORD_REQUIRMENTS: "Password must be at least 8 characters long and include at least one uppercase letter, one number, and one special character.", + EMAIL_ERROR_MESSAGE: "Please enter a valid email", + ENTER_EMAIL_PASSWORD: "Please enter your email and password", +}; From 4e257b2f5e091054925b23158c870ea09fd4e20e Mon Sep 17 00:00:00 2001 From: Will Baxter Date: Tue, 30 Jul 2024 10:40:10 +0100 Subject: [PATCH 119/120] Swapped switch statement for labelMap object --- src/pages/profile/index.jsx | 40 ++++++++++--------------------------- 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/src/pages/profile/index.jsx b/src/pages/profile/index.jsx index 35ceb4fa..1ba42095 100644 --- a/src/pages/profile/index.jsx +++ b/src/pages/profile/index.jsx @@ -29,35 +29,15 @@ const Profile = () => { const handleChange = (e) => {}; - const labelMaker = (string) => { - let result; - switch (string) { - case "cohortId": - result = "Cohort ID"; - break; - case "role": - result = "Role"; - break; - case "email": - result = "Email"; - break; - case "firstName": - result = "First Name"; - break; - case "lastName": - result = "Last Name"; - break; - case "bio": - result = "Bio"; - break; - case "githubUsername": - result = "GitHub Username"; - break; - default: - result = "Something went wrong"; - } - return result; - }; + const labelMap = { + cohortId: 'Cohort ID', + role: 'Role', + email: 'Email', + firstName: 'First Name', + lastName: 'Last Name', + bio: 'Bio', + githubUsername: 'GitHub Username', + } return (
@@ -79,7 +59,7 @@ const Profile = () => { className="profile-input" key={index} name={input} - label={labelMaker(input)} + label={labelMap[input]} value={formData[input]} /> ); From f8b8b041d2e17a12a0e730de11b7acc6cce1b5be Mon Sep 17 00:00:00 2001 From: homonoviscoding Date: Tue, 30 Jul 2024 10:55:24 +0100 Subject: [PATCH 120/120] added dotenv file --- .env.example | 2 +- package-lock.json | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.env.example b/.env.example index e67d4e53..415efed5 100644 --- a/.env.example +++ b/.env.example @@ -1 +1 @@ -REACT_APP_API_URL="http://localhost:4000" \ No newline at end of file +VITE_API_URL="http://localhost:4000" \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 715e236f..c00f681d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3956,12 +3956,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -5277,9 +5277,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1"