From dd4d0474c00177397571d64b980fbfd8c95fbaea Mon Sep 17 00:00:00 2001 From: Morgan Higby-Flowers Date: Fri, 22 Nov 2024 08:55:25 -0600 Subject: [PATCH 01/14] session Manager --- src/components/Auth/Login.jsx | 6 +- src/components/Auth/Logout.jsx | 7 +++ src/components/Manager/SessionsManager.jsx | 67 ++++++++++++++++++++++ 3 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 src/components/Manager/SessionsManager.jsx diff --git a/src/components/Auth/Login.jsx b/src/components/Auth/Login.jsx index 99e230a..c3d0bd9 100644 --- a/src/components/Auth/Login.jsx +++ b/src/components/Auth/Login.jsx @@ -3,9 +3,10 @@ import { jwtDecode } from 'jwt-decode'; import { useContext, useEffect } from 'react'; import { Logout } from './Logout'; import { myContext } from '../../App'; +import { startSession } from '../Manager/SessionsManager'; export const Login = () => { - const { user, setUser } = useContext(myContext); + const { user, setUser, vocabUrl } = useContext(myContext); useEffect(() => { const storedUser = localStorage.getItem('user'); @@ -30,6 +31,9 @@ export const Login = () => { 'user', JSON.stringify(credentialResponseDecoded) ); + startSession(vocabUrl,credentialResponseDecoded.email); + + }} onError={() => { console.log('Login Failed'); diff --git a/src/components/Auth/Logout.jsx b/src/components/Auth/Logout.jsx index 3aa8d16..7334c38 100644 --- a/src/components/Auth/Logout.jsx +++ b/src/components/Auth/Logout.jsx @@ -2,12 +2,19 @@ import { googleLogout } from '@react-oauth/google'; import './Auth.scss'; import { LogoutOutlined } from '@ant-design/icons'; import { Tooltip } from 'antd'; +import { endSession } from '../Manager/SessionsManager'; +import { useContext } from 'react'; +import { myContext } from '../../App'; + + export const Logout = ({ user, setUser }) => { + const { vocabUrl } = useContext(myContext); const logOut = () => { googleLogout(); setUser(null); localStorage.removeItem('user'); + endSession(vocabUrl); }; return ( diff --git a/src/components/Manager/SessionsManager.jsx b/src/components/Manager/SessionsManager.jsx new file mode 100644 index 0000000..a82f33a --- /dev/null +++ b/src/components/Manager/SessionsManager.jsx @@ -0,0 +1,67 @@ +export const startSession = (vocabUrl, email) => { + const body = { + "user_id": email, + "affiliation": "affiliation" + } + return fetch(`${vocabUrl}/session/start`, { + method: 'POST', + body: JSON.stringify(body), + headers: { + 'Content-Type': 'application/json', + }, + }).then(async res => { + if (res.ok) { + const data = await res.json(); + console.log(data, 'Session Successful'); + return res.json(); + } else if (res.status === 401) { + navigate('/login'); + } else { + return res.json().then(error => { + throw new Error(error); + }); + } + }); +}; + +export const endSession = (vocabUrl) => { + return fetch(`${vocabUrl}/session/terminate`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }).then(async res => { + if (res.ok) { + const data = await res.json(); + console.log(data, 'Session Ended'); + return res.json(); + } else { + return res.json().then(error => { + throw new Error(error); + }); + } + }); + +} + +export const getSessionStatus = (vocabUrl) => { + return fetch(`${vocabUrl}/session/terminate`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }).then(async res => { + if (res.ok) { + const data = await res.json(); + console.log(data, 'Session Info'); + return res.json(); + } else { + return res.json().then(error => { + throw new Error(error); + }); + } + }); + +} + + From f737abc8d48966889d910289daf40575417f621e Mon Sep 17 00:00:00 2001 From: Yelena Cox Date: Thu, 5 Dec 2024 15:31:21 -0600 Subject: [PATCH 02/14] removed editor from bodies --- .env | 6 +- src/AppRouter.jsx | 88 +++++++++++++++---- src/components/Auth/Login.jsx | 26 +++--- src/components/Auth/Logout.jsx | 6 +- src/components/Manager/FetchManager.jsx | 2 +- .../MappingsFunctions/AssignMappings.jsx | 1 - .../MappingsFunctions/ClearMappings.jsx | 2 +- .../Manager/MappingsFunctions/FilterReset.jsx | 5 +- .../MappingsFunctions/FilterSelect.jsx | 1 - .../MappingsFunctions/GetMappingsModal.jsx | 25 ++++-- .../Projects/Tables/AddVariable.jsx | 27 +++++- .../Projects/Tables/DeleteTable.jsx | 2 +- .../Tables/EditMappingsTableModal.jsx | 3 +- .../Projects/Tables/EditTableDetails.jsx | 2 +- .../Projects/Tables/EditVariable.jsx | 16 +++- .../Projects/Tables/LoadVariables.jsx | 2 +- .../Projects/Tables/ResetTableMappings.jsx | 2 +- .../Projects/Tables/TableDetails.jsx | 2 +- src/components/Projects/Tables/TableMenu.jsx | 2 +- .../Projects/Tables/UploadTable.jsx | 2 +- .../Projects/Terminologies/AddCode.jsx | 3 +- .../Terminologies/AssignMappingsViaButton.jsx | 2 +- .../Terminologies/DeleteTerminology.jsx | 1 - .../Projects/Terminologies/EditCode.jsx | 2 +- .../Terminologies/EditMappingModal.jsx | 4 +- .../Terminologies/PreferredTerminology.jsx | 2 +- .../Projects/Terminologies/ResetMappings.jsx | 2 +- .../Projects/Terminologies/Terminology.jsx | 6 +- .../Terminologies/TerminologyMenu.jsx | 2 +- 29 files changed, 167 insertions(+), 79 deletions(-) diff --git a/.env b/.env index 85f0aac..637e35e 100644 --- a/.env +++ b/.env @@ -1,7 +1,7 @@ VITE_SEARCH_ENDPOINT = https://www.ebi.ac.uk/ols4/api/search? VITE_MONARCH_SEARCH = https://api-v3.monarchinitiative.org/v3/api/search? # VITE_VOCAB_ENDPOINT = http://127.0.0.1:5000/api # local -VITE_VOCAB_ENDPOINT = https://locutus-110109177269.us-central1.run.app/api # dev -# VITE_VOCAB_ENDPOINT = https://locutus-1066621297011.us-central1.run.app/api # uat -VITE_CLIENT_ID = 907694787484-36jr4oaf04mmniik6482batc87ejemm8.apps.googleusercontent.com +VITE_VOCAB_ENDPOINT = https://locutus-dev-110109177269.us-central1.run.app/api # dev +# VITE_VOCAB_ENDPOINT = https://locutus-uat-1066621297011.us-central1.run.app/api # uat +VITE_CLIENT_ID = 159970607373-mvkv4lfl4ovpul5bssatuf1re4ucvg8n.apps.googleusercontent.com VITE_MAPDRAGON_VERSION = "Development" \ No newline at end of file diff --git a/src/AppRouter.jsx b/src/AppRouter.jsx index 1bd9ef6..6bfdfe7 100644 --- a/src/AppRouter.jsx +++ b/src/AppRouter.jsx @@ -29,17 +29,16 @@ import { SearchContextRoot } from './Contexts/SearchContext.jsx'; import { About } from './components/About/About.jsx'; export const AppRouter = () => { - const {user} = useContext(myContext); + const { user } = useContext(myContext); const isLoggedIn = () => { - const storedUser = localStorage.getItem('user'); - - if (storedUser) { + if (user) { return true; } else { return false; } - } + }; + return ( @@ -64,47 +63,102 @@ export const AppRouter = () => { }> } /> } /> - + } /> } /> - : } /> - }/> + : + } + /> + } /> } /> }> }> - : } /> + : + } + /> } /> - : } /> + : + } + /> - : } /> + + ) : ( + + ) + } + /> : } + element={ + isLoggedIn() ? : + } /> - : } /> + + ) : ( + + ) + } + /> : } + element={ + isLoggedIn() ? ( + + ) : ( + + ) + } /> : } + element={ + isLoggedIn() ? ( + + ) : ( + + ) + } /> : } + element={ + isLoggedIn() ? ( + + ) : ( + + ) + } /> : } + element={ + isLoggedIn() ? : + } /> diff --git a/src/components/Auth/Login.jsx b/src/components/Auth/Login.jsx index c3d0bd9..c3c9b08 100644 --- a/src/components/Auth/Login.jsx +++ b/src/components/Auth/Login.jsx @@ -8,12 +8,12 @@ import { startSession } from '../Manager/SessionsManager'; export const Login = () => { const { user, setUser, vocabUrl } = useContext(myContext); - useEffect(() => { - const storedUser = localStorage.getItem('user'); - if (storedUser) { - setUser(JSON.parse(storedUser)); - } - }, []); + // useEffect(() => { + // const storedUser = localStorage.getItem('user'); + // if (storedUser) { + // setUser(JSON.parse(storedUser)); + // } + // }, []); // If there is a user, it displays the Logout function with user information. Otherwise, it displays the login button return user ? ( @@ -27,13 +27,11 @@ export const Login = () => { credentialResponse.credential ); setUser(credentialResponseDecoded); - localStorage.setItem( - 'user', - JSON.stringify(credentialResponseDecoded) - ); - startSession(vocabUrl,credentialResponseDecoded.email); - - + // localStorage.setItem( + // 'user', + // JSON.stringify(credentialResponseDecoded) + // ); + startSession(vocabUrl, credentialResponseDecoded.email); }} onError={() => { console.log('Login Failed'); @@ -41,4 +39,4 @@ export const Login = () => { /> ); -}; \ No newline at end of file +}; diff --git a/src/components/Auth/Logout.jsx b/src/components/Auth/Logout.jsx index 7334c38..03a48d7 100644 --- a/src/components/Auth/Logout.jsx +++ b/src/components/Auth/Logout.jsx @@ -6,14 +6,12 @@ import { endSession } from '../Manager/SessionsManager'; import { useContext } from 'react'; import { myContext } from '../../App'; - - export const Logout = ({ user, setUser }) => { const { vocabUrl } = useContext(myContext); const logOut = () => { googleLogout(); setUser(null); - localStorage.removeItem('user'); + // localStorage.removeItem('user'); endSession(vocabUrl); }; @@ -33,4 +31,4 @@ export const Logout = ({ user, setUser }) => { ); -}; \ No newline at end of file +}; diff --git a/src/components/Manager/FetchManager.jsx b/src/components/Manager/FetchManager.jsx index 374d1cd..e358162 100644 --- a/src/components/Manager/FetchManager.jsx +++ b/src/components/Manager/FetchManager.jsx @@ -52,7 +52,7 @@ export const handleDelete = (evt, vocabUrl, name, component, user) => { options.headers = { 'Content-Type': 'application/json', }; - options.body = JSON.stringify({ editor: user.email }); + // options.body = JSON.stringify({ editor: user.email }); } return fetch(`${vocabUrl}/${name}/${component.id}`, options) .then(response => { diff --git a/src/components/Manager/MappingsFunctions/AssignMappings.jsx b/src/components/Manager/MappingsFunctions/AssignMappings.jsx index fc321f3..43ffa63 100644 --- a/src/components/Manager/MappingsFunctions/AssignMappings.jsx +++ b/src/components/Manager/MappingsFunctions/AssignMappings.jsx @@ -67,7 +67,6 @@ export const AssignMappings = ({ })); const mappingsDTO = { mappings: selectedMappings, - editor: user.email, }; fetch(`${vocabUrl}/Terminology/${terminology.id}/mapping/${mappingProp}`, { diff --git a/src/components/Manager/MappingsFunctions/ClearMappings.jsx b/src/components/Manager/MappingsFunctions/ClearMappings.jsx index bf2506b..c5c2cfe 100644 --- a/src/components/Manager/MappingsFunctions/ClearMappings.jsx +++ b/src/components/Manager/MappingsFunctions/ClearMappings.jsx @@ -21,7 +21,7 @@ export const ClearMappings = ({ propId, component }) => { headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ editor: user.email }), + // body: JSON.stringify({ editor: user.email }), }) .then(res => { if (res.ok) { diff --git a/src/components/Manager/MappingsFunctions/FilterReset.jsx b/src/components/Manager/MappingsFunctions/FilterReset.jsx index 8efc0f4..69aca0f 100644 --- a/src/components/Manager/MappingsFunctions/FilterReset.jsx +++ b/src/components/Manager/MappingsFunctions/FilterReset.jsx @@ -20,14 +20,13 @@ export const FilterReset = ({ table, terminology }) => { `${vocabUrl}/${ table ? `Table/${table.id}/filter/self` - : `Terminology/${terminology.id}/filter}` + : `Terminology/${terminology.id}/filter` }`, { method: 'DELETE', headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ editor: user.email }), } ) .then(res => { @@ -48,7 +47,7 @@ export const FilterReset = ({ table, terminology }) => { `${vocabUrl}/${ table ? `Table/${table.id}/filter/self` - : `Terminology/${terminology.id}/filter}` + : `Terminology/${terminology.id}/filter` }`, { method: 'GET', diff --git a/src/components/Manager/MappingsFunctions/FilterSelect.jsx b/src/components/Manager/MappingsFunctions/FilterSelect.jsx index 5de5553..abe785f 100644 --- a/src/components/Manager/MappingsFunctions/FilterSelect.jsx +++ b/src/components/Manager/MappingsFunctions/FilterSelect.jsx @@ -124,7 +124,6 @@ export const FilterSelect = ({ component, table, terminology }) => { const apiPreferenceDTO = { api_preference: apiPreference?.api_preference, - editor: user.email, }; const method = diff --git a/src/components/Manager/MappingsFunctions/GetMappingsModal.jsx b/src/components/Manager/MappingsFunctions/GetMappingsModal.jsx index f71d663..4fa4c7b 100644 --- a/src/components/Manager/MappingsFunctions/GetMappingsModal.jsx +++ b/src/components/Manager/MappingsFunctions/GetMappingsModal.jsx @@ -137,11 +137,7 @@ export const GetMappingsModal = ({ const onClose = () => { setPage(0); - setResults([]); - setSelectedMappings([]); - setDisplaySelectedMappings([]); setApiPreferencesCode(undefined); - setSelectedBoxes([]); setSelectedKey(null); setPrefTerminologies([]); }; @@ -164,7 +160,7 @@ export const GetMappingsModal = ({ })); const mappingsDTO = { mappings: selectedMappings, - editor: user.email, + // editor: user.email, }; setLoading(true); fetch( @@ -186,9 +182,22 @@ export const GetMappingsModal = ({ }) .then(data => { setMapping(data.codes); - form.resetFields(); setGetMappings(null); message.success('Changes saved successfully.'); + form.resetFields(); + setResults([]); + setSelectedMappings([]); + setDisplaySelectedMappings([]); + setSelectedBoxes([]); + }) + .catch(error => { + if (error) { + notification.error({ + message: 'Error', + description: 'An error occurred saving the mapping.', + }); + } + return error; }) .finally(() => setLoading(false)); table @@ -406,6 +415,10 @@ export const GetMappingsModal = ({ onClose(); form.resetFields(); setGetMappings(null); + setResults([]); + setSelectedMappings([]); + setDisplaySelectedMappings([]); + setSelectedBoxes([]); }} maskClosable={false} destroyOnClose={true} diff --git a/src/components/Projects/Tables/AddVariable.jsx b/src/components/Projects/Tables/AddVariable.jsx index 8b22e62..5881f97 100644 --- a/src/components/Projects/Tables/AddVariable.jsx +++ b/src/components/Projects/Tables/AddVariable.jsx @@ -1,6 +1,15 @@ import { useContext, useState } from 'react'; import { myContext } from '../../../App'; -import { Button, Form, Input, message, Modal, Select, Space } from 'antd'; +import { + Button, + Form, + Input, + message, + Modal, + notification, + Select, + Space, +} from 'antd'; import DataTypeSubForm from './DataTypeSubForm'; import { ModalSpinner } from '../../Manager/Spinner'; import { RequiredLogin } from '../../Auth/RequiredLogin'; @@ -27,7 +36,8 @@ export const AddVariable = ({ table, setTable }) => { headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ ...values, editor: user.email }), + body: JSON.stringify(values), + // body: JSON.stringify({ ...values, editor: user.email }), }) .then(res => { if (res.ok) { @@ -40,9 +50,18 @@ export const AddVariable = ({ table, setTable }) => { setTable(data); form.resetFields(); setAddRow(false); + message.success('Variable added successfully.'); }) - // Displays a self-closing message that the udpates have been successfully saved. - .then(() => message.success('Variable added successfully.')) + .catch(error => { + if (error) { + notification.error({ + message: 'Error', + description: 'An error occurred saving the variable.', + }); + } + return error; + }) + .finally(() => setLoading(false)); }; diff --git a/src/components/Projects/Tables/DeleteTable.jsx b/src/components/Projects/Tables/DeleteTable.jsx index 67b315d..2888e8b 100644 --- a/src/components/Projects/Tables/DeleteTable.jsx +++ b/src/components/Projects/Tables/DeleteTable.jsx @@ -18,7 +18,7 @@ export const DeleteTable = ({ DDId, studyId }) => { headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ editor: user.email }), + // body: JSON.stringify({ editor: user.email }), }) .then(res => { if (res.ok) { diff --git a/src/components/Projects/Tables/EditMappingsTableModal.jsx b/src/components/Projects/Tables/EditMappingsTableModal.jsx index 4000478..fcf8286 100644 --- a/src/components/Projects/Tables/EditMappingsTableModal.jsx +++ b/src/components/Projects/Tables/EditMappingsTableModal.jsx @@ -166,7 +166,7 @@ export const EditMappingsTableModal = ({ setLoading(true); const mappingsDTO = { mappings: values?.mappings?.map(v => JSON.parse(v)) ?? [], - editor: user.email, + // editor: user.email, }; fetch(`${vocabUrl}/Table/${tableId}/mapping/${editMappings.code}`, { method: 'PUT', @@ -217,7 +217,6 @@ export const EditMappingsTableModal = ({ ...(values.existing_mappings?.map(v => JSON.parse(v)) ?? []), ...(selectedMappings ?? []), ], - editor: user.email, }; fetch(`${vocabUrl}/Table/${tableId}/mapping/${editMappings.code}`, { diff --git a/src/components/Projects/Tables/EditTableDetails.jsx b/src/components/Projects/Tables/EditTableDetails.jsx index cae5c4c..5bfbf1d 100644 --- a/src/components/Projects/Tables/EditTableDetails.jsx +++ b/src/components/Projects/Tables/EditTableDetails.jsx @@ -26,7 +26,7 @@ export const EditTableDetails = ({ table, setTable, edit, setEdit }) => { ...values, filename: table.filename, variables: table?.variables, - editor: user.email, + // editor: user.email, }) .then(data => { setTable(data); diff --git a/src/components/Projects/Tables/EditVariable.jsx b/src/components/Projects/Tables/EditVariable.jsx index a87daa7..956f1d8 100644 --- a/src/components/Projects/Tables/EditVariable.jsx +++ b/src/components/Projects/Tables/EditVariable.jsx @@ -74,7 +74,6 @@ export const EditVariable = ({ if (!table.variables.some(item => item?.name === values?.name)) { handlePatch(vocabUrl, 'Table', table, { ...updatedName, - editor: user.email, }) .catch(error => { if (error) { @@ -91,7 +90,8 @@ export const EditVariable = ({ headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ ...values, editor: user.email }), + body: JSON.stringify(values), + // body: JSON.stringify({ ...values, editor: user.email }), }) .then(res => { if (res.ok) { @@ -129,7 +129,7 @@ export const EditVariable = ({ headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ ...values, editor: user.email }), + // body: JSON.stringify({ ...values, editor: user.email }), }) .then(res => { if (res.ok) { @@ -144,6 +144,16 @@ export const EditVariable = ({ setEditRow(''); message.success('Changes saved successfully.'); }) + .catch(error => { + if (error) { + notification.error({ + message: 'Error', + description: 'An error occurred editing the variable.', + }); + } + return error; + }) + .finally(() => setLoading(false)) .then(() => getById(vocabUrl, 'Table', `${tableId}/mapping`) diff --git a/src/components/Projects/Tables/LoadVariables.jsx b/src/components/Projects/Tables/LoadVariables.jsx index feff7ce..b0a5306 100644 --- a/src/components/Projects/Tables/LoadVariables.jsx +++ b/src/components/Projects/Tables/LoadVariables.jsx @@ -19,7 +19,7 @@ export const LoadVariables = ({ load, setLoad }) => { headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ ...values, editor: user.email }), + body: JSON.stringify(values), }) .then(res => { if (res.status === 400) { diff --git a/src/components/Projects/Tables/ResetTableMappings.jsx b/src/components/Projects/Tables/ResetTableMappings.jsx index ad63bd8..5ccbcee 100644 --- a/src/components/Projects/Tables/ResetTableMappings.jsx +++ b/src/components/Projects/Tables/ResetTableMappings.jsx @@ -18,7 +18,7 @@ export const ResetTableMappings = ({ tableId, editMappings, setReset }) => { headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ editor: user.email }), + // body: JSON.stringify({ editor: user.email }), }) .then(response => { if (response.ok) { diff --git a/src/components/Projects/Tables/TableDetails.jsx b/src/components/Projects/Tables/TableDetails.jsx index e722a53..83f1201 100644 --- a/src/components/Projects/Tables/TableDetails.jsx +++ b/src/components/Projects/Tables/TableDetails.jsx @@ -76,7 +76,7 @@ export const TableDetails = () => { // setLoading(true); const mappingsDTO = { mappings: mapArr, - editor: user.email, + // editor: user.email, }; fetch(`${vocabUrl}/Table/${tableId}/mapping/${mappingCode}`, { diff --git a/src/components/Projects/Tables/TableMenu.jsx b/src/components/Projects/Tables/TableMenu.jsx index 83c261a..9832646 100644 --- a/src/components/Projects/Tables/TableMenu.jsx +++ b/src/components/Projects/Tables/TableMenu.jsx @@ -59,7 +59,7 @@ export const TableMenu = ({ headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ editor: user.email }), + // body: JSON.stringify({ editor: user.email }), }) .then(res => { if (res.ok) { diff --git a/src/components/Projects/Tables/UploadTable.jsx b/src/components/Projects/Tables/UploadTable.jsx index 850976e..2d5cd75 100644 --- a/src/components/Projects/Tables/UploadTable.jsx +++ b/src/components/Projects/Tables/UploadTable.jsx @@ -37,7 +37,7 @@ export const UploadTable = ({ addTable, setAddTable }) => { headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ ...values, editor: user.email }), + body: JSON.stringify(values), }) .then(res => { if (res.status === 400) { diff --git a/src/components/Projects/Terminologies/AddCode.jsx b/src/components/Projects/Terminologies/AddCode.jsx index 56f3701..cb733bc 100644 --- a/src/components/Projects/Terminologies/AddCode.jsx +++ b/src/components/Projects/Terminologies/AddCode.jsx @@ -23,7 +23,8 @@ export const AddCode = ({ terminology, setTerminology }) => { headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ ...values, editor: user.email }), + body: JSON.stringify(values), + // EDITOR NOT SHOWING UP IN PROVENANCE body: JSON.stringify({ ...values, editor: user.email }), }) .then(res => { if (res.ok) { diff --git a/src/components/Projects/Terminologies/AssignMappingsViaButton.jsx b/src/components/Projects/Terminologies/AssignMappingsViaButton.jsx index d475350..824cdda 100644 --- a/src/components/Projects/Terminologies/AssignMappingsViaButton.jsx +++ b/src/components/Projects/Terminologies/AssignMappingsViaButton.jsx @@ -61,7 +61,7 @@ export const AssignMappingsViaButton = ({ })); const mappingsDTO = { mappings: selectedMappings, - editor: user.email, + // editor: user.email, }; fetch( diff --git a/src/components/Projects/Terminologies/DeleteTerminology.jsx b/src/components/Projects/Terminologies/DeleteTerminology.jsx index ba01443..4a791ba 100644 --- a/src/components/Projects/Terminologies/DeleteTerminology.jsx +++ b/src/components/Projects/Terminologies/DeleteTerminology.jsx @@ -17,7 +17,6 @@ export const DeleteTerminology = ({ setTerms, deleteId, setDeleteId }) => { headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ editor: user.email }), }) .then(res => { if (res.ok) { diff --git a/src/components/Projects/Terminologies/EditCode.jsx b/src/components/Projects/Terminologies/EditCode.jsx index 466c1c5..9302d62 100644 --- a/src/components/Projects/Terminologies/EditCode.jsx +++ b/src/components/Projects/Terminologies/EditCode.jsx @@ -76,7 +76,7 @@ export const EditCode = ({ handlePatch(vocabUrl, 'Terminology', terminology, { ...updatedRowDTO, - editor: user.email, + // editor: user.email, }) .then(data => { setTerminology(data); diff --git a/src/components/Projects/Terminologies/EditMappingModal.jsx b/src/components/Projects/Terminologies/EditMappingModal.jsx index 8c1d63c..df2b005 100644 --- a/src/components/Projects/Terminologies/EditMappingModal.jsx +++ b/src/components/Projects/Terminologies/EditMappingModal.jsx @@ -165,7 +165,7 @@ export const EditMappingsModal = ({ setLoading(true); const mappingsDTO = { mappings: values?.mappings?.map(v => JSON.parse(v)) ?? [], - editor: user.email, + // editor: user.email, }; fetch( `${vocabUrl}/Terminology/${terminologyId}/mapping/${editMappings.code}`, @@ -219,7 +219,7 @@ export const EditMappingsModal = ({ ...(values.existing_mappings?.map(v => JSON.parse(v)) ?? []), ...(selectedMappings ?? []), ], - editor: user.email, + // editor: user.email, }; fetch( diff --git a/src/components/Projects/Terminologies/PreferredTerminology.jsx b/src/components/Projects/Terminologies/PreferredTerminology.jsx index ef66b10..89f97c4 100644 --- a/src/components/Projects/Terminologies/PreferredTerminology.jsx +++ b/src/components/Projects/Terminologies/PreferredTerminology.jsx @@ -66,7 +66,7 @@ export const PreferredTerminology = ({ terminology, setTerminology }) => { const preferredTermDTO = () => { return { - 'editor': user.email, + // 'editor': user.email, 'preferred_terminologies': preferredTerminologies, }; }; diff --git a/src/components/Projects/Terminologies/ResetMappings.jsx b/src/components/Projects/Terminologies/ResetMappings.jsx index 86dc52b..e728bf2 100644 --- a/src/components/Projects/Terminologies/ResetMappings.jsx +++ b/src/components/Projects/Terminologies/ResetMappings.jsx @@ -20,7 +20,7 @@ export const ResetMappings = ({ terminologyId, editMappings, setReset }) => { headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ editor: user.email }), + // body: JSON.stringify({ editor: user.email }), } ) .then(response => { diff --git a/src/components/Projects/Terminologies/Terminology.jsx b/src/components/Projects/Terminologies/Terminology.jsx index 6c7699b..a61ca31 100644 --- a/src/components/Projects/Terminologies/Terminology.jsx +++ b/src/components/Projects/Terminologies/Terminology.jsx @@ -74,10 +74,10 @@ export const Terminology = () => { const navigate = useNavigate(); const updateMappings = (mapArr, mappingCode) => { - // setLoading(true); + setLoading(true); const mappingsDTO = { mappings: mapArr, - editor: user.email, + // editor: user.email, }; fetch(`${vocabUrl}/Terminology/${terminologyId}/mapping/${mappingCode}`, { @@ -97,8 +97,8 @@ export const Terminology = () => { .then(data => { setMapping(data.codes); setEditMappings(null); - form.resetFields(); message.success('Mapping removed.'); + form.resetFields(); }) .catch(error => { console.log(error, 'error'); diff --git a/src/components/Projects/Terminologies/TerminologyMenu.jsx b/src/components/Projects/Terminologies/TerminologyMenu.jsx index 54b5ed5..ac013dc 100644 --- a/src/components/Projects/Terminologies/TerminologyMenu.jsx +++ b/src/components/Projects/Terminologies/TerminologyMenu.jsx @@ -72,7 +72,7 @@ export const TerminologyMenu = ({ headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ editor: user.email }), + // body: JSON.stringify({ editor: user.email }), }) .then(res => { if (res.ok) { From f278a912cdcb9aef3089fb501f226171ff714b6f Mon Sep 17 00:00:00 2001 From: Yelena Cox Date: Mon, 9 Dec 2024 09:35:04 -0600 Subject: [PATCH 03/14] committing to get stashed changes --- src/components/Auth/Login.jsx | 20 ++++++++++---------- src/components/Auth/Logout.jsx | 4 +--- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/components/Auth/Login.jsx b/src/components/Auth/Login.jsx index c3c9b08..d3e29fb 100644 --- a/src/components/Auth/Login.jsx +++ b/src/components/Auth/Login.jsx @@ -8,12 +8,12 @@ import { startSession } from '../Manager/SessionsManager'; export const Login = () => { const { user, setUser, vocabUrl } = useContext(myContext); - // useEffect(() => { - // const storedUser = localStorage.getItem('user'); - // if (storedUser) { - // setUser(JSON.parse(storedUser)); - // } - // }, []); + useEffect(() => { + const storedUser = localStorage.getItem('user'); + if (storedUser) { + setUser(JSON.parse(storedUser)); + } + }, []); // If there is a user, it displays the Logout function with user information. Otherwise, it displays the login button return user ? ( @@ -27,10 +27,10 @@ export const Login = () => { credentialResponse.credential ); setUser(credentialResponseDecoded); - // localStorage.setItem( - // 'user', - // JSON.stringify(credentialResponseDecoded) - // ); + localStorage.setItem( + 'user', + JSON.stringify(credentialResponseDecoded) + ); startSession(vocabUrl, credentialResponseDecoded.email); }} onError={() => { diff --git a/src/components/Auth/Logout.jsx b/src/components/Auth/Logout.jsx index 03a48d7..e8410a6 100644 --- a/src/components/Auth/Logout.jsx +++ b/src/components/Auth/Logout.jsx @@ -7,12 +7,10 @@ import { useContext } from 'react'; import { myContext } from '../../App'; export const Logout = ({ user, setUser }) => { - const { vocabUrl } = useContext(myContext); const logOut = () => { googleLogout(); setUser(null); - // localStorage.removeItem('user'); - endSession(vocabUrl); + localStorage.removeItem('user'); }; return ( From 6972ee9fd24ab03f51a292825605f5e7f089cf5e Mon Sep 17 00:00:00 2001 From: Yelena Cox Date: Mon, 9 Dec 2024 10:35:48 -0600 Subject: [PATCH 04/14] Console log for getStatusSession, commented out editor property in bodies --- .github/workflows/dispatch_deploy.yml | 18 ++- docs/.nojekyll | 0 docs/README.md | 45 +++++++ docs/_navbar.md | 4 + docs/datadictionary.md | 31 +++++ docs/index.html | 23 ++++ package.json | 1 + src/App.jsx | 4 + src/AppRouter.jsx | 7 +- src/Contexts/MappingContext.jsx | 9 ++ src/components/Auth/Login.jsx | 1 + .../MappingsFunctions/AssignMappings.jsx | 5 +- .../MappingsFunctions/ClearMappings.jsx | 6 +- .../MappingsFunctions/DisplaySelected.jsx | 6 +- .../MappingsFunctions/EditMappingsLabel.jsx | 68 ++++++++++ .../MappingsFunctions/GetMappingsModal.jsx | 15 ++- .../MappingsFunctions/MappingRelationship.jsx | 40 ++++++ .../MappingsFunctions/MappingReset.jsx | 4 + .../MappingsFunctions/MappingSearch.jsx | 7 + src/components/Manager/SessionsManager.jsx | 120 +++++++++--------- src/components/Manager/ShowHistory.jsx | 31 ++--- .../Projects/Tables/EditDataTypeSubForm.jsx | 2 +- .../Tables/EditMappingsTableModal.jsx | 91 ++++++------- .../Projects/Tables/TableDetails.jsx | 13 +- .../Projects/Tables/TableStyling.scss | 14 +- .../Terminologies/AssignMappingsViaButton.jsx | 5 +- .../Terminologies/EditMappingModal.jsx | 74 +++++------ .../Projects/Terminologies/Terminology.jsx | 8 ++ 28 files changed, 456 insertions(+), 196 deletions(-) create mode 100755 docs/.nojekyll create mode 100755 docs/README.md create mode 100644 docs/_navbar.md create mode 100644 docs/datadictionary.md create mode 100755 docs/index.html create mode 100644 src/components/Manager/MappingsFunctions/EditMappingsLabel.jsx create mode 100644 src/components/Manager/MappingsFunctions/MappingRelationship.jsx diff --git a/.github/workflows/dispatch_deploy.yml b/.github/workflows/dispatch_deploy.yml index f400a48..96f2b65 100644 --- a/.github/workflows/dispatch_deploy.yml +++ b/.github/workflows/dispatch_deploy.yml @@ -65,19 +65,27 @@ jobs: run: |- gcloud auth configure-docker '${{ env.REGION }}-docker.pkg.dev' - - name: 'Build Docker Image' # New step to build the image + - name: 'Build Docker Image' run: |- - DOCKER_TAG="${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.SERVICE }}-${{ github.event.inputs.environment }}/${{ env.IMAGE_NAME }}:${{ github.sha }}" + if [[ "${{ github.event.inputs.environment }}" == "prod" ]]; then + SERVICE="${{ env.SERVICE }}" + else + SERVICE="${{ env.SERVICE }}-${{ github.event.inputs.environment }}" + fi + DOCKER_TAG="${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${SERVICE}/${{ env.IMAGE_NAME }}:${{ github.sha }}" + + echo "DOCKER_TAG=${DOCKER_TAG}" >> $GITHUB_ENV + echo "SERVICE=${SERVICE}" >> $GITHUB_ENV + docker build -t "${DOCKER_TAG}" --build-arg ENV=${{ github.event.inputs.environment }} . - name: 'Push Docker Image' run: |- - DOCKER_TAG="${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.SERVICE }}-${{ github.event.inputs.environment }}/${{ env.IMAGE_NAME }}:${{ github.sha }}" docker push "${DOCKER_TAG}" - name: 'Deploy to Cloud Run' uses: 'google-github-actions/deploy-cloudrun@v2' with: - service: '${{ env.SERVICE }}-${{ github.event.inputs.environment }}' + service: ${{ env.SERVICE }} region: '${{ env.REGION }}' - image: "${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.SERVICE }}-${{ github.event.inputs.environment }}/${{ env.IMAGE_NAME }}:${{ github.sha }}" + image: ${{ env.DOCKER_TAG }} \ No newline at end of file diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100755 index 0000000..e69de29 diff --git a/docs/README.md b/docs/README.md new file mode 100755 index 0000000..8ffc7b5 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,45 @@ +# Map Dragon +This application will support several user groups in submitting and validating their data, managing and tracking data definitions, and aligning data to standardized terms. + +## Getting Started +### Vite React App + +This is a React application bootstrapped with Vite. + +### Prerequisites + +Make sure you have Node.js and npm installed on your machine. + +### Installation + +1. Clone the repository: + + ```sh + git clone git@github.com:NIH-NCPI/map-dragon.git + +2. Navigate into the project directory: + + ```sh + cd map-dragon +3. Install dependencies + + ```sh + npm i + +### Development +1. To start the development server, run: + ```sh + npm run dev + +### Dependencies +This project utilizes the following dependencies: + ++ React: A JavaScript library for building user interfaces ++ Vite: A front-end tooling for web development ++ Ant Design: A design component library ++ Sass: A CSS preprocessor ++ React OAuth2 | Google: A React library for Google OAuth ++ jwt-decode: A JWT decoder ++ Papa Parse: A CSV parser + + diff --git a/docs/_navbar.md b/docs/_navbar.md new file mode 100644 index 0000000..b097239 --- /dev/null +++ b/docs/_navbar.md @@ -0,0 +1,4 @@ +* Map Dragon + * [Map Dragon Local Installation Directions](https://github.com/NIH-NCPI/map-dragon) +* File Formats + * [Loading a Data Dictionary](datadictionary.md) \ No newline at end of file diff --git a/docs/datadictionary.md b/docs/datadictionary.md new file mode 100644 index 0000000..675f3b8 --- /dev/null +++ b/docs/datadictionary.md @@ -0,0 +1,31 @@ +# Loading a Data Dictionary +(TBD How to use the website to load the DD) + +## Expected Format for DD Table +The following table format is currently the only format accepted by the Map Dragon "Load Table" function. When run, a new data dictionary table is instantiated along with relevant enumerations as new terminologies. + +Fields such as *variable_name* and *data_type* are required. Other fields such as *description* are optional but highly recommended. + +The data should be a valid, ASCII only CSV file with double quotes used for "Quote Characters", commas for delimiters, etc. + +| *Column Name* | *Column Descriptions* | *Column Type* | +| ------------ | --------------------- | ------------- | +| *variable_name* | The human readable name associated with variable name | String | +| *description* | Fully informative description of the contents associated with this variable/column| String | +| *data_type* | Data type associated with column data | integer, number, string, enumeration | +| *min* | Minimum value (integers and numbers only) | numeric value | +| *max* | Maximum acceptable value (integers and numbers only) | numeric value | +| *units* | UCUM code for units | UCUM code prefixed with the curie, UCUM. UCUM:ml for example | +| *enumerations* | Semi-colon separated list of enumerated values. See below for more details about formatting the enumeration list. | String | + +### Formatting for Enumerations +At the top level, enumerations are just a list of terms separated by semi-colons. For enumerated types with specified codes, these will be provided in the following format: + +> 1=Male;2=Female;0=Unknown;-1=Preferred Not To Answer + +In this example, the following codes are extracted: 1, 2, 0 and -1 and assigned the following displays (in order): Male, Female, Unknown and Preferred Not To Answer. + +If there is no code specified, the codes will be created using the following logic: +> Case is dropped to lower case and potentially problematic characters, such as parenthesis, "(" or ")" will be removed. Whitespace characters will be replaced with underscores, "_". + +There is currently no way to allow codes or values to include semi-colons, ";", or equal signs, "=" in them. They should be replaced prior to loading into Map Dragon. \ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100755 index 0000000..00b6590 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,23 @@ + + + + + Document + + + + + + +
+ + + + + diff --git a/package.json b/package.json index 34885eb..ae8a038 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "build-dev": "vite build --mode dev", "build-uat": "vite build --mode uat", "build-alpha": "vite build --mode alpha", + "build-prod": "vite build --mode prod", "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview" }, diff --git a/src/App.jsx b/src/App.jsx index 6363339..040f2bb 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -2,6 +2,7 @@ import { message } from 'antd'; import { useState, createContext, useRef, useEffect } from 'react'; import { AppRouter } from './AppRouter'; import { GoogleOAuthProvider } from '@react-oauth/google'; +import { getSessionStatus } from './components/Manager/SessionsManager'; export const myContext = createContext(); @@ -41,6 +42,9 @@ function App() { message.config({ top: '25vh', }); + useEffect(() => { + getSessionStatus(vocabUrl); + }); return ( { - const { user } = useContext(myContext); - + const { user, vocabUrl } = useContext(myContext); const isLoggedIn = () => { - if (user) { + const storedUser = localStorage.getItem('user'); + + if (storedUser) { return true; } else { return false; diff --git a/src/Contexts/MappingContext.jsx b/src/Contexts/MappingContext.jsx index 97102e7..6a35c0f 100644 --- a/src/Contexts/MappingContext.jsx +++ b/src/Contexts/MappingContext.jsx @@ -13,6 +13,9 @@ export function MappingContextRoot() { const [selectedMappings, setSelectedMappings] = useState([]); const [displaySelectedMappings, setDisplaySelectedMappings] = useState([]); const [selectedBoxes, setSelectedBoxes] = useState([]); + const [relationshipOptions, setRelationshipOptions] = useState([]); + const [idsForSelect, setIdsForSelect] = useState([]); + const [showOptions, setShowOptions] = useState(false); const context = { editMappings, @@ -33,6 +36,12 @@ export function MappingContextRoot() { setSelectedBoxes, assignMappings, setAssignMappings, + relationshipOptions, + setRelationshipOptions, + idsForSelect, + setIdsForSelect, + showOptions, + setShowOptions, }; return ( diff --git a/src/components/Auth/Login.jsx b/src/components/Auth/Login.jsx index d3e29fb..dbda699 100644 --- a/src/components/Auth/Login.jsx +++ b/src/components/Auth/Login.jsx @@ -32,6 +32,7 @@ export const Login = () => { JSON.stringify(credentialResponseDecoded) ); startSession(vocabUrl, credentialResponseDecoded.email); + setUser(credentialResponseDecoded); }} onError={() => { console.log('Login Failed'); diff --git a/src/components/Manager/MappingsFunctions/AssignMappings.jsx b/src/components/Manager/MappingsFunctions/AssignMappings.jsx index 43ffa63..3707f3f 100644 --- a/src/components/Manager/MappingsFunctions/AssignMappings.jsx +++ b/src/components/Manager/MappingsFunctions/AssignMappings.jsx @@ -17,7 +17,8 @@ export const AssignMappings = ({ const { vocabUrl, user } = useContext(myContext); const { prefTerminologies, setApiResults } = useContext(SearchContext); - const { setMapping } = useContext(MappingContext); + const { setMapping, idsForSelect, setIdsForSelect } = + useContext(MappingContext); const [terminologiesToMap, setTerminologiesToMap] = useState([]); const [loading, setLoading] = useState(false); const [mappingProp, setMappingProp] = useState(''); @@ -27,6 +28,7 @@ export const AssignMappings = ({ setSelectedKey(null); setMappingProp(''); setApiResults([]); + setIdsForSelect([]); }; const fetchTerminologies = () => { setLoading(true); @@ -64,6 +66,7 @@ export const AssignMappings = ({ ? item.description[0] : item.description, system: item.system, + mapping_relationship: idsForSelect[item.obo_id || item.code], })); const mappingsDTO = { mappings: selectedMappings, diff --git a/src/components/Manager/MappingsFunctions/ClearMappings.jsx b/src/components/Manager/MappingsFunctions/ClearMappings.jsx index c5c2cfe..c851d36 100644 --- a/src/components/Manager/MappingsFunctions/ClearMappings.jsx +++ b/src/components/Manager/MappingsFunctions/ClearMappings.jsx @@ -18,9 +18,9 @@ export const ClearMappings = ({ propId, component }) => { const handleDelete = evt => { return fetch(`${vocabUrl}/${component}/${propId}/mapping`, { method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - }, + // headers: { + // 'Content-Type': 'application/json', + // }, // body: JSON.stringify({ editor: user.email }), }) .then(res => { diff --git a/src/components/Manager/MappingsFunctions/DisplaySelected.jsx b/src/components/Manager/MappingsFunctions/DisplaySelected.jsx index 97ac7ad..39c43de 100644 --- a/src/components/Manager/MappingsFunctions/DisplaySelected.jsx +++ b/src/components/Manager/MappingsFunctions/DisplaySelected.jsx @@ -1,5 +1,6 @@ import { Checkbox, Form, Tooltip } from 'antd'; import { ellipsisString } from '../Utilitiy'; +import { MappingRelationship } from './MappingRelationship'; export const DisplaySelected = ({ displaySelectedMappings, @@ -13,8 +14,11 @@ export const DisplaySelected = ({
{selected?.code}
+
{selected?.display || selected?.label}
+
+ +
-
{selected?.display || selected?.label}
{selected?.description?.length > 85 ? ( { + const { showOptions, setShowOptions, relationshipOptions, idsForSelect } = + useContext(MappingContext); + // Find the object in relationshipOptions where the code matches the mappings's mapping_relationship + // If there is a match, return the display. If not, return null. + const displayRelationship = item => { + const findDisplay = relationshipOptions.find( + ro => ro.code === item.mapping_relationship + ); + + return findDisplay ? findDisplay.display : null; + }; + + return ( + <> +
+
+
+
+ {item?.display} +
+
{item?.code}
+
{ + e.preventDefault(); + setShowOptions(item.mapping_relationship); + }} + > + {item?.mapping_relationship && !showOptions ? ( + displayRelationship(item) + ) : item.mapping_relationship && !!showOptions ? ( + + ) : ( + !item.mapping_relationship && ( + + ) + )} +
+
+
+ {item?.description?.length > 100 ? ( + + {ellipsisString(item?.description, '100')} + + ) : ( + ellipsisString(item?.description, '100') + )} +
+
+
+ + ); +}; diff --git a/src/components/Manager/MappingsFunctions/GetMappingsModal.jsx b/src/components/Manager/MappingsFunctions/GetMappingsModal.jsx index 4fa4c7b..c6a4ef8 100644 --- a/src/components/Manager/MappingsFunctions/GetMappingsModal.jsx +++ b/src/components/Manager/MappingsFunctions/GetMappingsModal.jsx @@ -17,7 +17,7 @@ import { getFiltersByCode, olsFilterOntologiesSearch } from '../FetchManager'; import { OntologyCheckboxes } from './OntologyCheckboxes'; import { OntologyFilterCodeSubmit } from './OntologyFilterCodeSubmit'; import { OntologyFilterCodeSubmitTerm } from './OntologyFilterCodeSubmitTerm'; -import { useParams } from 'react-router-dom'; +import { MappingRelationship } from './MappingRelationship'; export const GetMappingsModal = ({ componentString, @@ -54,18 +54,21 @@ export const GetMappingsModal = ({ const [filteredResultsCount, setFilteredResultsCount] = useState(0); const [inputValue, setInputValue] = useState(searchProp); //Sets the value of the search bar const [currentSearchProp, setCurrentSearchProp] = useState(searchProp); + const { setSelectedMappings, displaySelectedMappings, setDisplaySelectedMappings, selectedBoxes, setSelectedBoxes, + idsForSelect, } = useContext(MappingContext); let ref = useRef(); // since the code is passed through searchProp, the '!!' forces it to be evaluated as a boolean. // if there is a searchProp being passed, it evaluates to true and runs the search function. // inputValue and currentSearchProp for the search bar is set to the passed searchProp. // The function is run when the code changes. + useEffect(() => { setInputValue(searchProp); setCurrentSearchProp(searchProp); @@ -157,12 +160,16 @@ export const GetMappingsModal = ({ display: item.label, description: item.description[0], system: systemsMatch(item.obo_id.split(':')[0], ontologyApis), + mapping_relationship: idsForSelect[item.obo_id], })); + const mappingsDTO = { mappings: selectedMappings, - // editor: user.email, + // editor: user?.email, }; + setLoading(true); + console.log(user); fetch( `${vocabUrl}/${componentString}/${component.id}/mapping/${mappingProp}`, { @@ -330,6 +337,9 @@ export const GetMappingsModal = ({ {d?.obo_id}
+
+ +
{ellipsisString(d?.description[0], '100')}
@@ -413,6 +423,7 @@ export const GetMappingsModal = ({ }} onCancel={() => { onClose(); + setResults([]); form.resetFields(); setGetMappings(null); setResults([]); diff --git a/src/components/Manager/MappingsFunctions/MappingRelationship.jsx b/src/components/Manager/MappingsFunctions/MappingRelationship.jsx new file mode 100644 index 0000000..451a57b --- /dev/null +++ b/src/components/Manager/MappingsFunctions/MappingRelationship.jsx @@ -0,0 +1,40 @@ +import { Select } from 'antd'; +import { useContext } from 'react'; +import { MappingContext } from '../../../Contexts/MappingContext'; + +export const MappingRelationship = ({ mapping }) => { + const { relationshipOptions, idsForSelect, setIdsForSelect } = + useContext(MappingContext); + + const handleSelectChange = (obo_id, value) => { + setIdsForSelect(prev => ({ + ...prev, + [obo_id]: value, + })); + }; + + const options = relationshipOptions.map(ro => { + return { + value: ro.code, + label: ro.display, + }; + }); + + return ( +