Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect } from "react";
import { useEffect, useContext } from "react";
import CssBaseline from "@mui/material/CssBaseline";
import { Box, ThemeProvider } from "@mui/material";
import {
Expand Down Expand Up @@ -26,6 +26,9 @@ import SingleOrganization from "./components/SingleOrganization";
import TermActivity from "./components/term_activity/TermActivity";
import OrganizationsCurieEditor from "./components/CurieEditor/OrganizationCurieEditor";
import { handleOrcidLogin } from "./api/endpoints";
import { GlobalDataContext } from "./contexts/DataContext";
import { useCookies } from 'react-cookie'
import { API_CONFIG } from "./config";

const PageContainer = ({ children }) => {
return (
Expand All @@ -36,6 +39,30 @@ const PageContainer = ({ children }) => {
};

function MainContent() {
const [cookies] = useCookies(['session'])
const cookiesInfo = JSON.parse(localStorage.getItem(API_CONFIG.SESSION_DATA.COOKIE));
const { user, setUserData } = useContext(GlobalDataContext);

// check if cookie is expired
if (!user) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be True if user = null.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's expected, in the context initially the userData is null, then set to an emtpy object in case the cookie is expired.

const sessionCookie = cookies.session;
const expires = new Date(cookiesInfo.expires);
const today = new Date();
if (sessionCookie === cookiesInfo?.value && expires > today) {
const userData = JSON.parse(localStorage.getItem(API_CONFIG.SESSION_DATA.SETTINGS));
setUserData({
name: userData['groupname'],
id: userData['orcid'],
email: userData?.emails[0]?.email,
role: userData['own-role'],
groupname: userData['groupname'],
settings: userData
});
} else {
setUserData({});
}
}

return (
<Box
sx={{
Expand Down
14 changes: 13 additions & 1 deletion src/api/endpoints/apiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,15 @@ export interface RegisterRequest {
}

export const login = createPostRequest<any, LoginRequest>(API_CONFIG.REAL_API.SIGNIN, "application/x-www-form-urlencoded")

export const register = createPostRequest<any, RegisterRequest>(API_CONFIG.REAL_API.NEWUSER_ILX, "application/x-www-form-urlencoded")


export const getUserSettings = (group: string) => {
const endpoint = `/${group}${API_CONFIG.REAL_API.USER_SETTINGS}`;
return createGetRequest<any, any>(endpoint, "application/json")();
};

export const createNewOrganization = ({group, data} : {group: string, data: any}) => {
const endpoint = `/${group}${API_CONFIG.REAL_API.CREATE_NEW_ORGANIZATION}`;
return createPostRequest<any, any>(endpoint, "application/json")(data);
Expand All @@ -28,4 +35,9 @@ export const createNewOrganization = ({group, data} : {group: string, data: any}
export const getOrganizations = (group: string) => {
const endpoint = `/${group}${API_CONFIG.REAL_API.GET_ORGANIZATIONS}`;
return createGetRequest<any, any>(endpoint, "application/json")();
};
};

export const userLogout = (group: string) => {
const endpoint = `/${group}${API_CONFIG.REAL_API.LOGOUT}`;
return createGetRequest<any, any>(endpoint, "application/json")();
};
2 changes: 1 addition & 1 deletion src/api/endpoints/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as mockApi from './../../api/endpoints/swaggerMockMissingEndpoints';
import * as api from "./../../api/endpoints/interLexURIStructureAPI";
import { TERM, ONTOLOGY, ORGANIZATION } from '../../model/frontend/types'
import curieParser from '../../parsers/curieParser';
import termParser, { elasticSearhParser, getTerm } from '../../parsers/termParser';
import termParser, { elasticSearchParser, getTerm } from '../../parsers/termParser';
import axios from 'axios';
import { API_CONFIG } from '../../config';

Expand Down
42 changes: 27 additions & 15 deletions src/components/Auth/Login.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { API_CONFIG } from "../../config";
import { GlobalDataContext } from "../../contexts/DataContext";
import * as yup from "yup";
import { useCookies } from 'react-cookie'
import { requestUserSettings } from "./utils";


const schema = yup.object().shape({
Expand All @@ -45,22 +46,32 @@ const Login = () => {
let eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
let eventer = window[eventMethod];
let messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
eventer(messageEvent, function (e) {
eventer(messageEvent, async function (e) {
if (!e.data || !e.data.orcid_meta) return;
// TODO: get the session cookie when here and add it to our domain.
// also store the user info once logged from here in the local storage for future usage.
const { code, orcid_meta, cookies } = e.data;
const _cookies = JSON.parse(cookies);
// create a cookie with the name "session" and the value of the session cookie
const sessionCookie = _cookies.find(cookie => cookie.name === "session");
if (sessionCookie) {
let expires = new Date()
expires.setTime(expires.getTime() + (2 * 24 * 60 * 60 * 1000)); // 2 days
setCookie('session', sessionCookie.value, { path: '/', domain: '.localhost', secure: false, sameSite: false, expires, httpOnly: false });
}

const { code, cookies, groupname } = e.data;
if (code === 200 || code === 302) {
setUserData({ name: orcid_meta.name, id: orcid_meta.orcid });
const _cookies = JSON.parse(cookies);
const sessionCookie = _cookies.find(cookie => cookie.name === "session");
let expires = new Date()
if (sessionCookie) {
expires.setTime(expires.getTime() + (2 * 24 * 60 * 60 * 1000)); // 2 days
setCookie('session', sessionCookie.value, { path: '/', domain: '.localhost', secure: false, sameSite: false, expires, httpOnly: false });
}
const userData = await requestUserSettings(groupname);
localStorage.setItem(API_CONFIG.SESSION_DATA.SETTINGS, JSON.stringify(userData));
localStorage.setItem(API_CONFIG.SESSION_DATA.COOKIE, JSON.stringify({
name: 'session',
value: sessionCookie.value,
expires: expires
}));
setUserData({
name: userData['groupname'],
id: userData['orcid'],
email: userData?.emails[0]?.email,
role: userData['own-role'],
groupname: userData['groupname'],
settings: userData
});
navigate("/")
} else if (code === 401) {
setErrors((prev) => ({
Expand Down Expand Up @@ -95,11 +106,12 @@ const Login = () => {
if (!result.data || !result.data?.orcid_meta) {
setErrors((prev) => ({
...prev,
auth: "Interlex API is not returning the user information, please contact the support at support@interlex.org",
auth: "Interlex API is not returning the user information, reminder to ask Tom to send the groupname back so that we can query the priv/setting endpoint to get the rest of the info required",
}));
} else {
const { code, orcid_meta } = result.data;
if (code === 200 || code === 302) {
// TODO: the backend should return the groupname, for now is just returning a message.
setUserData({ name: orcid_meta.name, id: orcid_meta.orcid });
}
navigate("/")
Expand Down
18 changes: 18 additions & 0 deletions src/components/Auth/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { API_CONFIG } from "../../config";
import { getUser } from "../../api/endpoints";
import { getUserSettings } from "../../api/endpoints/apiService";

export const requestUserSettings = async (group: string) => {
try {
const response = await getUserSettings(group);
if (response.status === 200) {
const data = response.settings;
return data;
} else {
throw new Error(`Error fetching user settings: ${response.statusText}`);
}
} catch (error) {
console.error("Error fetching user settings:", error);
throw error;
}
};
6 changes: 3 additions & 3 deletions src/components/Dashboard/Organizations/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import AddOutlinedIcon from "@mui/icons-material/AddOutlined";
import OrganizationsList from "../../common/OrganizationsList";
import CustomViewButton from "../../common/CustomViewButton";
import MessageDialog from "../../common/MessageDialog";
import { GlobalDataContext } from "../../../contexts/DataContext";

const { gray600, gray200 } = vars;


const Organizations = () => {
const [open, setOpen] = useState(false);

// TODO: change this to be dynamic when we get the response from api call
const groupname = "aigul"
const { user } = GlobalDataContext();
const groupname = user?.groupname || "base";

const {
listView,
Expand Down
112 changes: 62 additions & 50 deletions src/components/GraphViewer/Graph.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import * as d3 from "d3";
import PropTypes from "prop-types";
import { Box } from "@mui/material";
import { useMemo, useEffect } from "react";
import { getGraphStructure , OBJECT, SUBJECT, PREDICATE, ROOT} from "./GraphStructure";
import { getGraphStructure, PREDICATE, ROOT} from "./GraphStructure";
import { vars } from "../../theme/variables";
const { gray600, white } = vars;

const MARGIN = { top: 60, right: 60, bottom: 60, left: 60 };

Expand Down Expand Up @@ -59,67 +61,72 @@ const Graph = ({ width, height, predicate }) => {

const allNodes = dendrogram.descendants().map((node) => {
let textOffset = 0;
if ( node.data.type === OBJECT ) {
textOffset = 40;
} else if ( node.data.type === SUBJECT ) {
textOffset = 40;
} else if ( node.data.type === PREDICATE ) {
textOffset = 40;
if (node.data.type === PREDICATE || node.data.type === ROOT) {
textOffset = -40;
} else {
textOffset = 5;
}

const truncatedName = node.data.name.length > 25
? `${node.data.name.substring(0, 25)}...`
: node.data.name;

return (
<g key={node.id} >
{(
<text
x={(boundsWidth - (node.y) ) - textOffset }
y={node.x - (node.data.type === ROOT ? 0 : 10)}
id={node.data.name}
className="node--leaf-g"
fontSize={12}
textAnchor="left"
alignmentBaseline="middle"
fill="black"
target="_blank"
href={node.data.name}
>
{truncatedName}
</text>
)}
<g key={node.id}>
<text
x={node.y + textOffset} // Flip the x-coordinate
y={node.x - ((node.data.type === ROOT || node.data.type === PREDICATE) ? 10 : 0)}
id={node.data.name}
className="node--leaf-g"
fontSize={12}
textAnchor="start" // Align text to the start
alignmentBaseline="middle"
fill="black"
target="_blank"
href={node.data.name}
>
{truncatedName}
</text>
</g>
);
});

const allEdges = dendrogram.descendants().map((node, index) => {
if (!node.parent) {
return;
// Add a black circle at the root
return (
<circle
key={`root-circle-${index}`}
cx={node.y}
cy={node.x}
r={5} // Circle radius
fill="grey"
/>
);
}

const line = d3
.line()
.x(d => d[0])
.y(d => d[1])
.curve(d3.curveBundle.beta(.75));

const start = [boundsWidth - node.parent.y, node.parent.x]
const end = [boundsWidth - node.y, node.x]
.curve(d3.curveBundle.beta(0.75));
const start = [node.parent.y, node.parent.x];
const end = [node.y, node.x];
const radius = 5;

const points = [
start,
[start[0] + radius, end[1]],
end
[start[0] - radius, end[1]],
end,
];

return (
<path
key={`${node.id}-${index}`}
fill="none"
stroke="grey"
markerStart='url(#head)'
markerEnd="url(#arrowhead)" // Add arrowhead at the end
d={line(points)}
/>
);
Expand All @@ -132,22 +139,27 @@ const Graph = ({ width, height, predicate }) => {
pointerEvents: "none",
opacity: 0,
zIndex: 1000,
background: gray600,
color: white,
padding: "0.5rem",
borderRadius: "0.5rem",
}}>
</Box>
<svg width={width} height={height} >
<defs>
<marker
id="head"
viewBox="0 0 10 10"
refX="10"
refY="5"
fill="grey"
markerWidth="10"
markerHeight="10"
orient="auto-start">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
</defs>
<defs>
<marker
id="arrowhead"
viewBox="0 0 10 10"
refX="10"
refY="5"
fill="grey"
markerWidth="10"
markerHeight="10"
orient="auto"
>
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
</defs>
<g
width={boundsWidth}
height={boundsHeight}
Expand Down
10 changes: 6 additions & 4 deletions src/components/Header/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ 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 { userLogout } from "../../api/endpoints/apiService";

import { vars } from "../../theme/variables";
const { gray200, white, gray100, gray600 } = vars;
Expand Down Expand Up @@ -220,9 +221,10 @@ const Header = () => {

const handleMenuClick = (e, menu) => {
if (menu.label === 'Log out') {
// TODO: call logout endpoint {group}/priv/logout also
// TODO: flush the userinfo from the localstorage
setUserData(null, null);
userLogout();
localStorage.removeItem('session');
localStorage.removeItem('settings');
setUserData({});
navigate('/');
}
if (menu.href) {
Expand Down Expand Up @@ -250,7 +252,7 @@ const Header = () => {

React.useEffect(() => {
console.log("Stored user in context ", user)
if(user) {
if(user !== null && user?.groupname !== undefined) {
setIsLoggedIn(true)
} else {
setIsLoggedIn(false)
Expand Down
Loading