diff --git a/mock/mutator/customClient.ts b/mock/mutator/customClient.ts index b09388ab..94d4c0da 100644 --- a/mock/mutator/customClient.ts +++ b/mock/mutator/customClient.ts @@ -1,35 +1,84 @@ -import Axios, { AxiosRequestConfig } from 'axios'; +import Axios, { AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'; 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 +const AXIOS_INSTANCE = Axios.create({ + baseURL: API_CONFIG.BASE_URL, + validateStatus: function (status) { + return status !== undefined && status >= 200 && status < 400; + }, + withCredentials: true, +}); + +AXIOS_INSTANCE.interceptors.response.use( + async (response: AxiosResponse) => { + const redirectUrl = response.headers['x-redirect-location']; + + if (response.status === 303 && redirectUrl) { + let redirectedPath: string; + + try { + // If it's a full URL, extract only the pathname + const parsed = new URL(redirectUrl); + redirectedPath = parsed.pathname; + } catch { + // If it's already a relative path + redirectedPath = redirectUrl; + } + + console.debug('Redirect intercepted. Rewriting to:', redirectedPath); + + try { + const redirectedResponse = await AXIOS_INSTANCE.get(redirectedPath, { + headers: { + Accept: 'text/html', + ...response.config.headers, + }, + withCredentials: true, + }); + + return redirectedResponse; + } catch (fetchError) { + console.error('Redirect follow failed:', fetchError); + return Promise.reject(fetchError); + } + } + + return response; + }, + (error) => { + console.error('Interceptor error:', error); + return Promise.reject(error); + } +); + +export default AXIOS_INSTANCE; + export const customInstance = ( config: AxiosRequestConfig, options?: AxiosRequestConfig, ): Promise => { const source = Axios.CancelToken.source(); + const promise = AXIOS_INSTANCE({ ...config, ...options, cancelToken: source.token, - }).then(({ data }) => data).catch(error => { - throw error; - }); + }) + .then(({ data }) => data) + .catch((error) => { + throw error; + }); - // @ts-ignore promise.cancel = () => { - console.log("query was cancelled") + console.log('Query was cancelled'); source.cancel('Query was cancelled'); }; return promise; }; -// In some case with react-query and swr you want to be able to override the return error type so you can also do it here like this export type ErrorType = AxiosError; 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; diff --git a/src/api/endpoints/apiActions.ts b/src/api/endpoints/apiActions.ts index 302d05dd..d25d8458 100644 --- a/src/api/endpoints/apiActions.ts +++ b/src/api/endpoints/apiActions.ts @@ -6,16 +6,14 @@ import { useCookies } from 'react-cookie' type SecondParameter any> = Parameters[1]; -export const createPostRequest = (endpoint: string, contentType = "application/json") => { +export const createPostRequest = (endpoint: string, headers : object) => { return (data?: D, options?: SecondParameter) => { return customInstance( { url: endpoint, method: "POST", data: data, - headers: { - "Content-Type": contentType, - }, + headers: headers, withCredentials: true }, options, diff --git a/src/api/endpoints/apiService.ts b/src/api/endpoints/apiService.ts index fd281631..84866d94 100644 --- a/src/api/endpoints/apiService.ts +++ b/src/api/endpoints/apiService.ts @@ -28,9 +28,9 @@ interface JsonLdResponse { '@graph'?: GraphNode[]; } -export const login = createPostRequest(API_CONFIG.REAL_API.SIGNIN, "application/x-www-form-urlencoded") +export const login = createPostRequest(API_CONFIG.REAL_API.SIGNIN, {"Content-Type": "application/x-www-form-urlencoded"}) -export const register = createPostRequest(API_CONFIG.REAL_API.NEWUSER_ILX, "application/x-www-form-urlencoded") +export const register = createPostRequest(API_CONFIG.REAL_API.NEWUSER_ILX, {"Content-Type": "application/x-www-form-urlencoded"}) export const getUserSettings = (group: string) => { @@ -40,7 +40,7 @@ export const getUserSettings = (group: string) => { 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); + return createPostRequest(endpoint, { "Content-Type" : "application/json" })(data); }; export const getOrganizations = (group: string) => { @@ -77,4 +77,50 @@ export const getSelectedTermLabel = async (searchTerm: string): Promise { + try { + const endpoint = `/${group}${API_CONFIG.REAL_API.CREATE_NEW_ENTITY}`; + const response = await createPostRequest( + endpoint, + { "Content-Type" : "application/x-www-form-urlencoded" } + )(data); + + // If the response is HTML (a string), extract TMP ID + if (typeof response === "string") { + const match = response.match(/TMP:\d{9}/); + if (match) { + return { + term: { + id: `${match[0]}`, + }, + raw: response, + status: 200, + }; + } + } + + // Otherwise, return response as-is + return response; + } catch (error) { + if (error?.response.status === 409) { + const match = error?.response?.data?.existing?.[0]; + if (match) { + return { + term: { + id: `${match}`, + }, + raw: error?.response, + status: error?.response?.status, + }; + } + } + + return { + raw: error?.response, + status: error?.response?.status, + }; + } + }; \ No newline at end of file diff --git a/src/api/endpoints/index.ts b/src/api/endpoints/index.ts index 5eb900e9..3ee1f8ee 100644 --- a/src/api/endpoints/index.ts +++ b/src/api/endpoints/index.ts @@ -126,11 +126,11 @@ export const getCuries = async (term) => { }); } -export const getMatchTerms = async (term, filters = {}) => { +export const getMatchTerms = async (group, term, filters = {}) => { const { getEndpointsIlx } = useApi(); /** Call Endpoint */ - return getEndpointsIlx(BASE_GROUP,term, BASE_EXTENSION).then((data) => { + return getEndpointsIlx(group,term, BASE_EXTENSION).then((data) => { return termParser(data, term); }) .catch((error) => { @@ -270,23 +270,22 @@ export const getRawData = async (group, termID, format) => { }); } -export const addTerm = async (group, term) => { +export const addTerm = async (user: string, token: string, session: string, term: { label: string; synonyms: string[] }) => { const { postPrivEntityNew } = useApi(); - /** Call Endpoint */ - return postPrivEntityNew(group, term).then((data) => { - let termParsed = getTerm(data.data); - let response = { - status : data.status, - term : termParsed - } + const headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + }; + + const body = { + 'rdf-type': 'owl:Class', + label: term.label, + exact: term.synonyms, + }; + + return await postPrivEntityNew(user, body, { headers }); +}; - return response; - }) - .catch((error) => { - return error; - }); -} export const bulkEditTerms = async (group, payload) => { const { bulkEditTerms } = useMockApi(); @@ -330,14 +329,11 @@ export const getUser = async (id) => { }); } -export const getExistingIDs = async () => { - const { getMatchTerms } = useMockApi(); - +export const getExistingIDs = async (searchTerm) => { /** Call Endpoint */ - return getMatchTerms("base", "*").then((data) => { - const terms = termParser(data, undefined); - let existingIds = terms?.results?.map( term => term.id?.split("/").pop() ); - return terms?.results?.[0]?.id != undefined ? existingIds : []; + return elasticSearch(searchTerm).then((data) => { + const terms = data?.results; + return terms != undefined ? terms : []; }) .catch((error) => { return error; diff --git a/src/api/endpoints/interLexURIStructureAPI.ts b/src/api/endpoints/interLexURIStructureAPI.ts index 8c28cda5..61d7feb2 100644 --- a/src/api/endpoints/interLexURIStructureAPI.ts +++ b/src/api/endpoints/interLexURIStructureAPI.ts @@ -7565,16 +7565,21 @@ after the label and exact synonyms are done an no matches confirmed the user sho * @summary The workflow we want for this is a bit more complex than a simple form */ export const postPrivEntityNew = ( - group: string, - options?: SecondParameter,) => { - - - return customInstance( - {url: `https://uri.olympiangods.org/${group}/priv/entity-new`, method: 'POST' + group: string, + data: any, + options?: SecondParameter & { headers?: Record } +) => { + return customInstance( + { + url: `https://uri.olympiangods.org/${group}/priv/entity-new`, + method: 'POST', + data, + headers: options?.headers, }, - options); - } - + options + ); +}; + export const getPostPrivEntityNewMutationOptions = , @@ -9727,7 +9732,7 @@ export const getEndpointsIlx = ( return customInstance( - {url: `https://uri.olympiangods.org/${group}/${fragPrefId}.${extension}`, method: 'GET', signal + {url: `/${group}/${fragPrefId}.${extension}`, method: 'GET', signal }, options); } diff --git a/src/components/Auth/Login.jsx b/src/components/Auth/Login.jsx index edd7d4a4..e583b4f9 100644 --- a/src/components/Auth/Login.jsx +++ b/src/components/Auth/Login.jsx @@ -113,6 +113,7 @@ const Login = () => { value: sessionCookie.value, expires: expires })); + localStorage.setItem("token", sessionCookie.value) setUserData({ name: userData['groupname'], id: userData['orcid'], diff --git a/src/components/Dashboard/EditBulkTerms/TermsTable.jsx b/src/components/Dashboard/EditBulkTerms/TermsTable.jsx index 13e6cfe3..5071f919 100644 --- a/src/components/Dashboard/EditBulkTerms/TermsTable.jsx +++ b/src/components/Dashboard/EditBulkTerms/TermsTable.jsx @@ -89,7 +89,7 @@ const TermsTable = ({ setOpenEditAttributes, setAttributes, attributes, searchCo React.useEffect(() => { setLoading(true) - getMatchTerms("i", { filters }).then(data => { + getMatchTerms("base", "i", { filters }).then(data => { setTerms(data.results); setLoading(false); }).catch(err => { diff --git a/src/components/SingleTermView/OverView/CustomizedTable.jsx b/src/components/SingleTermView/OverView/CustomizedTable.jsx index 0a56004b..fc0fbec3 100644 --- a/src/components/SingleTermView/OverView/CustomizedTable.jsx +++ b/src/components/SingleTermView/OverView/CustomizedTable.jsx @@ -9,6 +9,8 @@ import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; import AddOutlinedIcon from '@mui/icons-material/AddOutlined'; import { useCallback, useEffect, useRef, useState } from "react"; import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'; +import { GlobalDataContext } from "../../../contexts/DataContext"; +import { useContext } from "react"; import { vars } from "../../../theme/variables"; const { gray100, gray50, gray600, gray500, brand600, brand50, brand700, gray700 } = vars; @@ -156,6 +158,7 @@ const CustomizedTable = ({ data, term, isAddButtonVisible }) => { // eslint-disable-next-line no-unused-vars const [deletedObj, setDeletedObj] = useState({}); const [editTermDialogOpen, setEditTermDialogOpen] = useState(false); + const { user } = useContext(GlobalDataContext); const targetRow = useRef(); const sourceRow = useRef(); @@ -245,7 +248,7 @@ const CustomizedTable = ({ data, term, isAddButtonVisible }) => { // eslint-disable-next-line react-hooks/exhaustive-deps const fetchTerms = useCallback(debounce(async (searchTerm) => { - const data = await getMatchTerms(searchTerm); + const data = await getMatchTerms(user?.groupname, searchTerm); setTerms(data?.results[0]); }, 500), [getMatchTerms]); diff --git a/src/components/SingleTermView/OverView/OverView.jsx b/src/components/SingleTermView/OverView/OverView.jsx index 468c71b0..28c36d11 100644 --- a/src/components/SingleTermView/OverView/OverView.jsx +++ b/src/components/SingleTermView/OverView/OverView.jsx @@ -20,8 +20,8 @@ const OverView = ({ searchTerm, isCodeViewVisible, selectedDataFormat }) => { const fetchTerms = useCallback( debounce((searchTerm) => { if (searchTerm) { - getMatchTerms(searchTerm).then(data => { - setData(data?.results[0]); + getMatchTerms("base", searchTerm).then(data => { + setData(data?.results?.[0]); setLoading(false); }); } @@ -43,7 +43,7 @@ const OverView = ({ searchTerm, isCodeViewVisible, selectedDataFormat }) => { - {isCodeViewVisible ? : + {isCodeViewVisible ? : <>
diff --git a/src/components/SingleTermView/OverView/PredicateGroupInput.jsx b/src/components/SingleTermView/OverView/PredicateGroupInput.jsx index 0e2caa49..d18cc5b2 100644 --- a/src/components/SingleTermView/OverView/PredicateGroupInput.jsx +++ b/src/components/SingleTermView/OverView/PredicateGroupInput.jsx @@ -39,7 +39,7 @@ const PredicateGroupInput = ({ predicate, onChange }) => { // eslint-disable-next-line react-hooks/exhaustive-deps const fetchTerms = useCallback(debounce(async (searchTerm) => { - const data = await getMatchTerms(searchTerm); + const data = await getMatchTerms("base", searchTerm); setTerms(data?.results); }, 500), [getMatchTerms]); diff --git a/src/components/SingleTermView/OverView/RawDataViewer.jsx b/src/components/SingleTermView/OverView/RawDataViewer.jsx index c84ad0d6..421d2d16 100644 --- a/src/components/SingleTermView/OverView/RawDataViewer.jsx +++ b/src/components/SingleTermView/OverView/RawDataViewer.jsx @@ -3,8 +3,6 @@ import { useState, useEffect } from 'react'; import { Light as SyntaxHighlighter } from 'react-syntax-highlighter'; import { a11yLight } from 'react-syntax-highlighter/dist/esm/styles/hljs'; import { getRawData } from "../../../api/endpoints"; -import { GlobalDataContext } from "../../../contexts/DataContext"; -import { useContext } from "react"; import { vars } from '../../../theme/variables'; const { gray25, gray200, gray500 } = vars; @@ -30,14 +28,12 @@ const RawDataViewer = ({ dataId, dataFormat }) => { const [formattedData, setFormattedData] = useState(null); // eslint-disable-next-line no-unused-vars const [loading, setLoading] = useState(true); - const { user } = useContext(GlobalDataContext); useEffect(() => { - getRawData(user?.name || "base",dataId, formatExtensions[dataFormat]).then( rawResponse => { + getRawData("base",dataId, formatExtensions[dataFormat]).then( rawResponse => { setFormattedData(JSON.stringify(rawResponse, null, 2)); setLoading(false) }) - // eslint-disable-next-line react-hooks/exhaustive-deps }, [dataId, dataFormat]); return ( diff --git a/src/components/SingleTermView/RequestMergeChanges.jsx b/src/components/SingleTermView/RequestMergeChanges.jsx index d892a891..4fb02a61 100644 --- a/src/components/SingleTermView/RequestMergeChanges.jsx +++ b/src/components/SingleTermView/RequestMergeChanges.jsx @@ -38,8 +38,8 @@ const RequestMergeChanges = ({ searchTerm, open, handleClose }) => { const fetchTerms = useCallback( debounce((searchTerm) => { if (searchTerm) { - getMatchTerms(searchTerm).then(data => { - setData(data?.results[0]); + getMatchTerms("base", searchTerm).then(data => { + setData(data?.results?.[0]); setLoading(false); }); getVariant("base", "ILX_....").then(data => { diff --git a/src/components/TermEditor/AddNewTermDialogContent.jsx b/src/components/TermEditor/AddNewTermDialogContent.jsx index 7c11140e..41426fa2 100644 --- a/src/components/TermEditor/AddNewTermDialogContent.jsx +++ b/src/components/TermEditor/AddNewTermDialogContent.jsx @@ -3,26 +3,23 @@ import PropTypes from "prop-types"; import { Box } from "@mui/material"; import ImportFileTab from "./ImportFileTab"; import BasicTabs from "../common/CustomTabs"; -import { addTerm } from "../../api/endpoints"; import NewTermSidebar from "./NewTermSidebar"; import StatusStep from "../common/StatusStep"; import { useNavigate } from "react-router-dom"; import ManualImportTab from "./ManualImportTab"; import AddPredicatesStep from "./AddPredicatesStep"; import { getAddTermStatusProps } from "./termStatusProps"; -import { termParser } from "../../../src/parsers/termParser"; import AddOutlinedIcon from "@mui/icons-material/AddOutlined"; import { getExistingIDs, getUser } from "../../api/endpoints"; import { useState, useEffect, useMemo, useCallback } from "react"; -import * as mockApi from "../../api/endpoints/swaggerMockMissingEndpoints"; -import * as mockApiInterlex from "../../api/endpoints/interLexURIStructureAPI"; +import { getEndpointsIlx, elasticSearch } from './../../api/endpoints/index'; +import { GlobalDataContext } from "../../contexts/DataContext"; +import { useContext } from "react"; +import { createNewEntity } from './../../api/endpoints/apiService' import { vars } from "../../theme/variables"; const { gray800, gray700 } = vars; -const useMockApi = () => mockApi; -const useMockApiInterlex = () => mockApiInterlex; - const initialFormState = { label: "", synonyms: [], @@ -34,7 +31,7 @@ const initialFormState = { } const formatIdText = (termId) => { - const [prefix, suffix] = termId.split('_'); + const [prefix, suffix] = termId.split(':'); return (
@@ -47,17 +44,15 @@ const formatIdText = (termId) => { ); } -const AddNewTermDialogContent = ({ activeStep, areMatchesChecked, onMatchesChange, onReset }) => { +const AddNewTermDialogContent = ({ activeStep, areMatchesChecked, onMatchesChange, onReset, onClose }) => { - const { getMatchTerms } = useMockApi(); - const { getEndpointsIlx } = useMockApiInterlex(); const [loading, setLoading] = useState(true); const navigate = useNavigate(); const [termResults, setTermResults] = useState([]); const [tabValue, setTabValue] = useState(0); const [openSidebar, setOpenSidebar] = useState(true); - const [data, setData] = useState(null); - const [responseStatus, setResponseStatus] = useState(null) + const [data] = useState(null); + const [addTermResponse, setAddTermResponse] = useState(null) const [termValue, setTermValue] = useState(''); const [ids, setIds] = useState([]); const [predicates, setPredicates] = useState([{ subject: '', predicate: '', object: { type: 'Object', value: '', isLink: false } }]); @@ -65,6 +60,7 @@ const AddNewTermDialogContent = ({ activeStep, areMatchesChecked, onMatchesChang const [url, setUrl] = useState(''); const [formState, setFormState] = useState(initialFormState); const [newTermId, setNewTermId] = useState(""); + const { user } = useContext(GlobalDataContext); const memoData = useMemo(() => data, [data]); @@ -72,39 +68,38 @@ const AddNewTermDialogContent = ({ activeStep, areMatchesChecked, onMatchesChang const fetchTerms = useCallback( debounce((termValue) => { setLoading(true); - if (termValue) { - getEndpointsIlx("base", termValue).then(data => { - const parsedData = termParser(data); - setData(parsedData?.results[0]); - setLoading(false); - }); - } else { - getMatchTerms("base", "i", { filter: "", value: "" }).then(data => { - const parsedData = termParser(data, ""); - setTermResults(parsedData.results); - setLoading(false); - }); - } + elasticSearch(termValue).then(data => { + setTermResults(data.results?.results); + setLoading(false); + }); }, 300), - [getEndpointsIlx, getMatchTerms] + [getEndpointsIlx, elasticSearch] ); const addTermRequest = useCallback(async (group, term) => { - await addTerm("base", term).then((response) => { - console.log("Term added ", response) - setNewTermId(response.term.id.split("/").pop()) - }) - .catch((error) => { - console.log("Error ", error) - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [addTerm]); + const token = localStorage.getItem("token"); + const groupName = user?.groupname || group; + const body = { + 'rdf-type': term.superClass || 'owl:Class', + label: term.label, + exact: term.synonyms?.map( s => s.label), + }; + + try { + const response = await createNewEntity({ group: groupName, data: body, session: token }); + setAddTermResponse(response); + setNewTermId(response.term.id.split("/").pop()); + } catch (error) { + setAddTermResponse(error); + } + }, [user]); const handleChangeTabs = (_, newValue) => setTabValue(newValue); const handleSidebarToggle = () => setOpenSidebar(!openSidebar); const handleAddNewTerm = () => { onReset(); setTermValue(''); + setAddTermResponse(null) setIds([]); setFormState(initialFormState) } @@ -147,15 +142,16 @@ const AddNewTermDialogContent = ({ activeStep, areMatchesChecked, onMatchesChang } const handleGoToTermClick = () => { - navigate(`/view?searchTerm=${termValue.charAt(0).toUpperCase() + termValue.slice(1)}`); + navigate(`/view?searchTerm=${newTermId}`); + onClose() } useEffect(() => { - getMatchTerms("base", "i", { filter: "", value: "" }).then(data => { - const parsedData = termParser(data, termValue); - setTermResults(parsedData.results); + elasticSearch(termValue, 20, 0).then(data => { + setTermResults(data.results?.results); + setLoading(false); }); - }, [termValue, getMatchTerms]); + }, [termValue]); useEffect(() => { fetchTerms(termValue); @@ -171,14 +167,14 @@ const AddNewTermDialogContent = ({ activeStep, areMatchesChecked, onMatchesChang }, [memoData]); // eslint-disable-next-line react-hooks/exhaustive-deps - const getIds = useCallback(debounce(async () => { - const ids = await getExistingIDs(); - setIds(ids) + const getIds = useCallback(debounce(async (termValue) => { + const ids = await getExistingIDs(termValue || "a"); + setIds(ids?.results) }), [getUser]); useEffect(() => { - getIds(); - }, [getIds]); + getIds(termValue); + }, [termValue,getIds]); useEffect(() => { if (activeStep === 2) { @@ -186,41 +182,19 @@ const AddNewTermDialogContent = ({ activeStep, areMatchesChecked, onMatchesChang } }, [addTermRequest, formState, activeStep]); - //can be deleted, use only for testing purposes - useEffect(() => { - const fetchData = async () => { - try { - const response = await fetch('/api/some-endpoint'); - if (!response.ok) { - throw new Error('HTTP error'); - } - const data = await response.json(); - setResponseStatus({ success: true, data }); - } catch (error) { - // should be success: false, but true for now so we can wee success status message - setResponseStatus({ success: true, error: error.message }); - } finally { - setLoading(false); - } - }; - - fetchData(); - }, []); - - const predicatesOptions = predicates.map(row => ({ label: row.title, value: row.title })); - const isResultsEmpty = termResults.length === 0; - const isLabelEmpty = formState.label === ""; + const isResultsEmpty = termResults?.length === 0; + const isLabelEmpty = formState?.label === ""; // eslint-disable-next-line no-unused-vars const isContinueButtonDisabled = !areMatchesChecked || isLabelEmpty const formattedNewTermId = formatIdText(newTermId); - const statusProps = getAddTermStatusProps(responseStatus, termValue); + const statusProps = getAddTermStatusProps(addTermResponse, termValue); return ( <> @@ -247,10 +221,10 @@ const AddNewTermDialogContent = ({ activeStep, areMatchesChecked, onMatchesChang )} {activeStep === 1 && } - {activeStep === 2 && console.log("Try again")} + onTryAgain={handleAddNewTerm} onClose={handleGoToTermClick} actionButtonStartIcon={} additionalInfo={formattedNewTermId} @@ -263,7 +237,8 @@ AddNewTermDialogContent.propTypes = { activeStep: PropTypes.number.isRequired, areMatchesChecked: PropTypes.bool.isRequired, onMatchesChange: PropTypes.func.isRequired, - onReset: PropTypes.func.isRequired + onReset: PropTypes.func.isRequired, + onClose: PropTypes.func.isRequired, }; export default AddNewTermDialogContent; diff --git a/src/components/TermEditor/ExistingIdsSearch.tsx b/src/components/TermEditor/ExistingIdsSearch.tsx index 0543ffd6..5504f802 100644 --- a/src/components/TermEditor/ExistingIdsSearch.tsx +++ b/src/components/TermEditor/ExistingIdsSearch.tsx @@ -19,7 +19,8 @@ const ExistingIdsSearch = ({ options, value, onChange, label, placeholder }) => <> {label} )} - {open && ( + {(open && results )&& ( {isResultsEmpty ? ( @@ -60,7 +60,7 @@ export default function NewTermSidebar({ open, loading, onToggle, results, isRes ) : ( - <>{results.map((result) => ( + <>{results?.map((result) => ( areMatchesChecked={areMatchesChecked} onMatchesChange={handleMatchesChange} onReset={handleReset} + onClose={handleCancelBtnClick} /> )} diff --git a/src/components/TermEditor/termStatusProps.js b/src/components/TermEditor/termStatusProps.js index ff9f0f0d..1ba93906 100644 --- a/src/components/TermEditor/termStatusProps.js +++ b/src/components/TermEditor/termStatusProps.js @@ -1,12 +1,14 @@ -export const getAddTermStatusProps = (responseStatus, termValue) => ({ - statusResponse: responseStatus, +export const getAddTermStatusProps = (response, termValue) => ({ + statusResponse: response, + success : response?.status < 400, + error : response?.status > 400 , successMessage: "Term successfully created", - failureMessage: "Unable to create the term", + failureMessage: "Unable to create the term : " + response?.data?.error, successDescription: `Your term “${termValue.charAt(0).toUpperCase() + termValue.slice(1)}” has been added. Go to your term or add a new one.`, - failureDescription: `Your term “${termValue.charAt(0).toUpperCase() + termValue.slice(1)}” has can’t be added. Close or try again.`, - actionButtonMessage: responseStatus?.success ? "Add a new term" : null, - isCloseButtonVisible: responseStatus?.success ? false : true, - isTryButtonVisible: responseStatus?.success ? false : true, + failureDescription: `Your term “${termValue.charAt(0).toUpperCase() + termValue.slice(1)}” has can’t be added. Close or try again.` , + actionButtonMessage: response?.status < 400 ? "Add a new term" : null, + isCloseButtonVisible: response?.status < 400 ? false : true, + isTryButtonVisible: response?.status < 400 ? false : true, }); export const getTermStatusProps = (responseStatus, termValue) => ({ diff --git a/src/components/common/StatusStep.jsx b/src/components/common/StatusStep.jsx index 4a1ee49f..1915d985 100644 --- a/src/components/common/StatusStep.jsx +++ b/src/components/common/StatusStep.jsx @@ -5,7 +5,7 @@ import { StatusErrorBackgroundPattern, AddedSuccessfully } from "../../Icons"; import { vars } from "../../theme/variables"; const { gray900, gray600 } = vars; -const StatusBackground = ({ responseStatus }) => ( +const StatusBackground = ({ success }) => ( ( zIndex: 1, }} > - {responseStatus?.success ? : } + {success ? : } ); StatusBackground.propTypes = { - responseStatus: PropTypes.object, + success: PropTypes.bool, }; const StatusMessage = ({ message, description, additionalInfo }) => ( @@ -80,7 +80,7 @@ ActionButtons.propTypes = { const StatusStep = ({ statusProps, onAction, onTryAgain, onClose, actionButtonStartIcon, additionalInfo }) => { const { - statusResponse, + success, successMessage, successDescription, failureMessage, @@ -90,17 +90,13 @@ const StatusStep = ({ statusProps, onAction, onTryAgain, onClose, actionButtonSt isCloseButtonVisible, } = statusProps; - const message = statusResponse?.success + const message = success ? successMessage - : statusResponse?.error - ? failureMessage - : ''; + : failureMessage - const description = statusResponse?.success + const description = success ? successDescription - : statusResponse?.error - ? failureDescription - : ''; + : failureDescription return ( - + { console.log(_options); proxy.on('error', (err, _req, _res) => { @@ -51,15 +54,48 @@ export default defineConfig({ }); proxy.on('proxyReq', (proxyReq, req, _res) => { console.log('Sending Request to the Target:', req.method, req.url); + console.log('Headers sent to backend:', proxyReq.getHeaders()); 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); + proxy.on('proxyRes', (proxyRes, req, res) => { + const location = proxyRes.headers['location']; + console.log('Received location', location); + + if (proxyRes.statusCode === 303 && location) { + // Prevent browser from seeing the actual Location + delete proxyRes.headers['location']; + + // Inject the location into a custom header we can use in Axios + res.setHeader('X-Redirect-Location', location); + } + + // Required for credentialed CORS + const origin = req.headers.origin; + if (origin) { + res.setHeader('Access-Control-Allow-Origin', origin); + } + res.setHeader('Access-Control-Allow-Credentials', 'true'); + res.setHeader('Access-Control-Expose-Headers', 'X-Redirect-Location'); }); + }, }, + '^/[^/]+/(tmp|ilx)_.*\\.(html|ttl|jsonld|n3|owl|csv)$': { + target: 'https://uri.olympiangods.org', + changeOrigin: true, + secure: false, + rewrite: path => path, // Keep full path intact + configure: (proxy) => { + proxy.on('proxyRes', (proxyRes, req, res) => { + const origin = req.headers.origin; + if (origin) { + res.setHeader('Access-Control-Allow-Origin', origin); + } + res.setHeader('Access-Control-Allow-Credentials', 'true'); + }); + }, + } }, }, });