From e4124dc3c16718b2369f867b9ad8631ef94956b2 Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Thu, 13 Feb 2025 15:45:36 +0100 Subject: [PATCH 01/24] ILEX-83 make changes to login page: connect to backend api --- src/components/Auth/Login.jsx | 258 ++++++++++++++--------- src/components/Auth/UI/Formfield.jsx | 66 +++--- src/components/Auth/UI/PasswordField.jsx | 108 +++++----- src/theme/index.jsx | 35 ++- src/theme/variables.js | 1 + 5 files changed, 285 insertions(+), 183 deletions(-) diff --git a/src/components/Auth/Login.jsx b/src/components/Auth/Login.jsx index a6b3ea54..c7b60964 100644 --- a/src/components/Auth/Login.jsx +++ b/src/components/Auth/Login.jsx @@ -1,4 +1,5 @@ -import * as React from "react"; +import React, { useState } from "react"; +import { useNavigate } from "react-router-dom"; import { Box, Button, @@ -14,108 +15,165 @@ import { Link } from "react-router-dom"; import { CheckedIcon, OrcidIcon, UncheckedIcon } from "../../Icons"; import FormField from "./UI/Formfield"; import PasswordField from "./UI/PasswordField"; +import { CLIENT_ID, REDIRECT_URI, ORCID_LINK } from "../../model/frontend/auth"; +import * as mockApi from "../../api/endpoints/swaggerMockMissingEndpoints"; +import * as yup from 'yup'; + +const useMockApi = () => mockApi; + +const schema = yup.object().shape({ + email: yup.string().email().required(), + password: yup.string().required().min(6), +}); const Login = () => { - const label = { inputProps: { "aria-label": "Checkbox demo" } }; + const [formData, setFormData] = useState({ + email: '', + password: '' + }); + const [errors, setErrors] = useState({}); + const label = { inputProps: { "aria-label": "Checkbox demo" } }; + const { login } = useMockApi(); + const navigate = useNavigate(); + + const handleSubmit = async (e) => { + e.preventDefault(); + try { + await schema.validate(formData, { abortEarly: false }) + setErrors({}) + + login(formData).then(response => { + if(response.status === 200){ + localStorage.setItem("token", response.token) + navigate("/") + } + }) + } catch (err) { + if (err) { + const newErrors = {} + err.inner.forEach((error) => { + if (error.path) { + newErrors[error.path] = error.message + } + }) + setErrors(newErrors) + } + } + } + + const handleChange = (e) => { + const { name, value } = e.target + setFormData((prev) => ({ ...prev, [name]: value })) + } - return ( - <> - - { + const authUrl = `${ORCID_LINK}/oauth/authorize?client_id=${CLIENT_ID}&response_type=code&scope=/authenticate&redirect_uri=${REDIRECT_URI}`; + window.location.href = authUrl; + }; + + return ( + + + + + Return to page + + Log in to your account + Welcome! Please enter your details. +
+ + + + + + + } + checkedIcon={} + {...label} + defaultChecked + color="primary" + /> + } + label="Remember for 30 days" + /> + + - - - Return to page - - Log in to your account - - Welcome! Please enter your details. - - - - - - - - - } - checkedIcon={} - {...label} - defaultChecked - color="primary" - /> - } - label="Remember for 30 days" - /> - - - Forgot password - - - - - - - - - - - or - - - - - - - - - - - - Don’t have an account? Register - - - - -
+ Forgot password + +
+ + + + + + + + + or + + + + + + + + + + + + Don't have an account? Register + + - - ); + +
+
+ ); }; -export default Login; +export default Login; \ No newline at end of file diff --git a/src/components/Auth/UI/Formfield.jsx b/src/components/Auth/UI/Formfield.jsx index f387535b..b1509104 100644 --- a/src/components/Auth/UI/Formfield.jsx +++ b/src/components/Auth/UI/Formfield.jsx @@ -1,39 +1,57 @@ import * as React from "react"; import PropTypes from "prop-types"; import { - Grid, - FormControl, - Box, - FormHelperText, - OutlinedInput, + Grid, + FormControl, + Box, + FormHelperText, + OutlinedInput, + InputAdornment, + Typography, } from "@mui/material"; +import ErrorOutlineOutlinedIcon from '@mui/icons-material/ErrorOutlineOutlined'; -const FormField = ({ xs = 12, label, helperText, placeholder }) => { - return ( - - - - - {helperText} - - - - - ); +const FormField = ({ xs = 12, label, helperText, placeholder, value, name, onChange, errorMessage }) => { + return ( + + + + + {helperText} + + + + + ) + } + /> + {errorMessage && {`${errorMessage.charAt(0).toUpperCase() + errorMessage.slice(1)}`}} + + + ); }; // Add prop types for validation FormField.propTypes = { - xs: PropTypes.number, - label: PropTypes.string.isRequired, - helperText: PropTypes.string, - placeholder: PropTypes.string, + xs: PropTypes.number, + label: PropTypes.string.isRequired, + helperText: PropTypes.string, + placeholder: PropTypes.string, }; // Add default values for optional props FormField.defaultProps = { - helperText: "", - placeholder: "", + helperText: "", + placeholder: "", }; -export default FormField; +export default FormField; \ No newline at end of file diff --git a/src/components/Auth/UI/PasswordField.jsx b/src/components/Auth/UI/PasswordField.jsx index cfe932c8..d45dbd7d 100644 --- a/src/components/Auth/UI/PasswordField.jsx +++ b/src/components/Auth/UI/PasswordField.jsx @@ -1,68 +1,80 @@ import React, { useState } from "react"; import PropTypes from "prop-types"; import { - FormControl, - Box, - FormHelperText, - OutlinedInput, - InputAdornment, - IconButton, - Grid, - Typography, + FormControl, + Box, + FormHelperText, + OutlinedInput, + InputAdornment, + IconButton, + Grid, + Typography } from "@mui/material"; import VisibilityOutlinedIcon from "@mui/icons-material/VisibilityOutlined"; import VisibilityOffOutlinedIcon from "@mui/icons-material/VisibilityOffOutlined"; +import ErrorOutlineOutlinedIcon from '@mui/icons-material/ErrorOutlineOutlined'; -const PasswordField = ({ xs = 12, label, placeholder, helperText }) => { - const [showPassword, setShowPassword] = useState(false); - const handleClickShowPassword = () => setShowPassword((show) => !show); +const PasswordField = ({ xs = 12, label, placeholder, helperText, value, name, onChange, errorMessage }) => { + const [showPassword, setShowPassword] = useState(false); - const handleMouseDownPassword = (event) => event.preventDefault(); + const handleClickShowPassword = () => setShowPassword((show) => !show); - return ( - - - - - {helperText} - - - - {showPassword ? ( - - ) : ( - - )} - - - } - /> - - - ); + const handleMouseDownPassword = (event) => event.preventDefault(); + + return ( + + + + + {helperText} + + + + {errorMessage ? : <> + {showPassword ? ( + + ) : ( + + )} + + } + + + } + /> + {errorMessage && {`${errorMessage.charAt(0).toUpperCase() + errorMessage.slice(1)}`}} + + + ); }; // Prop Types for validation PasswordField.propTypes = { - xs: PropTypes.number, - label: PropTypes.string.isRequired, - placeholder: PropTypes.string, - helperText: PropTypes.string, + xs: PropTypes.number, + label: PropTypes.string.isRequired, + placeholder: PropTypes.string, + helperText: PropTypes.string, }; // Default Props PasswordField.defaultProps = { - placeholder: "", - helperText: "", + placeholder: "", + helperText: "", }; -export default PasswordField; +export default PasswordField; \ No newline at end of file diff --git a/src/theme/index.jsx b/src/theme/index.jsx index fb0e0478..b8dabbe7 100644 --- a/src/theme/index.jsx +++ b/src/theme/index.jsx @@ -30,9 +30,12 @@ const { brand50, error50, error200, + error300, + error500, error700, gray400, brand200, + errorInputBoxShadow } = vars; const theme = createTheme({ @@ -194,21 +197,31 @@ const theme = createTheme({ MuiOutlinedInput: { styleOverrides: { root: { - "&:hover": { - "& .MuiOutlinedInput-notchedOutline": { - borderColor: gray200, - }, + "& .MuiOutlinedInput-notchedOutline": { + borderColor: gray200, + }, + "&:hover .MuiOutlinedInput-notchedOutline": { + borderColor: gray200, + }, + "&.Mui-focused .MuiOutlinedInput-notchedOutline": { + borderColor: gray200, + borderWidth: "0.0625rem", }, - "&.Mui-focused": { + "&.Mui-error": { "& .MuiOutlinedInput-notchedOutline": { - borderColor: gray200, - borderWidth: "0.0625rem", + borderColor: error300, + }, + "&.Mui-focused .MuiOutlinedInput-notchedOutline": { + boxShadow: errorInputBoxShadow, }, + "& .MuiInputAdornment-root": { + color: error500 + }, + "& .MuiIconButton-root": { + color: error500 + } }, - }, - notchedOutline: { - borderColor: gray200, - }, + } }, }, diff --git a/src/theme/variables.js b/src/theme/variables.js index e37873c3..648a1927 100644 --- a/src/theme/variables.js +++ b/src/theme/variables.js @@ -73,4 +73,5 @@ export const vars = { success900: '#074D31', success950: '#053321', paperShadow: '0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03)', + errorInputBoxShadow: '0px 1px 2px 0px rgba(16, 24, 40, 0.05), 0px 0px 0px 4px rgba(240, 68, 56, 0.24)' } \ No newline at end of file From 3355e33573c0273ccb71e1cab1e792206bfc5552 Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Mon, 17 Feb 2025 14:10:06 +0100 Subject: [PATCH 02/24] ILEX-83 make changes after merge --- src/components/Auth/Login.jsx | 245 ++++++++++------------- src/components/Auth/UI/Formfield.jsx | 25 +-- src/components/Auth/UI/PasswordField.jsx | 82 ++++---- 3 files changed, 160 insertions(+), 192 deletions(-) diff --git a/src/components/Auth/Login.jsx b/src/components/Auth/Login.jsx index 6a6d1dfc..56c166c1 100644 --- a/src/components/Auth/Login.jsx +++ b/src/components/Auth/Login.jsx @@ -15,12 +15,9 @@ import { Link } from "react-router-dom"; import { CheckedIcon, UncheckedIcon, OrcidIcon } from "../../Icons"; import FormField from "./UI/Formfield"; import PasswordField from "./UI/PasswordField"; -import { CLIENT_ID, REDIRECT_URI, ORCID_LINK } from "../../model/frontend/auth"; -import * as mockApi from "../../api/endpoints/swaggerMockMissingEndpoints"; +import { handleLogin } from "../../api/endpoints/index"; import * as yup from 'yup'; -const useMockApi = () => mockApi; - const schema = yup.object().shape({ email: yup.string().email().required(), password: yup.string().required().min(6), @@ -28,152 +25,126 @@ const schema = yup.object().shape({ const Login = () => { const [formData, setFormData] = useState({ - email: '', - password: '' + email: "", + password: "" }); const [errors, setErrors] = useState({}); - const label = { inputProps: { "aria-label": "Checkbox demo" } }; - const { login } = useMockApi(); const navigate = useNavigate(); - - const handleSubmit = async (e) => { - e.preventDefault(); - try { - await schema.validate(formData, { abortEarly: false }) - setErrors({}) - login(formData).then(response => { - if(response.status === 200){ - localStorage.setItem("token", response.token) - navigate("/") - } - }) - } catch (err) { - if (err) { - const newErrors = {} - err.inner.forEach((error) => { - if (error.path) { - newErrors[error.path] = error.message - } - }) - setErrors(newErrors) - } - } - } + const loginUser = async () => { + try { + await schema.validate(formData, { abortEarly: false }) + setErrors({}) + + await handleLogin(formData.email, formData.password); + console.log("Login successful"); + navigate("/"); + } catch (error) { + const newErrors = {} + error.inner.forEach((e) => { + if (e.path) { + newErrors[e.path] = e.message + } + }) + setErrors(newErrors) + console.error("Login error:", error); + } + }; const handleChange = (e) => { const { name, value } = e.target setFormData((prev) => ({ ...prev, [name]: value })) } - const handleOrcidLogin = () => { - const authUrl = `${ORCID_LINK}/oauth/authorize?client_id=${CLIENT_ID}&response_type=code&scope=/authenticate&redirect_uri=${REDIRECT_URI}`; - window.location.href = authUrl; + const handleOrcidSignIn = () => { + const originUrl = window.location.href; + const orcidSignInUrl = `https://uri.olympiangods.org/u/ops/orcid-login`; + window.location.href = orcidSignInUrl; }; - + return ( - - - - Return to page - - Log in to your account - Welcome! Please enter your details. -
- - - - - - - } - checkedIcon={} - {...label} - defaultChecked - color="primary" - /> - } - label="Remember for 30 days" - /> - - - Forgot password - - - - - - - - - - - or - - - - - - - - - - - - Don't have an account? Register - - - -
-
+ + + + Return to page + + Log in to your account + Welcome! Please enter your details. +
+ + + + + + } + checkedIcon={} + defaultChecked + color="primary" + /> + } + label="Remember for 30 days" + /> + + Forgot password + + + + + + + + + + or + + + + + + + + Don’t have an account? Register + + +
+
); }; -export default Login; + +export default Login; \ No newline at end of file diff --git a/src/components/Auth/UI/Formfield.jsx b/src/components/Auth/UI/Formfield.jsx index ec00e2d2..98b60d67 100644 --- a/src/components/Auth/UI/Formfield.jsx +++ b/src/components/Auth/UI/Formfield.jsx @@ -1,17 +1,9 @@ import * as React from "react"; import PropTypes from "prop-types"; -import { - Grid, - FormControl, - Box, - FormHelperText, - OutlinedInput, - InputAdornment, - Typography, -} from "@mui/material"; +import { Grid, FormControl, Box, FormHelperText, OutlinedInput, InputAdornment, Typography } from "@mui/material"; import ErrorOutlineOutlinedIcon from '@mui/icons-material/ErrorOutlineOutlined'; -const FormField = ({ xs = 12, label, helperText, placeholder, value, name, onChange, errorMessage }) => { +const FormField = ({ xs = 12, label, helperText, placeholder, name, value, onChange, errorMessage }) => { return ( @@ -19,13 +11,13 @@ const FormField = ({ xs = 12, label, helperText, placeholder, value, name, onCha {helperText} - @@ -36,7 +28,7 @@ const FormField = ({ xs = 12, label, helperText, placeholder, value, name, onCha /> {errorMessage && {`${errorMessage.charAt(0).toUpperCase() + errorMessage.slice(1)}`}} - + ); }; @@ -45,6 +37,9 @@ FormField.propTypes = { label: PropTypes.string.isRequired, helperText: PropTypes.string, placeholder: PropTypes.string, + name: PropTypes.string.isRequired, + value: PropTypes.string.isRequired, + onChange: PropTypes.func.isRequired, }; FormField.defaultProps = { diff --git a/src/components/Auth/UI/PasswordField.jsx b/src/components/Auth/UI/PasswordField.jsx index 23313c97..5cd08dc4 100644 --- a/src/components/Auth/UI/PasswordField.jsx +++ b/src/components/Auth/UI/PasswordField.jsx @@ -14,52 +14,51 @@ import VisibilityOutlinedIcon from "@mui/icons-material/VisibilityOutlined"; import VisibilityOffOutlinedIcon from "@mui/icons-material/VisibilityOffOutlined"; import ErrorOutlineOutlinedIcon from '@mui/icons-material/ErrorOutlineOutlined'; - -const PasswordField = ({ xs = 12, label, placeholder, helperText, value, name, onChange, errorMessage }) => { +const PasswordField = ({ xs = 12, label, placeholder, helperText, name, value, onChange, errorMessage }) => { const [showPassword, setShowPassword] = useState(false); const handleClickShowPassword = () => setShowPassword((show) => !show); const handleMouseDownPassword = (event) => event.preventDefault(); - + return ( - - - - - {helperText} - - - - {errorMessage ? : <> - {showPassword ? ( - - ) : ( - - )} - - } - - - } - /> - {errorMessage && {`${errorMessage.charAt(0).toUpperCase() + errorMessage.slice(1)}`}} - - + + + + + {helperText} + + + + {errorMessage ? : <> + {showPassword ? ( + + ) : ( + + )} + + } + + + } + /> + {errorMessage && {`${errorMessage.charAt(0).toUpperCase() + errorMessage.slice(1)}`}} + + ); }; @@ -68,6 +67,9 @@ PasswordField.propTypes = { label: PropTypes.string.isRequired, placeholder: PropTypes.string, helperText: PropTypes.string, + name: PropTypes.string.isRequired, + value: PropTypes.string.isRequired, + onChange: PropTypes.func.isRequired, }; PasswordField.defaultProps = { From 85346342cf35d4e376db24bc858732ff8f76c970 Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Thu, 20 Feb 2025 16:04:00 +0100 Subject: [PATCH 03/24] ILEX-83 make changes to login api call and change from email to username --- mock/mutator/customClient.ts | 5 ++- src/api/endpoints/index.ts | 9 ++--- src/api/endpoints/interLexURIStructureAPI.ts | 30 ++++++++++++---- src/components/Auth/Login.jsx | 37 +++++++++++++------- 4 files changed, 54 insertions(+), 27 deletions(-) diff --git a/mock/mutator/customClient.ts b/mock/mutator/customClient.ts index 761805db..4ca096ae 100644 --- a/mock/mutator/customClient.ts +++ b/mock/mutator/customClient.ts @@ -12,7 +12,10 @@ export const customInstance = ( ...config, ...options, cancelToken: source.token, - }).then(({ data }) => data).catch(error => console.log("Error ", error)); + }).then(({ data }) => data).catch(error => { + console.log("Error ", error); + throw error; + }); // @ts-ignore promise.cancel = () => { diff --git a/src/api/endpoints/index.ts b/src/api/endpoints/index.ts index b7ba2fde..f803ac7a 100644 --- a/src/api/endpoints/index.ts +++ b/src/api/endpoints/index.ts @@ -344,15 +344,12 @@ export const addMessateToVariantDiscussion = async (group, variantID, message) = }); } -export const handleLogin = async (email: string, password: string) => { +export const handleLogin = async (username: string, password: string) => { try { const { postOpsUserLogin } = useApi() - const response = await postOpsUserLogin({ - email, - password, - }); + const response = await postOpsUserLogin({ username: username, password: password }); console.log("Login successful:", response); - return response.data; + return response; } catch (error) { console.error("Login failed:", error); throw error; diff --git a/src/api/endpoints/interLexURIStructureAPI.ts b/src/api/endpoints/interLexURIStructureAPI.ts index 2b79b428..554a58c8 100644 --- a/src/api/endpoints/interLexURIStructureAPI.ts +++ b/src/api/endpoints/interLexURIStructureAPI.ts @@ -483,16 +483,32 @@ export const useGetOpsUserLogin = ,) => { +// options?: SecondParameter,) => { - return customInstance( - {url: `https://uri.olympiangods.org/u/ops/user-login`, method: 'POST' - }, - options); - } +// return customInstance( +// {url: `https://uri.olympiangods.org/u/ops/user-login`, method: 'POST' +// }, +// options); +// } +export const postOpsUserLogin = ( + userData: { username: string; password: string }, + options?: SecondParameter +) => { + return customInstance( + { + url: `https://uri.olympiangods.org/u/ops/user-login`, + method: 'POST', + data: userData, + headers: { + 'Content-Type': "application/x-www-form-urlencoded", + }, + }, + options + ); +}; diff --git a/src/components/Auth/Login.jsx b/src/components/Auth/Login.jsx index 56c166c1..981ccd01 100644 --- a/src/components/Auth/Login.jsx +++ b/src/components/Auth/Login.jsx @@ -1,5 +1,5 @@ -import React, { useState } from "react"; -import { useNavigate } from "react-router-dom"; +import React, { useState, useEffect } from "react"; +import { useNavigate, useLocation } from "react-router-dom"; import { Box, Button, @@ -19,13 +19,13 @@ import { handleLogin } from "../../api/endpoints/index"; import * as yup from 'yup'; const schema = yup.object().shape({ - email: yup.string().email().required(), + username: yup.string().required().min(3), password: yup.string().required().min(6), }); const Login = () => { const [formData, setFormData] = useState({ - email: "", + username: "", password: "" }); const [errors, setErrors] = useState({}); @@ -36,9 +36,20 @@ const Login = () => { await schema.validate(formData, { abortEarly: false }) setErrors({}) - await handleLogin(formData.email, formData.password); - console.log("Login successful"); - navigate("/"); + try { + await handleLogin(formData.username || "", formData.password || "") + navigate("/") + } catch (error) { + if (error.status === 401) { + setErrors({ + auth: "Invalid username or password. Please try again", + }) + } else { + setErrors({ + auth: `${error.message}. Please try again` || "An unknown error occured. Please try again", + }) + } + } } catch (error) { const newErrors = {} error.inner.forEach((e) => { @@ -47,7 +58,6 @@ const Login = () => { } }) setErrors(newErrors) - console.error("Login error:", error); } }; @@ -72,15 +82,16 @@ const Login = () => { Log in to your account Welcome! Please enter your details.
+ {errors.auth && {errors.auth}} Date: Thu, 20 Feb 2025 16:04:34 +0100 Subject: [PATCH 04/24] ILEX-83 change from email to username in login page --- src/components/Auth/Login.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Auth/Login.jsx b/src/components/Auth/Login.jsx index 981ccd01..d8a78144 100644 --- a/src/components/Auth/Login.jsx +++ b/src/components/Auth/Login.jsx @@ -68,7 +68,7 @@ const Login = () => { const handleOrcidSignIn = () => { const originUrl = window.location.href; - const orcidSignInUrl = `https://uri.olympiangods.org/u/ops/orcid-login`; + const orcidSignInUrl = `https://uri.olympiangods.org/u/ops/orcid-login`;ygj window.location.href = orcidSignInUrl; }; From afad20591fe85bda215dfb17a2bfec48348969e4 Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Thu, 20 Feb 2025 16:16:10 +0100 Subject: [PATCH 05/24] ILEX-83 make a small change --- src/components/Auth/Login.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Auth/Login.jsx b/src/components/Auth/Login.jsx index d8a78144..981ccd01 100644 --- a/src/components/Auth/Login.jsx +++ b/src/components/Auth/Login.jsx @@ -68,7 +68,7 @@ const Login = () => { const handleOrcidSignIn = () => { const originUrl = window.location.href; - const orcidSignInUrl = `https://uri.olympiangods.org/u/ops/orcid-login`;ygj + const orcidSignInUrl = `https://uri.olympiangods.org/u/ops/orcid-login`; window.location.href = orcidSignInUrl; }; From b94f1ad37e0e4f8309c5107a1fc753ed2e3ffa50 Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Fri, 21 Feb 2025 17:14:05 +0100 Subject: [PATCH 06/24] ILEX-83 make changes to sign in with orcid --- src/App.jsx | 16 ++++++++++++++++ src/api/endpoints/index.ts | 14 ++++++++++++++ src/components/Auth/Login.jsx | 12 ++++++------ 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index e0fca193..f5d3b453 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,3 +1,4 @@ +import { useEffect } from "react"; import CssBaseline from "@mui/material/CssBaseline"; import { Box, ThemeProvider } from "@mui/material"; import { @@ -23,6 +24,7 @@ import ForgotPassword from "./components/Auth/ForgotPassword"; import ResetPassword from "./components/Auth/ResetPassword"; import SingleOrganization from "./components/SingleOrganization"; import OrganizationsCurieEditor from "./components/CurieEditor/OrganizationCurieEditor"; +import { handleOrcidLogin } from "./api/endpoints"; const PageContainer = ({ children }) => { return ( @@ -114,6 +116,20 @@ const Layout = ({ children }) => { const authPaths = ["/login", "/register", "/forgot", "/reset"]; const location = useLocation(); const isAuthPath = authPaths.includes(location.pathname); + + useEffect(() => { + const params = new URLSearchParams(location.search); + const code = params.get("code"); + if (code) {(async () => { + try { + const response = await handleOrcidLogin(code); + localStorage.setItem("token", response.token); + } catch (error) { + console.log("error: ", error) + }})(); + } + }, [location]); + // Determine whether to show the footer based on the current route const showFooter = location.pathname !== "/"; return ( diff --git a/src/api/endpoints/index.ts b/src/api/endpoints/index.ts index f803ac7a..8766a664 100644 --- a/src/api/endpoints/index.ts +++ b/src/api/endpoints/index.ts @@ -356,6 +356,20 @@ export const handleLogin = async (username: string, password: string) => { } }; +export const handleOrcidLogin = async (code: string) => { + const response = await fetch("https://uri.olympiangods.org/u/ops/orcid-login", { + method: "POST", + headers: { "Content-Type": "application/json" }, + // body: JSON.strijiuigngify({ code }), + }); + + if (!response.ok) { + throw new Error("ORCID authentication failed"); + } + + return response.json(); +}; + export const handleRegister = async ( firstName: string, lastName: string, diff --git a/src/components/Auth/Login.jsx b/src/components/Auth/Login.jsx index 981ccd01..6124d2f8 100644 --- a/src/components/Auth/Login.jsx +++ b/src/components/Auth/Login.jsx @@ -1,5 +1,5 @@ -import React, { useState, useEffect } from "react"; -import { useNavigate, useLocation } from "react-router-dom"; +import React, { useState } from "react"; +import { useNavigate } from "react-router-dom"; import { Box, Button, @@ -15,7 +15,7 @@ import { Link } from "react-router-dom"; import { CheckedIcon, UncheckedIcon, OrcidIcon } from "../../Icons"; import FormField from "./UI/Formfield"; import PasswordField from "./UI/PasswordField"; -import { handleLogin } from "../../api/endpoints/index"; +import { handleLogin, handleOrcidLogin } from "../../api/endpoints/index"; import * as yup from 'yup'; const schema = yup.object().shape({ @@ -67,9 +67,9 @@ const Login = () => { } const handleOrcidSignIn = () => { - const originUrl = window.location.href; - const orcidSignInUrl = `https://uri.olympiangods.org/u/ops/orcid-login`; - window.location.href = orcidSignInUrl; + const authUrl = `https://orcid.org/oauth/authorize?client_id=APP-JEHJFK3781EHRT1J&response_type=code&scope=/authenticate&redirect_uri=http://127.0.0.1:5173/`; + // const orcidSignInUrl = `https://uri.olympiangods.org/u/ops/orcid-login`; + window.location.href = authUrl; }; return ( From 983a650e3ecf682a392bab6bf299fae80be51116 Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Mon, 24 Feb 2025 13:09:56 +0100 Subject: [PATCH 07/24] ilex-83 push yup --- package.json | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 3ed59959..9e91387d 100644 --- a/package.json +++ b/package.json @@ -17,17 +17,18 @@ "@mui/lab": "^5.0.0-alpha.170", "@mui/material": "^5.15.15", "@mui/x-tree-view": "^7.5.0", - "body-parser" : "^1.20.3", - "cors" : "^2.8.5", + "body-parser": "^1.20.3", + "cors": "^2.8.5", "d3": "^7.1.1", - "dotenv": "16.4.7", "date-fns": "^3.6.0", - "express" : "^4.21.2", + "dotenv": "16.4.7", + "express": "^4.21.2", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.23.1", "react-rte": "^0.16.5", - "react-syntax-highlighter": "^15.5.0" + "react-syntax-highlighter": "^15.5.0", + "yup": "^1.6.1" }, "devDependencies": { "@faker-js/faker": "^8.4.1", From b49401b3ded5f7e9670158ec0d3ca432d74a4385 Mon Sep 17 00:00:00 2001 From: ddelpiano Date: Fri, 28 Feb 2025 17:18:31 +0100 Subject: [PATCH 08/24] fixing login form styling --- src/components/Auth/Login.jsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/Auth/Login.jsx b/src/components/Auth/Login.jsx index 6124d2f8..c58f3a27 100644 --- a/src/components/Auth/Login.jsx +++ b/src/components/Auth/Login.jsx @@ -66,11 +66,11 @@ const Login = () => { setFormData((prev) => ({ ...prev, [name]: value })) } - const handleOrcidSignIn = () => { - const authUrl = `https://orcid.org/oauth/authorize?client_id=APP-JEHJFK3781EHRT1J&response_type=code&scope=/authenticate&redirect_uri=http://127.0.0.1:5173/`; - // const orcidSignInUrl = `https://uri.olympiangods.org/u/ops/orcid-login`; - window.location.href = authUrl; - }; + // const handleOrcidSignIn = () => { + // const authUrl = `https://orcid.org/oauth/authorize?client_id=APP-JEHJFK3781EHRT1J&response_type=code&scope=/authenticate&redirect_uri=http://127.0.0.1:5173/`; + // // const orcidSignInUrl = `https://uri.olympiangods.org/u/ops/orcid-login`; + // window.location.href = authUrl; + // }; return ( @@ -134,7 +134,7 @@ const Login = () => { - or + or @@ -142,7 +142,7 @@ const Login = () => { startIcon={} variant="contained" className="authlightButton" - onClick={handleOrcidSignIn} + onClick={handleOrcidLogin} > Sign in with ORCID @@ -158,4 +158,4 @@ const Login = () => { ); }; -export default Login; \ No newline at end of file +export default Login; From 53f547426675f2ae64c8eafc230014d80ab2e302 Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Mon, 3 Mar 2025 15:38:48 +0100 Subject: [PATCH 09/24] ILEX-83 add orcid js widget --- public/orcid-widget.js | 240 +++++++++++++++++++++++++ src/components/Auth/Login.jsx | 2 + src/components/Auth/UI/OrcidWidget.jsx | 28 +++ 3 files changed, 270 insertions(+) create mode 100644 public/orcid-widget.js create mode 100644 src/components/Auth/UI/OrcidWidget.jsx diff --git a/public/orcid-widget.js b/public/orcid-widget.js new file mode 100644 index 00000000..87798fb5 --- /dev/null +++ b/public/orcid-widget.js @@ -0,0 +1,240 @@ +(function() { + const elementId = '#orcidWidget'; + const sandboxIssuer = "https://sandbox.orcid.org"; + const sandboxUrl = "https://sandbox.orcid.org/oauth/authorize"; + const sandboxKey = {"kty":"RSA","e":"AQAB","use":"sig","kid":"sandbox-orcid-org-3hpgosl3b6lapenh1ewsgdob3fawepoj","n":"pl-jp-kTAGf6BZUrWIYUJTvqqMVd4iAnoLS6vve-KNV0q8TxKvMre7oi9IulDcqTuJ1alHrZAIVlgrgFn88MKirZuTqHG6LCtEsr7qGD9XyVcz64oXrb9vx4FO9tLNQxvdnIWCIwyPAYWtPMHMSSD5oEVUtVL_5IaxfCJvU-FchdHiwfxvXMWmA-i3mcEEe9zggag2vUPPIqUwbPVUFNj2hE7UsZbasuIToEMFRZqSB6juc9zv6PEUueQ5hAJCEylTkzMwyBMibrt04TmtZk2w9DfKJR91555s2ZMstX4G_su1_FqQ6p9vgcuLQ6tCtrW77tta-Rw7McF_tyPmvnhQ"}; + const prodIssuer = "https://orcid.org"; + const prodUrl = "https://orcid.org/oauth/authorize"; + const prodKey = {"kty":"RSA","e":"AQAB","use":"sig","kid":"production-orcid-org-7hdmdswarosg3gjujo8agwtazgkp1ojs","n":"jxTIntA7YvdfnYkLSN4wk__E2zf_wbb0SV_HLHFvh6a9ENVRD1_rHK0EijlBzikb-1rgDQihJETcgBLsMoZVQqGj8fDUUuxnVHsuGav_bf41PA7E_58HXKPrB2C0cON41f7K3o9TStKpVJOSXBrRWURmNQ64qnSSryn1nCxMzXpaw7VUo409ohybbvN6ngxVy4QR2NCC7Fr0QVdtapxD7zdlwx6lEwGemuqs_oG5oDtrRuRgeOHmRps2R6gG5oc-JqVMrVRv6F9h4ja3UgxCDBQjOVT1BFPWmMHnHCsVYLqbbXkZUfvP2sO1dJiYd_zrQhi-FtNth9qrLLv3gkgtwQ"}; + + var authUrl; + var clientId; + var env; + var issuer; + var jQuery;// Localize jQuery variable to avoid conflicts with other versions a site may be using + var key; + var nonce; + var pubKey; + var redirectUri; + var scopes; + var signedInIdToken; + var size; + var state; + var submitUri; + + /******** Load jQuery if not present *********/ + if (window.jQuery === undefined || window.jQuery.fn.jquery !== '2.2.4') { + var script_tag = document.createElement('script'); + script_tag.setAttribute("type","text/javascript"); + script_tag.setAttribute("src", + "https://code.jquery.com/jquery-2.2.4.js"); + if (script_tag.readyState) { + script_tag.onreadystatechange = function () { // For old versions of IE + if (this.readyState == 'complete' || this.readyState == 'loaded') { + scriptLoadHandler(); + } + }; + } else { + script_tag.onload = scriptLoadHandler; + } + // Try to find the head, otherwise default to the documentElement + (document.getElementsByTagName("head")[0] || document.documentElement).appendChild(script_tag); + } else { + // The jQuery version on the window is the one we want to use + jQuery = window.jQuery; + init(); + } + /******** Called once jQuery has loaded ******/ + function scriptLoadHandler() { + // Restore $ and window.jQuery to their previous values and store the + // new jQuery in our local jQuery variable + jQuery = window.jQuery.noConflict(true); + // Call our main function + init(); + } + + function init(){ + jQuery(document).ready(function($) { + clientId = $(elementId).data("clientid"); + env = $(elementId).data("env"); + nonce = $(elementId).data("nonce"); + redirectUri = $(elementId).data("redirecturi"); + scopes = $(elementId).data("scopes"); + size = $(elementId).data("size"); + state = getQueryParameterByName("state"); + submitUri = $(elementId).data("submituri"); + + if (env === 'production'){ + issuer = prodIssuer; + authUrl = prodUrl; + key = prodKey; + } else { + issuer = sandboxIssuer; + authUrl = sandboxUrl; + key = sandboxKey; + } + + if(size=='lg'){ + $(elementId).addClass('large'); + } else { + $(elementId).addClass('small'); + } + + /******* Load CSS *******/ + var css_link = $("", { + rel: "stylesheet", + type: "text/css", + href: "orcid-widget.css" + }); + css_link.appendTo('head'); + + /******* Load jsrasign *******/ + $.getScript( "https://kjur.github.io/jsrsasign/jsrsasign-latest-all-min.js", function( data, textStatus, jqxhr ) { + //check for response - if exists process it + if (getFragmentParameterByName("id_token")){ + var id_token = getFragmentParameterByName("id_token"); + if(id_token){ + if (checkSig(id_token)){ + signedInIdToken = JSON.parse(KJUR.jws.JWS.parse(id_token).payloadPP); + if(submitUri){ + submitIdTokenData($, id_token, signedInIdToken); + } else { + showSuccess($, id_token, signedInIdToken); + } + } else { + signedInIdToken = null; + showError($); + } + } + } else if (getFragmentParameterByName("error")){ + var error = getFragmentParameterByName("error"); + if('access_denied' === error) { + submitUserDenied($); + } else { + showError($); + } + } + //if we don't have an id_token or a signed in user, show sign in button + else { + showAuthButton($); + } + + $( "#removeOrcidId" ).click(function() { + $(elementId).empty(); + showAuthButton($); + }); + }); + + }); + } + + function buildReturnUrl(nonce){ + var url = authUrl+"?response_type=token&redirect_uri="+redirectUri+"&client_id="+clientId+"&scope=openid"; + if(scopes) { + url += "%20"+scopes; + } + if (nonce) + url += "&nonce="+nonce; + if (state) + url += "&state="+state; + return url; + } + + function checkSig(id_token){ + pubKey = KEYUTIL.getKey(key); + return KJUR.jws.JWS.verifyJWT(id_token, pubKey, { + alg: ['RS256'], iss: [issuer] , aud:clientId,gracePeriod: 15*60 //15 mins skew allowed + }); + } + + function getFragmentParameterByName(name) { + name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); + var regex = new RegExp("[\\#&]" + name + "=([^&#]*)"), + results = regex.exec(window.location.hash); + return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); + } + + function getQueryParameterByName(name) { + name = name.replace(/[\[\]]/g, '\\$&'); + var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'), + results = regex.exec(window.location.href); + if (!results) return null; + if (!results[2]) return ''; + return decodeURIComponent(results[2].replace(/\+/g, ' ')); + } + + function showAuthButton($){ + $('Connect your ORCID iD').appendTo(elementId); + } + + function showError($){ + $('

Oops, something went terribly wrong
and we couldn\'t fetch your ORCID iD

').appendTo(elementId); + } + + function showDenied($) { + $('

Oops, you have denied access
so we will not be able to update your ORCID iD

').appendTo(elementId); + } + + function showSuccess($, id_token, signedInIdToken){ + $('

Thanks, ' +signedInIdToken.given_name+ " " +signedInIdToken.family_name+ '!
' + issuer + '/' + signedInIdToken.sub + '

').appendTo(elementId); + //Add hidden inputs with ORCID info + $('').attr({ + type: 'hidden', + id: 'orcidId', + name: 'orcidId', + value: signedInIdToken.iss + "/" + signedInIdToken.sub + }).appendTo(elementId); + $('').attr({ + type: 'hidden', + id: 'orcidGivenName', + name: 'orcidGivenName', + value: signedInIdToken.given_name + }).appendTo(elementId); + $('').attr({ + type: 'hidden', + id: 'orcidFamilyName', + name: 'orcidFamilyName', + value: signedInIdToken.family_name + }).appendTo(elementId); + $('').attr({ + type: 'hidden', + id: 'orcidIdToken', + name: 'orcidIdToken', + value: id_token + }).appendTo(elementId); + if(state){ + $('').attr({ + type: 'hidden', + id: 'orcidState', + name: 'orcidState', + value: state + }).appendTo(elementId); + } + + } + function submitIdTokenData($, id_token, signedInIdToken){ + $.ajax({ + url:submitUri, + type:'POST', + data: JSON.stringify({ "id_token": id_token, "state": state}), + contentType:'application/json; charset=utf-8'}) + .done(function(){ + showSuccess($, id_token, signedInIdToken); + }).fail(function(jqXHR, textStatus, errorThrown) { + showError($); + }); + } + + function submitUserDenied($){ + $.ajax({ + url:submitUri, + type:'POST', + data: JSON.stringify({ "denied": true, "state": state}), + contentType:'application/json; charset=utf-8'}) + .done(function(){ + showDenied($); + }).fail(function(jqXHR, textStatus, errorThrown) { + showError($); + }); + } +})(); \ No newline at end of file diff --git a/src/components/Auth/Login.jsx b/src/components/Auth/Login.jsx index c58f3a27..95235423 100644 --- a/src/components/Auth/Login.jsx +++ b/src/components/Auth/Login.jsx @@ -17,6 +17,7 @@ import FormField from "./UI/Formfield"; import PasswordField from "./UI/PasswordField"; import { handleLogin, handleOrcidLogin } from "../../api/endpoints/index"; import * as yup from 'yup'; +import OrcidWidget from "./UI/OrcidWidget"; const schema = yup.object().shape({ username: yup.string().required().min(3), @@ -138,6 +139,7 @@ const Login = () => { + diff --git a/src/components/Auth/UI/OrcidWidget.jsx b/src/components/Auth/UI/OrcidWidget.jsx index 28d2f64a..9b859b42 100644 --- a/src/components/Auth/UI/OrcidWidget.jsx +++ b/src/components/Auth/UI/OrcidWidget.jsx @@ -1,19 +1,7 @@ -import { useEffect, useRef } from "react" +import React from "react"; +import PropTypes from "prop-types"; const OrcidWidget = ({ clientId, redirectUri }) => { - const initialized = useRef(false); - - useEffect(() => { - if (initialized.current) return - initialized.current = true - - const script = document.createElement("script") - script.id = "orcid-widget-script" - script.src = "/orcid-widget.js" - script.async = true - document.body.appendChild(script) - }, []) - return (
{ /> ) } +OrcidWidget.propTypes = { + clientId: PropTypes.string.isRequired, + redirectUri: PropTypes.string.isRequired, +}; export default OrcidWidget; From 421e6eed00f7578f9de9f87cc7b1a21fb196c655 Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Fri, 28 Mar 2025 15:07:52 +0100 Subject: [PATCH 14/24] ILEX-83 centralize api service --- public/orcid-widget.js | 240 ----------------------- src/api/endpoints/apiActions.ts | 42 ++++ src/api/endpoints/apiService.ts | 9 + src/components/Auth/Login.jsx | 115 +++++++---- src/components/Auth/UI/Formfield.jsx | 1 + src/components/Auth/UI/OrcidWidget.jsx | 21 -- src/components/Auth/UI/PasswordField.jsx | 1 + src/config.js | 1 + 8 files changed, 130 insertions(+), 300 deletions(-) delete mode 100644 public/orcid-widget.js create mode 100644 src/api/endpoints/apiActions.ts create mode 100644 src/api/endpoints/apiService.ts delete mode 100644 src/components/Auth/UI/OrcidWidget.jsx diff --git a/public/orcid-widget.js b/public/orcid-widget.js deleted file mode 100644 index 77c69261..00000000 --- a/public/orcid-widget.js +++ /dev/null @@ -1,240 +0,0 @@ -(function() { - const elementId = '#orcidWidget'; - const sandboxIssuer = "https://sandbox.orcid.org"; - const sandboxUrl = "https://sandbox.orcid.org/oauth/authorize"; - const sandboxKey = {"kty":"RSA","e":"AQAB","use":"sig","kid":"sandbox-orcid-org-3hpgosl3b6lapenh1ewsgdob3fawepoj","n":"pl-jp-kTAGf6BZUrWIYUJTvqqMVd4iAnoLS6vve-KNV0q8TxKvMre7oi9IulDcqTuJ1alHrZAIVlgrgFn88MKirZuTqHG6LCtEsr7qGD9XyVcz64oXrb9vx4FO9tLNQxvdnIWCIwyPAYWtPMHMSSD5oEVUtVL_5IaxfCJvU-FchdHiwfxvXMWmA-i3mcEEe9zggag2vUPPIqUwbPVUFNj2hE7UsZbasuIToEMFRZqSB6juc9zv6PEUueQ5hAJCEylTkzMwyBMibrt04TmtZk2w9DfKJR91555s2ZMstX4G_su1_FqQ6p9vgcuLQ6tCtrW77tta-Rw7McF_tyPmvnhQ"}; - const prodIssuer = "https://orcid.org"; - const prodUrl = "https://orcid.org/oauth/authorize"; - const prodKey = {"kty":"RSA","e":"AQAB","use":"sig","kid":"production-orcid-org-7hdmdswarosg3gjujo8agwtazgkp1ojs","n":"jxTIntA7YvdfnYkLSN4wk__E2zf_wbb0SV_HLHFvh6a9ENVRD1_rHK0EijlBzikb-1rgDQihJETcgBLsMoZVQqGj8fDUUuxnVHsuGav_bf41PA7E_58HXKPrB2C0cON41f7K3o9TStKpVJOSXBrRWURmNQ64qnSSryn1nCxMzXpaw7VUo409ohybbvN6ngxVy4QR2NCC7Fr0QVdtapxD7zdlwx6lEwGemuqs_oG5oDtrRuRgeOHmRps2R6gG5oc-JqVMrVRv6F9h4ja3UgxCDBQjOVT1BFPWmMHnHCsVYLqbbXkZUfvP2sO1dJiYd_zrQhi-FtNth9qrLLv3gkgtwQ"}; - - var authUrl; - var clientId; - var env; - var issuer; - var jQuery;// Localize jQuery variable to avoid conflicts with other versions a site may be using - var key; - var nonce; - var pubKey; - var redirectUri; - var scopes; - var signedInIdToken; - var size; - var state; - var submitUri; - - /******** Load jQuery if not present *********/ - if (window.jQuery === undefined || window.jQuery.fn.jquery !== '2.2.4') { - var script_tag = document.createElement('script'); - script_tag.setAttribute("type","text/javascript"); - script_tag.setAttribute("src", - "https://code.jquery.com/jquery-2.2.4.js"); - if (script_tag.readyState) { - script_tag.onreadystatechange = function () { // For old versions of IE - if (this.readyState == 'complete' || this.readyState == 'loaded') { - scriptLoadHandler(); - } - }; - } else { - script_tag.onload = scriptLoadHandler; - } - // Try to find the head, otherwise default to the documentElement - (document.getElementsByTagName("head")[0] || document.documentElement).appendChild(script_tag); - } else { - // The jQuery version on the window is the one we want to use - jQuery = window.jQuery; - init(); - } - /******** Called once jQuery has loaded ******/ - function scriptLoadHandler() { - // Restore $ and window.jQuery to their previous values and store the - // new jQuery in our local jQuery variable - jQuery = window.jQuery.noConflict(true); - // Call our main function - init(); - } - - function init(){ - jQuery(document).ready(function($) { - clientId = $(elementId).data("clientid"); - env = $(elementId).data("env"); - nonce = $(elementId).data("nonce"); - redirectUri = $(elementId).data("redirecturi"); - scopes = $(elementId).data("scopes"); - size = $(elementId).data("size"); - state = getQueryParameterByName("state"); - submitUri = $(elementId).data("submituri"); - - if (env === 'production'){ - issuer = prodIssuer; - authUrl = prodUrl; - key = prodKey; - } else { - issuer = sandboxIssuer; - authUrl = sandboxUrl; - key = sandboxKey; - } - - if(size=='lg'){ - $(elementId).addClass('large'); - } else { - $(elementId).addClass('small'); - } - - /******* Load CSS *******/ - var css_link = $("", { - rel: "stylesheet", - type: "text/css", - href: "orcid-widget.css" - }); - css_link.appendTo('head'); - - /******* Load jsrasign *******/ - $.getScript( "https://kjur.github.io/jsrsasign/jsrsasign-latest-all-min.js", function( data, textStatus, jqxhr ) { - //check for response - if exists process it - if (getFragmentParameterByName("id_token")){ - var id_token = getFragmentParameterByName("id_token"); - if(id_token){ - if (checkSig(id_token)){ - signedInIdToken = JSON.parse(KJUR.jws.JWS.parse(id_token).payloadPP); - if(submitUri){ - submitIdTokenData($, id_token, signedInIdToken); - } else { - showSuccess($, id_token, signedInIdToken); - } - } else { - signedInIdToken = null; - showError($); - } - } - } else if (getFragmentParameterByName("error")){ - var error = getFragmentParameterByName("error"); - if('access_denied' === error) { - submitUserDenied($); - } else { - showError($); - } - } - //if we don't have an id_token or a signed in user, show sign in button - else { - showAuthButton($); - } - - $( "#removeOrcidId" ).click(function() { - $(elementId).empty(); - showAuthButton($); - }); - }); - - }); - } - - function buildReturnUrl(nonce){ - var url = authUrl+"?response_type=token&redirect_uri="+redirectUri+"&client_id="+clientId+"&scope=openid"; - if(scopes) { - url += "%20"+scopes; - } - if (nonce) - url += "&nonce="+nonce; - if (state) - url += "&state="+state; - return url; - } - - function checkSig(id_token){ - pubKey = KEYUTIL.getKey(key); - return KJUR.jws.JWS.verifyJWT(id_token, pubKey, { - alg: ['RS256'], iss: [issuer] , aud:clientId,gracePeriod: 15*60 //15 mins skew allowed - }); - } - - function getFragmentParameterByName(name) { - name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); - var regex = new RegExp("[\\#&]" + name + "=([^&#]*)"), - results = regex.exec(window.location.hash); - return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); - } - - function getQueryParameterByName(name) { - name = name.replace(/[\[\]]/g, '\\$&'); - var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'), - results = regex.exec(window.location.href); - if (!results) return null; - if (!results[2]) return ''; - return decodeURIComponent(results[2].replace(/\+/g, ' ')); - } - - function showAuthButton($){ - $('Connect your ORCID iD').appendTo(elementId); - } - - function showError($){ - $('

Oops, something went terribly wrong
and we couldn\'t fetch your ORCID iD

').appendTo(elementId); - } - - function showDenied($) { - $('

Oops, you have denied access
so we will not be able to update your ORCID iD

').appendTo(elementId); - } - - function showSuccess($, id_token, signedInIdToken){ - $('

Thanks, ' +signedInIdToken.given_name+ " " +signedInIdToken.family_name+ '!
' + issuer + '/' + signedInIdToken.sub + '

').appendTo(elementId); - //Add hidden inputs with ORCID info - $('').attr({ - type: 'hidden', - id: 'orcidId', - name: 'orcidId', - value: signedInIdToken.iss + "/" + signedInIdToken.sub - }).appendTo(elementId); - $('').attr({ - type: 'hidden', - id: 'orcidGivenName', - name: 'orcidGivenName', - value: signedInIdToken.given_name - }).appendTo(elementId); - $('').attr({ - type: 'hidden', - id: 'orcidFamilyName', - name: 'orcidFamilyName', - value: signedInIdToken.family_name - }).appendTo(elementId); - $('').attr({ - type: 'hidden', - id: 'orcidIdToken', - name: 'orcidIdToken', - value: id_token - }).appendTo(elementId); - if(state){ - $('').attr({ - type: 'hidden', - id: 'orcidState', - name: 'orcidState', - value: state - }).appendTo(elementId); - } - - } - function submitIdTokenData($, id_token, signedInIdToken){ - $.ajax({ - url:submitUri, - type:'POST', - data: JSON.stringify({ "id_token": id_token, "state": state}), - contentType:'application/json; charset=utf-8'}) - .done(function(){ - showSuccess($, id_token, signedInIdToken); - }).fail(function(jqXHR, textStatus, errorThrown) { - showError($); - }); - } - - function submitUserDenied($){ - $.ajax({ - url:submitUri, - type:'POST', - data: JSON.stringify({ "denied": true, "state": state}), - contentType:'application/json; charset=utf-8'}) - .done(function(){ - showDenied($); - }).fail(function(jqXHR, textStatus, errorThrown) { - showError($); - }); - } -})(); \ No newline at end of file diff --git a/src/api/endpoints/apiActions.ts b/src/api/endpoints/apiActions.ts new file mode 100644 index 00000000..6c7efabc --- /dev/null +++ b/src/api/endpoints/apiActions.ts @@ -0,0 +1,42 @@ +import { AxiosRequestConfig } from 'axios'; +import { customInstance } from '../../../mock/mutator/customClient'; +import { API_CONFIG } from '../../config'; + + +type SecondParameter any> = Parameters[1]; + +export const createPostRequest = (endpoint: string, contentType = "application/json") => { + return (data?: D, options?: SecondParameter) => { + return customInstance( + { + url: API_CONFIG.OLYMPIAN_GODS + endpoint, + method: "POST", + data: data, + headers: { + "Content-Type": contentType, + }, + }, + options, + ) + } +} + +export const createGetRequest = (endpoint: string, contentType?: string) => { + return (params?: P, options?: SecondParameter, signal?: AbortSignal) => { + const config: AxiosRequestConfig = { + url: endpoint, + method: "GET", + params, + signal, + } + + if (contentType) { + config.headers = { + ...config.headers, + "Content-Type": contentType, + } + } + + return customInstance(config, options) + } +} \ No newline at end of file diff --git a/src/api/endpoints/apiService.ts b/src/api/endpoints/apiService.ts new file mode 100644 index 00000000..2307dda3 --- /dev/null +++ b/src/api/endpoints/apiService.ts @@ -0,0 +1,9 @@ +import { createPostRequest } from "./apiActions"; +import { API_CONFIG } from "../../config"; + +export interface LoginRequest { + username: string + password: string +} + +export const login = createPostRequest(API_CONFIG.REAL_API.SIGNIN, "application/x-www-form-urlencoded") diff --git a/src/components/Auth/Login.jsx b/src/components/Auth/Login.jsx index f440d590..fffb86b0 100644 --- a/src/components/Auth/Login.jsx +++ b/src/components/Auth/Login.jsx @@ -1,5 +1,4 @@ -import React, { useState } from "react"; -import { useNavigate } from "react-router-dom"; +import * as React from "react"; import { Box, Button, @@ -8,16 +7,18 @@ import { Grid, Paper, Typography, + Alert, } from "@mui/material"; import { ArrowBack } from "@mui/icons-material"; import Checkbox from "@mui/material/Checkbox"; -import { Link } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; import { CheckedIcon, UncheckedIcon, OrcidIcon } from "../../Icons"; import FormField from "./UI/Formfield"; import PasswordField from "./UI/PasswordField"; -import { handleLogin } from "../../api/endpoints/index"; -import * as yup from 'yup'; -import OrcidWidget from "./UI/OrcidWidget"; +import { login } from "../../api/endpoints/apiService"; +import { API_CONFIG } from "../../config"; +import { GlobalDataContext } from "../../contexts/DataContext"; +import * as yup from "yup"; const schema = yup.object().shape({ username: yup.string().required().min(3), @@ -25,47 +26,74 @@ const schema = yup.object().shape({ }); const Login = () => { - const [formData, setFormData] = useState({ + const [formData, setFormData] = React.useState({ username: "", - password: "" + password: "", }); - const [errors, setErrors] = useState({}); + const [errors, setErrors] = React.useState({}); + const { setUserData } = React.useContext(GlobalDataContext); const navigate = useNavigate(); + React.useEffect(() => { + let eventMethod = window.addEventListener ? "addEventListener" : "attachEvent"; + let eventer = window[eventMethod]; + let messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message"; + eventer(messageEvent, function (e) { + console.log(e); + + let response = { + code: "200", + status: "200", + username: "johndoe", + email: "johndoe@gmail.com", + token: "1234567890", + orcid: "0000-0000-0000-0000", + }; + + if(response.code === 200) { + setUserData({ username: response.username, email: response.email, id: response.orcid }); + navigate("/"); + } else if (response.code === 401) { + setErrors((prevErrors) => ({ + ...prevErrors, + auth: "Invalid username or password. Please try again", + })); + } else { + setErrors((prevErrors) => ({ + ...prevErrors, + auth: "An unknown error occurred. Please try again", + })); + } + }); + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const handleInputChange = (e) => { + const { name, value } = e.target; + setFormData((prev) => ({ ...prev, [name]: value })); + }; + const loginUser = async () => { try { await schema.validate(formData, { abortEarly: false }) setErrors({}) - try { - await handleLogin(formData.username || "", formData.password || "") - navigate("/") - } catch (error) { - if (error.status === 401) { - setErrors({ - auth: "Invalid username or password. Please try again", - }) - } else { - setErrors({ - auth: `${error.message}. Please try again` || "An unknown error occured. Please try again", - }) - } - } + await login({ username: formData.username, password: formData.password }) + } catch (error) { - const newErrors = {} - error.inner.forEach((e) => { - if (e.path) { - newErrors[e.path] = e.message - } - }) - setErrors(newErrors) + console.error("Login error:", error); + setErrors((prevErrors) => ({ + ...prevErrors, + auth: "An unknown error occurred. Please try again", + })); } }; - const handleChange = (e) => { - const { name, value } = e.target - setFormData((prev) => ({ ...prev, [name]: value })) - } + const handleOrcidSignIn = () => { + const orcidSignInUrl = API_CONFIG.OLYMPIAN_GODS + API_CONFIG.REAL_API.LOGIN_ORCID; + window.open(orcidSignInUrl, "Orcid Sign In", "width=600,height=800").focus(); + }; return ( @@ -76,8 +104,8 @@ const Login = () => { Log in to your account Welcome! Please enter your details. + {errors.auth && {errors.auth}} - {errors.auth && {errors.auth}} { helperText="Required" placeholder="Enter your username" value={formData.username} - onChange={handleChange} + onChange={handleInputChange} errorMessage={errors.username} /> { placeholder="Enter your password" helperText="Required" value={formData.password} - onChange={handleChange} + onChange={handleInputChange} errorMessage={errors.password} /> @@ -129,11 +157,20 @@ const Login = () => { - or + + or + - + diff --git a/src/components/Auth/UI/Formfield.jsx b/src/components/Auth/UI/Formfield.jsx index 7a81da29..0b5c7ea0 100644 --- a/src/components/Auth/UI/Formfield.jsx +++ b/src/components/Auth/UI/Formfield.jsx @@ -39,6 +39,7 @@ FormField.propTypes = { name: PropTypes.string.isRequired, value: PropTypes.string.isRequired, onChange: PropTypes.func.isRequired, + errorMessage: PropTypes.string.isRequired, }; FormField.defaultProps = { diff --git a/src/components/Auth/UI/OrcidWidget.jsx b/src/components/Auth/UI/OrcidWidget.jsx deleted file mode 100644 index 9b859b42..00000000 --- a/src/components/Auth/UI/OrcidWidget.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -const OrcidWidget = ({ clientId, redirectUri }) => { - return ( -
- ) -} -OrcidWidget.propTypes = { - clientId: PropTypes.string.isRequired, - redirectUri: PropTypes.string.isRequired, -}; - -export default OrcidWidget; diff --git a/src/components/Auth/UI/PasswordField.jsx b/src/components/Auth/UI/PasswordField.jsx index ba824651..700ab0c5 100644 --- a/src/components/Auth/UI/PasswordField.jsx +++ b/src/components/Auth/UI/PasswordField.jsx @@ -70,6 +70,7 @@ PasswordField.propTypes = { name: PropTypes.string.isRequired, value: PropTypes.string.isRequired, onChange: PropTypes.func.isRequired, + errorMessage: PropTypes.string.isRequired, }; PasswordField.defaultProps = { diff --git a/src/config.js b/src/config.js index 963c8c10..7605e479 100644 --- a/src/config.js +++ b/src/config.js @@ -23,6 +23,7 @@ export const API_CONFIG = { REAL_API: { GET_ENDPOINTS_ILX: "/api/real/endpoints-ilx", PATCH_ENDPOINTS_ILX: "/api/real/patch-endpoints-ilx", + SIGNIN: "/u/ops/user-login" }, OLYMPIAN_GODS : "https://uri.olympiangods.org", BASE_SCICRUNCH_URL: "/api/elasticsearch?key=", From efd3b9dbbbc4f3f8babd24a1d60344dde9ccef2c Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Tue, 1 Apr 2025 12:41:05 +0200 Subject: [PATCH 15/24] ILEX-83 add base url --- src/api/endpoints/apiActions.ts | 4 ++-- src/config.js | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/api/endpoints/apiActions.ts b/src/api/endpoints/apiActions.ts index 6c7efabc..77fd1ec0 100644 --- a/src/api/endpoints/apiActions.ts +++ b/src/api/endpoints/apiActions.ts @@ -9,7 +9,7 @@ export const createPostRequest = (endpoint: string, contentTyp return (data?: D, options?: SecondParameter) => { return customInstance( { - url: API_CONFIG.OLYMPIAN_GODS + endpoint, + url: API_CONFIG.BASE_URL + endpoint, method: "POST", data: data, headers: { @@ -24,7 +24,7 @@ export const createPostRequest = (endpoint: string, contentTyp export const createGetRequest = (endpoint: string, contentType?: string) => { return (params?: P, options?: SecondParameter, signal?: AbortSignal) => { const config: AxiosRequestConfig = { - url: endpoint, + url: API_CONFIG.BASE_URL + endpoint, method: "GET", params, signal, diff --git a/src/config.js b/src/config.js index 7605e479..36e510df 100644 --- a/src/config.js +++ b/src/config.js @@ -28,4 +28,5 @@ export const API_CONFIG = { OLYMPIAN_GODS : "https://uri.olympiangods.org", BASE_SCICRUNCH_URL: "/api/elasticsearch?key=", SCICRUNCH_KEY: import.meta.env.VITE_SCICRUNCH_API_KEY, + BASE_URL: import.meta.env.BASE_URL }; From 2223eb5793feeffe0fc5efe14d0b69789698c96a Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Tue, 1 Apr 2025 12:54:40 +0200 Subject: [PATCH 16/24] ILEX-83 fix base url --- src/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.js b/src/config.js index 36e510df..c1376061 100644 --- a/src/config.js +++ b/src/config.js @@ -28,5 +28,5 @@ export const API_CONFIG = { OLYMPIAN_GODS : "https://uri.olympiangods.org", BASE_SCICRUNCH_URL: "/api/elasticsearch?key=", SCICRUNCH_KEY: import.meta.env.VITE_SCICRUNCH_API_KEY, - BASE_URL: import.meta.env.BASE_URL + BASE_URL: import.meta.env.VITE_BASE_URL }; From 81b46cb546e1a152cc0f3473fa65a438835ea94e Mon Sep 17 00:00:00 2001 From: ddelpiano Date: Tue, 1 Apr 2025 19:26:24 +0200 Subject: [PATCH 17/24] fixing logging --- mock/mutator/customClient.ts | 7 +++---- src/api/custom-axios-instance.ts | 4 ++-- src/components/Auth/Login.jsx | 15 ++++++++------- src/config.js | 2 +- vite.config.js | 24 +++++++++++++++++++++++- 5 files changed, 37 insertions(+), 15 deletions(-) diff --git a/mock/mutator/customClient.ts b/mock/mutator/customClient.ts index 4ca096ae..b09388ab 100644 --- a/mock/mutator/customClient.ts +++ b/mock/mutator/customClient.ts @@ -1,6 +1,6 @@ import Axios, { AxiosRequestConfig } from 'axios'; - -export const AXIOS_INSTANCE = Axios.create({ baseURL: '.' }); // use your own URL here or environment variable +import { API_CONFIG } from '../../src/config'; +export const AXIOS_INSTANCE = Axios.create({ baseURL: API_CONFIG.BASE_URL }); // add a second `options` argument here if you want to pass extra options to each generated query export const customInstance = ( @@ -13,7 +13,6 @@ export const customInstance = ( ...options, cancelToken: source.token, }).then(({ data }) => data).catch(error => { - console.log("Error ", error); throw error; }); @@ -33,4 +32,4 @@ export type BodyType = BodyData; // Or, in case you want to wrap the body type (optional) // (if the custom instance is processing data before sending it, like changing the case for example) -export type BodyType = CamelCase; \ No newline at end of file +export type BodyType = CamelCase; diff --git a/src/api/custom-axios-instance.ts b/src/api/custom-axios-instance.ts index fa0487a8..664f5aab 100644 --- a/src/api/custom-axios-instance.ts +++ b/src/api/custom-axios-instance.ts @@ -1,5 +1,5 @@ import axios from "axios"; -import { API_CONFIG } from "../../config/config.js"; +import { API_CONFIG } from "../config.ts"; export const customAxiosInstance = axios.create({ baseURL: API_CONFIG.BASE_SCICRUNCH_URL, @@ -14,4 +14,4 @@ customAxiosInstance.interceptors.request.use((config) => { api_key: process.env.API_KEY, // Load API key from .env }; return config; -}); \ No newline at end of file +}); diff --git a/src/components/Auth/Login.jsx b/src/components/Auth/Login.jsx index fffb86b0..c92d1d8e 100644 --- a/src/components/Auth/Login.jsx +++ b/src/components/Auth/Login.jsx @@ -31,6 +31,8 @@ const Login = () => { password: "", }); const [errors, setErrors] = React.useState({}); + // eslint-disable-next-line no-unused-vars + const [isLoading, setIsLoading] = React.useState(false); const { setUserData } = React.useContext(GlobalDataContext); const navigate = useNavigate(); @@ -39,8 +41,6 @@ const Login = () => { let eventer = window[eventMethod]; let messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message"; eventer(messageEvent, function (e) { - console.log(e); - let response = { code: "200", status: "200", @@ -67,7 +67,7 @@ const Login = () => { }); // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [isLoading]); const handleInputChange = (e) => { const { name, value } = e.target; @@ -76,11 +76,12 @@ const Login = () => { const loginUser = async () => { try { - await schema.validate(formData, { abortEarly: false }) - setErrors({}) + // await schema.validate(formData, { abortEarly: false }) + // setErrors({}) + + const result = await login({ username: formData.username, password: formData.password }) + console.log(result); - await login({ username: formData.username, password: formData.password }) - } catch (error) { console.error("Login error:", error); setErrors((prevErrors) => ({ diff --git a/src/config.js b/src/config.js index c1376061..ec7326cf 100644 --- a/src/config.js +++ b/src/config.js @@ -28,5 +28,5 @@ export const API_CONFIG = { OLYMPIAN_GODS : "https://uri.olympiangods.org", BASE_SCICRUNCH_URL: "/api/elasticsearch?key=", SCICRUNCH_KEY: import.meta.env.VITE_SCICRUNCH_API_KEY, - BASE_URL: import.meta.env.VITE_BASE_URL + BASE_URL: import.meta.env.MODE === "production" ? API_CONFIG.OLYMPIAN_GODS : window.location.origin, }; diff --git a/vite.config.js b/vite.config.js index 47951ca5..56292794 100644 --- a/vite.config.js +++ b/vite.config.js @@ -15,7 +15,29 @@ export default defineConfig({ // Forward headers correctly "Content-Type": "application/json", }, - } + }, + "^/u/ops/.*": { + target: "https://uri.olympiangods.org", + changeOrigin: true, + secure: false, + configure: (proxy, _options) => { + console.log(_options); + proxy.on('error', (err, _req, _res) => { + console.log('proxy error', err); + console.log('proxy request', _req); + console.log('proxy response', _res); + }); + proxy.on('proxyReq', (proxyReq, req, _res) => { + console.log('Sending Request to the Target:', req.method, req.url); + console.log('Response:', _res); + console.log('Request:', proxyReq); + }); + proxy.on('proxyRes', (proxyRes, req, _res) => { + console.log('Received response', _res); + console.log('Received Response from the Target:', proxyRes.statusCode, req.url); + }); + }, + }, }, }, }); From 2e5fd04be85c603712c426fa19f0fd52177e11e0 Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Thu, 3 Apr 2025 14:47:11 +0200 Subject: [PATCH 18/24] ILEX-83 connect orcid login page --- src/components/Auth/Login.jsx | 208 +++++++++++++++++----------------- src/config.js | 3 +- src/contexts/DataContext.jsx | 19 ++-- 3 files changed, 120 insertions(+), 110 deletions(-) diff --git a/src/components/Auth/Login.jsx b/src/components/Auth/Login.jsx index c92d1d8e..fd1b43bc 100644 --- a/src/components/Auth/Login.jsx +++ b/src/components/Auth/Login.jsx @@ -8,6 +8,7 @@ import { Paper, Typography, Alert, + CircularProgress } from "@mui/material"; import { ArrowBack } from "@mui/icons-material"; import Checkbox from "@mui/material/Checkbox"; @@ -31,7 +32,6 @@ const Login = () => { password: "", }); const [errors, setErrors] = React.useState({}); - // eslint-disable-next-line no-unused-vars const [isLoading, setIsLoading] = React.useState(false); const { setUserData } = React.useContext(GlobalDataContext); const navigate = useNavigate(); @@ -41,31 +41,27 @@ const Login = () => { let eventer = window[eventMethod]; let messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message"; eventer(messageEvent, function (e) { - let response = { - code: "200", - status: "200", - username: "johndoe", - email: "johndoe@gmail.com", - token: "1234567890", - orcid: "0000-0000-0000-0000", - }; + if (!e.data || !e.data.orcid_meta) return; + const { code, orcid_meta } = e.data; - if(response.code === 200) { - setUserData({ username: response.username, email: response.email, id: response.orcid }); - navigate("/"); - } else if (response.code === 401) { - setErrors((prevErrors) => ({ - ...prevErrors, + if (code === 200 || code === 302) { + setUserData({ name: orcid_meta.name, id: orcid_meta.orcid }); + navigate("/") + } else if (code === 401) { + setErrors((prev) => ({ + ...prev, auth: "Invalid username or password. Please try again", })); } else { - setErrors((prevErrors) => ({ - ...prevErrors, + setErrors((prev) => ({ + ...prev, auth: "An unknown error occurred. Please try again", })); } }); + setIsLoading(false) + // eslint-disable-next-line react-hooks/exhaustive-deps }, [isLoading]); @@ -76,110 +72,118 @@ const Login = () => { const loginUser = async () => { try { - // await schema.validate(formData, { abortEarly: false }) - // setErrors({}) + setIsLoading(true) + await schema.validate(formData, { abortEarly: false }) + setErrors({}) const result = await login({ username: formData.username, password: formData.password }) - console.log(result); - + console.log("result: ", result) + navigate("/") } catch (error) { console.error("Login error:", error); setErrors((prevErrors) => ({ ...prevErrors, auth: "An unknown error occurred. Please try again", })); + } finally { + setIsLoading(false) } }; const handleOrcidSignIn = () => { - const orcidSignInUrl = API_CONFIG.OLYMPIAN_GODS + API_CONFIG.REAL_API.LOGIN_ORCID; + setIsLoading(true) + const orcidSignInUrl = `${API_CONFIG.OLYMPIAN_GODS}${API_CONFIG.REAL_API.ORCID_SIGNIN}?aspopup=true`; window.open(orcidSignInUrl, "Orcid Sign In", "width=600,height=800").focus(); }; return ( - - - - Return to page - - Log in to your account - Welcome! Please enter your details. - {errors.auth && {errors.auth}} - - - - - - - } - checkedIcon={} - defaultChecked - color="primary" - /> - } - label="Remember for 30 days" - /> - - Forgot password - - + {isLoading ? + + : ( + + + + Return to page + + Log in to your account + Welcome! Please enter your details. + {errors.auth && {errors.auth}} + + + + + + + } + checkedIcon={} + defaultChecked + color="primary" + /> + } + label="Remember for 30 days" + /> + + Forgot password + + + + + + + + + + + or + + - - - - - - - - or + + + + + + Don’t have an account? Register - - - - - - - - Don’t have an account? Register - - - - + + + + )} ); }; diff --git a/src/config.js b/src/config.js index ec7326cf..d0382e9f 100644 --- a/src/config.js +++ b/src/config.js @@ -23,7 +23,8 @@ export const API_CONFIG = { REAL_API: { GET_ENDPOINTS_ILX: "/api/real/endpoints-ilx", PATCH_ENDPOINTS_ILX: "/api/real/patch-endpoints-ilx", - SIGNIN: "/u/ops/user-login" + SIGNIN: "/u/ops/user-login", + ORCID_SIGNIN: "/u/ops/orcid-new" }, OLYMPIAN_GODS : "https://uri.olympiangods.org", BASE_SCICRUNCH_URL: "/api/elasticsearch?key=", diff --git a/src/contexts/DataContext.jsx b/src/contexts/DataContext.jsx index fc3ba719..80eb7530 100644 --- a/src/contexts/DataContext.jsx +++ b/src/contexts/DataContext.jsx @@ -34,19 +34,24 @@ const GlobalDataProvider = ({ children }) => { setEditBulkSearchFilters(filters); }; + const setUserData = (user) => { + setUser(user); + } + // eslint-disable-next-line react-hooks/exhaustive-deps - const fetchUser= useCallback(debounce(async () => { - const data = await getUser("123"); - setUser(data) - }, 500), [getUser]); + // const fetchUser= useCallback(debounce(async () => { + // const data = await getUser("123"); + // setUser(data) + // }, 500), [getUser]); - useEffect(() => { - fetchUser() - }, [fetchUser]); + // useEffect(() => { + // fetchUser() + // }, [fetchUser]); const dataContextValue = { user, + setUserData, activeOntology, setOntologyData, searchOrganizationFilters, From ae4f1b19b62ff1bc1cb521f2153c2c9b6dbe08c1 Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Thu, 3 Apr 2025 15:10:52 +0200 Subject: [PATCH 19/24] ILEX-97 connect top bar to user data --- src/components/Header/index.jsx | 36 +++++++++++++++++++++++---------- src/contexts/DataContext.jsx | 17 +--------------- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/components/Header/index.jsx b/src/components/Header/index.jsx index 20adcfbf..f9526929 100644 --- a/src/components/Header/index.jsx +++ b/src/components/Header/index.jsx @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import { AddIcon, DocumentationIcon, @@ -34,6 +33,7 @@ import ListItemButton from '@mui/material/ListItemButton'; import { GlobalDataContext } from "../../contexts/DataContext"; import EditBulkTermsDialog from "../Dashboard/EditBulkTerms/EditBulkTermsDialog"; import ModeEditOutlineOutlinedIcon from "@mui/icons-material/ModeEditOutlineOutlined"; +import PersonOutlineIcon from '@mui/icons-material/PersonOutline'; import { vars } from "../../theme/variables"; const { gray200, white, gray100, gray600 } = vars; @@ -101,6 +101,17 @@ const styles = { keyBoardInfo: { borderRadius: '0.25rem', pointerEvents: 'none', background: gray100, color: gray600, fontSize: '0.875rem', lineHeight: '142.857%', p: '0.125rem 0.5rem' + }, + + avatar: { + border: '0.0469rem solid rgba(0,0,0,0.08)', + width: '2.5rem', + height: '2.5rem', + '& .MuiSvgIcon-root': { + width: '1.5rem', + height: '1.5rem', + fontSize: '1.5rem' + } } } @@ -139,11 +150,12 @@ const UserNavMenu = [ } ] -const Header = ({ isLoggedIn = true }) => { +const Header = () => { const [anchorEl, setAnchorEl] = React.useState(null); const [anchorElUser, setAnchorElUser] = React.useState(null); const [openEditBulkTerms, setOpenEditBulkTerms] = React.useState(false); const [activeStep, setActiveStep] = React.useState(0); + const [isLoggedIn, setIsLoggedIn] = React.useState(false); const { user, setUserData } = useContext(GlobalDataContext); const [openNewTermDialog, setOpenNewTermDialog] = React.useState(false); @@ -230,6 +242,9 @@ const Header = ({ isLoggedIn = true }) => { React.useEffect(() => { console.log("Stored user in context ", user) + if(user) { + setIsLoggedIn(true) + } }, [user]) return ( @@ -297,8 +312,8 @@ const Header = ({ isLoggedIn = true }) => { {!isLoggedIn ? ( - - + +