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 24efec80..972fd6dd 100644 --- a/src/components/Header/Search.jsx +++ b/src/components/Header/Search.jsx @@ -13,13 +13,13 @@ 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 { 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 } from 'react'; +import { useEffect, useState, useCallback, forwardRef, useContext } from 'react'; import { CloseIcon, ForwardIcon, SearchIcon, TermsIcon } from '../../Icons'; import CorporateFareOutlinedIcon from '@mui/icons-material/CorporateFareOutlined'; @@ -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); @@ -99,14 +98,15 @@ const Search = () => { const handleSelectTerm = (event, newInputValue) => { if (!newInputValue) return; - + handleCloseList(); 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') { @@ -179,7 +179,7 @@ const Search = () => { // 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 +305,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 ListView = ({ searchResults, loading }) => { const navigate = useNavigate(); + const { updateStoredSearchTerm } = useContext(GlobalDataContext); const handleClick = (searchResult) => { - navigate(`/view?searchTerm=${searchResult?.label}`); + 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 ee1397ec..468c71b0 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); @@ -67,6 +64,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 3ccda1ce..ecb13764 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 } from "react"; import OverView from "./OverView/OverView"; import HistoryPanel from "./History/HistoryPanel"; import VariantsPanel from "./Variants/VariantsPanel"; @@ -43,34 +43,37 @@ 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; 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 [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); }; @@ -126,6 +129,17 @@ const SingleTermView = () => { { label: 'ILX:0101901' }, ]; + useEffect(() => { + const fetchLabel = async () => { + const result = await getSelectedTermLabel(searchTerm); + updateStoredSearchTerm(result); + }; + + if (searchTerm) { + fetchLabel(); + } + }, [searchTerm, updateStoredSearchTerm]); + const isItFork = true; return ( @@ -156,7 +170,7 @@ const SingleTermView = () => { - {searchTerm} + {storedSearchTerm} @@ -253,7 +267,7 @@ const SingleTermView = () => { { - tabValue === 0 && + tabValue === 0 && } { tabValue === 1 && @@ -267,10 +281,10 @@ const SingleTermView = () => { - + ) } 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 (