From 1e65bf6ed8df3a822fed3b497addad7a1ab2d766 Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Thu, 15 May 2025 10:44:18 +0200 Subject: [PATCH 1/4] ILEX-120 fix search term having id as a title --- src/components/Header/Search.jsx | 8 ++++---- src/components/SingleTermView/index.jsx | 6 ++++-- src/contexts/DataContext.jsx | 9 ++++++++- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/components/Header/Search.jsx b/src/components/Header/Search.jsx index 24efec80..06e94dae 100644 --- a/src/components/Header/Search.jsx +++ b/src/components/Header/Search.jsx @@ -13,15 +13,15 @@ import { } from "@mui/material"; import { debounce } from 'lodash'; import PropTypes from 'prop-types'; -import { useQuery } from "../../helpers"; import BasicTabs from "../common/CustomTabs"; import { useNavigate } from "react-router-dom"; import { SEARCH_TYPES } from "../../constants/types"; import { searchAll, elasticSearch } from "../../api/endpoints"; import FolderOutlinedIcon from '@mui/icons-material/FolderOutlined'; -import { useEffect, useState, useCallback, forwardRef } from 'react'; +import { useEffect, useState, useCallback, forwardRef, useContext } from 'react'; import { CloseIcon, ForwardIcon, SearchIcon, TermsIcon } from '../../Icons'; import CorporateFareOutlinedIcon from '@mui/icons-material/CorporateFareOutlined'; +import { GlobalDataContext } from "../../contexts/DataContext"; import { vars } from "../../theme/variables"; const { gray200, gray100, gray600, gray800, gray500, gray700 } = vars; @@ -87,11 +87,10 @@ const Search = () => { const [openList, setOpenList] = useState(false); const [tabValue, setTabValue] = useState(0); const navigate = useNavigate(); - const query = useQuery(); - const storedSearchTerm = query.get('searchTerm'); const [terms, setTerms] = useState([]); const [organizations, setOrganizations] = useState([]); const [ontologies, setOntologies] = useState([]); + const { storedSearchTerm, updateStoredSearchTerm } = useContext(GlobalDataContext); const handleOpenList = () => setOpenList(true); const handleCloseList = () => setOpenList(false); @@ -101,6 +100,7 @@ const Search = () => { if (!newInputValue) return; handleCloseList(); + updateStoredSearchTerm(newInputValue?.label) navigate(`/view?searchTerm=${newInputValue?.ilx}`); }; diff --git a/src/components/SingleTermView/index.jsx b/src/components/SingleTermView/index.jsx index 3ccda1ce..78e58474 100644 --- a/src/components/SingleTermView/index.jsx +++ b/src/components/SingleTermView/index.jsx @@ -23,7 +23,7 @@ import CopyLinkComponent from "../common/CopyLinkComponent"; import BasicTabs from "../common/CustomTabs"; import CustomButton from "../common/CustomButton"; import CustomMenu from "./CustomMenu"; -import React, { useState } from "react"; +import React, { useState, useContext } from "react"; import OverView from "./OverView/OverView"; import HistoryPanel from "./History/HistoryPanel"; import VariantsPanel from "./Variants/VariantsPanel"; @@ -43,6 +43,7 @@ import CustomSingleSelect from "../common/CustomSingleSelect"; import HomeOutlinedIcon from "@mui/icons-material/HomeOutlined"; import CreateForkDialog from "./CreateForkDialog"; import TermDialog from "../TermEditor/TermDialog"; +import { GlobalDataContext } from "../../contexts/DataContext"; const { gray200, brand700, gray600 } = vars; @@ -63,6 +64,7 @@ const SingleTermView = () => { const searchTerm = query.get('searchTerm'); const openDataFormatMenu = Boolean(dataFormatAnchorEl); const [openForkDialog, setOpenForkDialog] = React.useState(false); + const { storedSearchTerm } = useContext(GlobalDataContext); const handleForkDialogClose = () => { setOpenForkDialog(false); @@ -156,7 +158,7 @@ const SingleTermView = () => { - {searchTerm} + {storedSearchTerm} diff --git a/src/contexts/DataContext.jsx b/src/contexts/DataContext.jsx index 2f33151b..340a8abe 100644 --- a/src/contexts/DataContext.jsx +++ b/src/contexts/DataContext.jsx @@ -10,6 +10,7 @@ const GlobalDataProvider = ({ children }) => { const [searchTypeFilter, setSearchTypeFilter] = useState(null); const [predicatesSingleTermState, setPredicatesSingleTermState] = useState(false); const [editBulkSearchFilters, setEditBulkSearchFilters] = useState([]); + const [storedSearchTerm, setStoredSearchTerm] = useState(""); const setOntologyData = (ontology) => { setActiveOntology(ontology); }; @@ -34,6 +35,10 @@ const GlobalDataProvider = ({ children }) => { setUser(user); } + const updateStoredSearchTerm = (value) => { + setStoredSearchTerm(value) + } + const dataContextValue = { user, setUserData, @@ -46,7 +51,9 @@ const GlobalDataProvider = ({ children }) => { predicatesSingleTermState, setPredicatesSingleTermData, editBulkSearchFilters, - setEditBulkSearchData + setEditBulkSearchData, + storedSearchTerm, + updateStoredSearchTerm }; return ( From f61f752943c3f75835c53d935d804293ddd167e5 Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Thu, 15 May 2025 12:50:58 +0200 Subject: [PATCH 2/4] ILEX-120 fix overview section issue --- src/components/Header/Search.jsx | 8 ++++---- src/components/SearchResults/ListView.jsx | 6 +++++- src/components/SingleTermView/OverView/OverView.jsx | 8 ++++---- src/components/SingleTermView/index.jsx | 6 +++--- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/components/Header/Search.jsx b/src/components/Header/Search.jsx index 06e94dae..b777885d 100644 --- a/src/components/Header/Search.jsx +++ b/src/components/Header/Search.jsx @@ -100,7 +100,7 @@ const Search = () => { if (!newInputValue) return; handleCloseList(); - updateStoredSearchTerm(newInputValue?.label) + updateStoredSearchTerm(newInputValue) navigate(`/view?searchTerm=${newInputValue?.ilx}`); }; @@ -167,14 +167,14 @@ const Search = () => { }, 500), [searchAll]); useEffect(() => { - if (searchTerm && storedSearchTerm !== searchTerm) { + if (searchTerm && storedSearchTerm.label !== searchTerm) { fetchTerms(searchTerm); } }, [searchTerm, fetchTerms, storedSearchTerm]); useEffect(() => { - if (storedSearchTerm !== searchTerm) { - setSearchTerm(storedSearchTerm); + if (storedSearchTerm.label !== searchTerm) { + setSearchTerm(storedSearchTerm.label); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [storedSearchTerm]); diff --git a/src/components/SearchResults/ListView.jsx b/src/components/SearchResults/ListView.jsx index eeb6cd35..de36b4a3 100644 --- a/src/components/SearchResults/ListView.jsx +++ b/src/components/SearchResults/ListView.jsx @@ -1,9 +1,11 @@ import PropTypes from "prop-types"; +import { useContext } from "react"; import { useNavigate } from "react-router-dom"; import CustomButton from '../common/CustomButton'; import DeleteOutlinedIcon from '@mui/icons-material/DeleteOutlined'; import CreateNewFolderOutlinedIcon from '@mui/icons-material/CreateNewFolderOutlined'; import { Box, Typography, Grid, Stack, Chip, CircularProgress } from '@mui/material'; +import { GlobalDataContext } from "../../contexts/DataContext"; import { vars } from '../../theme/variables'; const { gray200, gray500, gray700, brand50, brand200, brand600, brand700, error50, error300, error700 } = vars; @@ -129,9 +131,11 @@ const InfoSection = ({ searchResult }) => { const ListView = ({ searchResults, loading }) => { const navigate = useNavigate(); + const { updateStoredSearchTerm } = useContext(GlobalDataContext); const handleClick = (searchResult) => { - navigate(`/view?searchTerm=${searchResult?.label}`); + updateStoredSearchTerm(searchResult) + navigate(`/view?searchTerm=${searchResult?.ilx}`); }; diff --git a/src/components/SingleTermView/OverView/OverView.jsx b/src/components/SingleTermView/OverView/OverView.jsx index ee1397ec..67041681 100644 --- a/src/components/SingleTermView/OverView/OverView.jsx +++ b/src/components/SingleTermView/OverView/OverView.jsx @@ -8,14 +8,11 @@ import { debounce } from 'lodash'; import PropTypes from 'prop-types'; import Hierarchy from "./Hierarchy"; import Predicates from "./Predicates"; -import {useQuery} from "../../../helpers"; import RawDataViewer from "./RawDataViewer"; import {useCallback, useEffect, useMemo, useState} from "react"; import { getMatchTerms } from "../../../api/endpoints"; -const OverView = ({ isCodeViewVisible, selectedDataFormat }) => { - const query = useQuery(); - const searchTerm = query.get('searchTerm'); +const OverView = ({ searchTerm, isCodeViewVisible, selectedDataFormat }) => { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); @@ -31,6 +28,8 @@ const OverView = ({ isCodeViewVisible, selectedDataFormat }) => { }, 300), [] ); + + console.log("searchTerm: ", searchTerm) useEffect(() => { setLoading(true); @@ -67,6 +66,7 @@ const OverView = ({ isCodeViewVisible, selectedDataFormat }) => { } OverView.propTypes = { + searchTerm: PropTypes.string, isCodeViewVisible: PropTypes.bool, selectedDataFormat: PropTypes.string } diff --git a/src/components/SingleTermView/index.jsx b/src/components/SingleTermView/index.jsx index 78e58474..a7c95643 100644 --- a/src/components/SingleTermView/index.jsx +++ b/src/components/SingleTermView/index.jsx @@ -158,7 +158,7 @@ const SingleTermView = () => { - {storedSearchTerm} + {storedSearchTerm.label} @@ -255,7 +255,7 @@ const SingleTermView = () => { { - tabValue === 0 && + tabValue === 0 && } { tabValue === 1 && @@ -268,7 +268,7 @@ const SingleTermView = () => { } - + Date: Fri, 16 May 2025 17:31:48 +0200 Subject: [PATCH 3/4] ILEX-120 use endpoint to handle stored search term --- src/api/endpoints/apiService.ts | 43 ++++++++++++- src/components/Header/Search.jsx | 25 ++++---- src/components/SearchResults/ListView.jsx | 2 +- .../SingleTermView/OverView/OverView.jsx | 2 - src/components/SingleTermView/index.jsx | 60 +++++++++++-------- 5 files changed, 89 insertions(+), 43 deletions(-) diff --git a/src/api/endpoints/apiService.ts b/src/api/endpoints/apiService.ts index f27e2e0a..fd281631 100644 --- a/src/api/endpoints/apiService.ts +++ b/src/api/endpoints/apiService.ts @@ -1,7 +1,5 @@ -import React from "react"; import { createPostRequest, createGetRequest } from "./apiActions"; import { API_CONFIG } from "../../config"; -import { GlobalDataContext } from "../../contexts/DataContext"; export interface LoginRequest { username: string @@ -17,6 +15,19 @@ export interface RegisterRequest { organization: string } +type LabelType = + | string + | { '@value': string; '@language'?: string } + | Array; + +interface GraphNode { + 'rdfs:label'?: LabelType; +} + +interface JsonLdResponse { + '@graph'?: GraphNode[]; +} + export const login = createPostRequest(API_CONFIG.REAL_API.SIGNIN, "application/x-www-form-urlencoded") export const register = createPostRequest(API_CONFIG.REAL_API.NEWUSER_ILX, "application/x-www-form-urlencoded") @@ -27,7 +38,7 @@ export const getUserSettings = (group: string) => { return createGetRequest(endpoint, "application/json")(); }; -export const createNewOrganization = ({group, data} : {group: string, data: any}) => { +export const createNewOrganization = ({ group, data }: { group: string, data: any }) => { const endpoint = `/${group}${API_CONFIG.REAL_API.CREATE_NEW_ORGANIZATION}`; return createPostRequest(endpoint, "application/json")(data); }; @@ -41,3 +52,29 @@ export const userLogout = (group: string) => { const endpoint = `/${group}${API_CONFIG.REAL_API.LOGOUT}`; return createGetRequest(endpoint, "application/json")(); }; + +export const getSelectedTermLabel = async (searchTerm: string): Promise => { + try { + const res = await fetch(`https://uri.olympiangods.org/base/${searchTerm}.jsonld`); + if (!res.ok) throw new Error(`Response status: ${res.status}`); + + const data: JsonLdResponse = await res.json(); + const label = data['@graph']?.[0]?.['rdfs:label']; + + const getLabelValue = (label: LabelType): string => { + if (typeof label === 'string') return label; + if (Array.isArray(label)) { + const en = label.find( + l => typeof l === 'string' || (typeof l === 'object' && l?.['@language'] === 'en') + ); + return typeof en === 'string' ? en : en?.['@value'] || ''; + } + return label?.['@value'] || ''; + }; + + return label ? getLabelValue(label) : undefined + } catch (err: any) { + console.error(err.message); + return undefined; + } +}; \ No newline at end of file diff --git a/src/components/Header/Search.jsx b/src/components/Header/Search.jsx index b777885d..8defa9c1 100644 --- a/src/components/Header/Search.jsx +++ b/src/components/Header/Search.jsx @@ -15,13 +15,13 @@ import { debounce } from 'lodash'; import PropTypes from 'prop-types'; import BasicTabs from "../common/CustomTabs"; import { useNavigate } from "react-router-dom"; +import { GlobalDataContext } from "../../contexts/DataContext"; import { SEARCH_TYPES } from "../../constants/types"; import { searchAll, elasticSearch } from "../../api/endpoints"; import FolderOutlinedIcon from '@mui/icons-material/FolderOutlined'; import { useEffect, useState, useCallback, forwardRef, useContext } from 'react'; import { CloseIcon, ForwardIcon, SearchIcon, TermsIcon } from '../../Icons'; import CorporateFareOutlinedIcon from '@mui/icons-material/CorporateFareOutlined'; -import { GlobalDataContext } from "../../contexts/DataContext"; import { vars } from "../../theme/variables"; const { gray200, gray100, gray600, gray800, gray500, gray700 } = vars; @@ -90,7 +90,7 @@ const Search = () => { const [terms, setTerms] = useState([]); const [organizations, setOrganizations] = useState([]); const [ontologies, setOntologies] = useState([]); - const { storedSearchTerm, updateStoredSearchTerm } = useContext(GlobalDataContext); + const { storedSearchTerm, updateStoredSearchTerm} = useContext(GlobalDataContext) const handleOpenList = () => setOpenList(true); const handleCloseList = () => setOpenList(false); @@ -98,15 +98,15 @@ const Search = () => { const handleSelectTerm = (event, newInputValue) => { if (!newInputValue) return; - + handleCloseList(); - updateStoredSearchTerm(newInputValue) navigate(`/view?searchTerm=${newInputValue?.ilx}`); + updateStoredSearchTerm(newInputValue?.label) }; const handleSearchTermClick = () => { - navigate(`/search?searchTerm=${searchTerm}`); handleCloseList(); + navigate(`/search?searchTerm=${searchTerm}`); }; const handleInputFocus = (event) => { @@ -139,7 +139,7 @@ const Search = () => { setTerms([]) setOntologies([]) setOrganizations([]) - },[]); + }, []); const handleKeyDown = useCallback(event => { if (event.ctrlKey && event.key === 'k') { @@ -167,19 +167,18 @@ const Search = () => { }, 500), [searchAll]); useEffect(() => { - if (searchTerm && storedSearchTerm.label !== searchTerm) { + if (searchTerm && storedSearchTerm !== searchTerm) { fetchTerms(searchTerm); } }, [searchTerm, fetchTerms, storedSearchTerm]); useEffect(() => { - if (storedSearchTerm.label !== searchTerm) { - setSearchTerm(storedSearchTerm.label); + if (storedSearchTerm !== searchTerm) { + setSearchTerm(storedSearchTerm); } - // eslint-disable-next-line react-hooks/exhaustive-deps }, [storedSearchTerm]); - // eslint-disable-next-line no-unused-vars + //eslint-disable-next-line no-unused-vars const ListboxComponent = forwardRef(function ListboxComponent(props, ref) { return ( <> @@ -305,7 +304,7 @@ const Search = () => { getOptionLabel={(option) => option?.hidden ? '' : (option.label || option.name || '')} renderOption={(props, option, { selected }) => { if (option?.hidden) return null; - + const { key, ...otherProps } = props; return ( { ); - }} + }} renderInput={(params) => ( { const { updateStoredSearchTerm } = useContext(GlobalDataContext); const handleClick = (searchResult) => { - updateStoredSearchTerm(searchResult) + updateStoredSearchTerm(searchResult?.label) navigate(`/view?searchTerm=${searchResult?.ilx}`); }; diff --git a/src/components/SingleTermView/OverView/OverView.jsx b/src/components/SingleTermView/OverView/OverView.jsx index 67041681..468c71b0 100644 --- a/src/components/SingleTermView/OverView/OverView.jsx +++ b/src/components/SingleTermView/OverView/OverView.jsx @@ -28,8 +28,6 @@ const OverView = ({ searchTerm, isCodeViewVisible, selectedDataFormat }) => { }, 300), [] ); - - console.log("searchTerm: ", searchTerm) useEffect(() => { setLoading(true); diff --git a/src/components/SingleTermView/index.jsx b/src/components/SingleTermView/index.jsx index a7c95643..f699ba8b 100644 --- a/src/components/SingleTermView/index.jsx +++ b/src/components/SingleTermView/index.jsx @@ -1,3 +1,4 @@ +import { useState, useEffect, useRef, useContext } from "react"; import { Box, Button, @@ -23,7 +24,6 @@ import CopyLinkComponent from "../common/CopyLinkComponent"; import BasicTabs from "../common/CustomTabs"; import CustomButton from "../common/CustomButton"; import CustomMenu from "./CustomMenu"; -import React, { useState, useContext } from "react"; import OverView from "./OverView/OverView"; import HistoryPanel from "./History/HistoryPanel"; import VariantsPanel from "./Variants/VariantsPanel"; @@ -43,6 +43,7 @@ import CustomSingleSelect from "../common/CustomSingleSelect"; import HomeOutlinedIcon from "@mui/icons-material/HomeOutlined"; import CreateForkDialog from "./CreateForkDialog"; import TermDialog from "../TermEditor/TermDialog"; +import { getSelectedTermLabel } from "../../api/endpoints/apiService"; import { GlobalDataContext } from "../../contexts/DataContext"; const { gray200, brand700, gray600 } = vars; @@ -50,29 +51,29 @@ const { gray200, brand700, gray600 } = vars; const dataFormats = ['JSON-LD', 'Turtle', 'N3', 'OWL', 'CSV'] const SingleTermView = () => { - const [open, setOpen] = React.useState(false); - const actionRef = React.useRef(null); - const anchorRef = React.useRef(null); - const [dataFormatAnchorEl, setDataFormatAnchorEl] = React.useState(null); - const [tabValue, setTabValue] = React.useState(0); - const [isCodeViewVisible, setIsCodeViewVisible] = React.useState(false); + const [open, setOpen] = useState(false); + const actionRef = useRef(null); + const anchorRef = useRef(null); + const [dataFormatAnchorEl, setDataFormatAnchorEl] = useState(null); + const [tabValue, setTabValue] = useState(0); + const [isCodeViewVisible, setIsCodeViewVisible] = useState(false); const [toggleButtonValue, setToggleButtonValue] = useState('defaultView'); - const [selectedDataFormat, setSelectedDataFormat] = React.useState('JSON-LD'); - const [openRequestMergeDialog, setOpenRequestMergeDialog] = React.useState(false); - const [editTermDialogOpen, setEditTermDialogOpen] = React.useState(false); + const [selectedDataFormat, setSelectedDataFormat] = useState('JSON-LD'); + const [openRequestMergeDialog, setOpenRequestMergeDialog] = useState(false); + const [editTermDialogOpen, setEditTermDialogOpen] = useState(false); const query = useQuery(); const searchTerm = query.get('searchTerm'); const openDataFormatMenu = Boolean(dataFormatAnchorEl); - const [openForkDialog, setOpenForkDialog] = React.useState(false); - const { storedSearchTerm } = useContext(GlobalDataContext); + const [openForkDialog, setOpenForkDialog] = useState(false); + const { storedSearchTerm, updateStoredSearchTerm } = useContext(GlobalDataContext); - const handleForkDialogClose = () => { - setOpenForkDialog(false); - } + const handleForkDialogClose = () => { + setOpenForkDialog(false); + } - const handleOpenForkDialog = () => { - setOpenForkDialog(true); - } + const handleOpenForkDialog = () => { + setOpenForkDialog(true); + } const handleClickDataFormatMenu = (event) => { setDataFormatAnchorEl(event.currentTarget); }; @@ -128,6 +129,17 @@ const SingleTermView = () => { { label: 'ILX:0101901' }, ]; + useEffect(() => { + const fetchLabel = async () => { + const result = await getSelectedTermLabel(searchTerm); + updateStoredSearchTerm(result); + }; + + if (searchTerm) { + fetchLabel(); + } + }, [searchTerm]); + const isItFork = true; return ( @@ -158,7 +170,7 @@ const SingleTermView = () => { - {storedSearchTerm.label} + {storedSearchTerm} @@ -268,11 +280,11 @@ const SingleTermView = () => { } - - + + ) } From 3950f471a0c4ee1568b157df10c914ee0cda1757 Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Fri, 16 May 2025 17:42:23 +0200 Subject: [PATCH 4/4] ILEX-120 fix linting --- src/components/Header/Search.jsx | 1 + src/components/SingleTermView/index.jsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Header/Search.jsx b/src/components/Header/Search.jsx index 8defa9c1..972fd6dd 100644 --- a/src/components/Header/Search.jsx +++ b/src/components/Header/Search.jsx @@ -176,6 +176,7 @@ const Search = () => { if (storedSearchTerm !== searchTerm) { setSearchTerm(storedSearchTerm); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [storedSearchTerm]); //eslint-disable-next-line no-unused-vars diff --git a/src/components/SingleTermView/index.jsx b/src/components/SingleTermView/index.jsx index f699ba8b..ecb13764 100644 --- a/src/components/SingleTermView/index.jsx +++ b/src/components/SingleTermView/index.jsx @@ -138,7 +138,7 @@ const SingleTermView = () => { if (searchTerm) { fetchLabel(); } - }, [searchTerm]); + }, [searchTerm, updateStoredSearchTerm]); const isItFork = true;