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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions public/locales/fr/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@
"There seems to be no question for this category.": "Il semble ne pas y avoir de question pour cette catégorie",
"Oh, it seems that this help page is still under development and does not yet contain any content.": "Oh, il semble que cette page d'aide soit encore en cours de développement et ne contienne pas encore de contenu.",
"This record already exists.": "Cet élément est déjà présent.",
"Search by <DOI> or <Title>": "Recherche par <DOI> ou <Titre>"
"Glossary": "Glossaire",
"Oh, it seems that this glossary page is still under development and does not yet contain any content.": "Oh, il semble que cette page de glossaire soit encore en cours de développement et ne contienne pas encore de contenu.",
"There don't seem to be any terms for this letter.": "Il ne semble pas y avoir de termes pour cette lettre",
Expand Down
7 changes: 4 additions & 3 deletions src/components/ExternalImport/ImportExternal.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useState } from "react";
import OrcidList from "./OrcidList";
import RorList from "./RorList";
import { OrcidList, RorList, Metadore } from "./";
import { useTranslation } from "react-i18next";

function ImportExternal({ fragment, setFragment, externalImports = {} }) {
Expand All @@ -26,7 +25,8 @@ function ImportExternal({ fragment, setFragment, externalImports = {} }) {
const externalImportComponent = (type, fragment, setFragment) => {
const importComponents = {
ror: (fragment, setFragment, mapping) => <RorList key={`${type}-import-component`} fragment={fragment} setFragment={setFragment} mapping={mapping} />,
orcid: (fragment, setFragment, mapping) => <OrcidList key={`${type}-import-component`} fragment={fragment} setFragment={setFragment} mapping={mapping} />
orcid: (fragment, setFragment, mapping) => <OrcidList key={`${type}-import-component`} fragment={fragment} setFragment={setFragment} mapping={mapping} />,
metadore: (fragment, setFragment, mapping) => <Metadore key={`${type}-import-component`} fragment={fragment} setFragment={setFragment} mapping={mapping} />
};

return (
Expand All @@ -45,6 +45,7 @@ function ImportExternal({ fragment, setFragment, externalImports = {} }) {
const buttons = {
ror: t('Retrieve ROR identifier'),
orcid: t('Retrieve ORCID identifier'),
metadore: t('Retrieve data by using Datacite'),
};

return (
Expand Down
192 changes: 192 additions & 0 deletions src/components/ExternalImport/Metadore.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { externalServices } from "../../services";
import Select from "react-select";
import CustomSpinner from "../Shared/CustomSpinner";
import CustomError from "../Shared/CustomError";
import Pagination from "../Shared/Pagination";
import { FaLink } from "react-icons/fa6";
import { FaCheckCircle, FaPlusSquare } from "react-icons/fa";

function Metadore({ fragment, setFragment, mapping = {} }) {
const { t } = useTranslation();
const pageSize = 8;
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [selectedData, setSelectedData] = useState(null);
const [currentData, setCurrentData] = useState([]);
const [text, setText] = useState('');

/**
* The function `getData` makes an API call to get data, sets the retrieved data in state variables, and creates an array of distinct countries from the
* data.
*/
const getData = async (query) => {
setLoading(true);

let response;
try {
response = await externalServices.getMetadore(query);
} catch (error) {
setError(error);
return setLoading(false);
}

const { data: resData } = response.data;

setData(resData);

if (resData.length === 0) { setCurrentData([]); }

setLoading(false);
};

/**
* The onChangePage function updates the state with a new page of items.
*/
const onChangePage = (pageOfItems) => {
// update state with new page of items
setCurrentData(pageOfItems);
};

/**
* The function `setSelectedValue` updates the selected key and sets a temporary object with affiliation information.
*/
const setSelectedValue = (el) => {
setSelectedData(selectedData === el.id ? null : el.id);
let obj = {};

if (mapping && Object.keys(mapping)?.length > 0) {
obj = Object.entries(obj).reduce((acc, [key, value]) => {
const objKey = mapping?.[key] || key;
if (!(objKey in mapping)) {
acc[objKey] = value;
}
return acc;
}, {});
}

setFragment({ ...fragment, ...obj });
};

/**
* The handleSearchTerm function filters data based on a text input value and updates the state with the filtered results.
*/
const handleSearchTerm = () => getData(text);

/**
* The handleKeyDown function fetch the data when the user uses the Enter button in the search field.
*/
const handleKeyDown = (e) => {
if (e.key === 'Enter') {
return getData(text);
}
return null;
}

/**
* The function `handleDeleteText` clears the text and then retrieves data.
*/
const handleDeleteText = () => {
setText('');
setData([]);
setCurrentData([]);
};

return (
<div style={{ position: 'relative' }}>
{error && <CustomError />}
{!error && (
<>
<div className="row" style={{ margin: '10px' }}>
<div>
<div className="row">
<div>
<div className="input-group">
<input
type="text"
className="form-control"
value={text}
onChange={(e) => setText(e.target.value)}
onKeyDown={(e) => handleKeyDown(e)}
placeholder={t('Search by <DOI> or <Title>')}
style={{ borderRadius: '8px 0 0 8px', borderWidth: '1px', borderColor: 'var(--dark-blue)', height: '43px' }}
/>
<span className="input-group-btn">
<button
className="btn btn-default"
type="button"
onClick={handleSearchTerm}
style={{ borderRadius: '0', borderWidth: '1px', borderColor: 'var(--dark-blue)', height: '43px', margin: '0' }}
>
<span className="fas fa-magnifying-glass" style={{ color: "var(--dark-blue)" }} />
</button>
</span>
<span className="input-group-btn">
<button
className="btn btn-default"
type="button"
onClick={handleDeleteText}
style={{ borderRadius: '0 8px 8px 0', borderWidth: '1px', borderColor: 'var(--dark-blue)', height: '43px', margin: '0' }}
>
<span className="fa fa-xmark" />
</button>
</span>
</div>
</div>
</div>
</div>
</div>

<table className="table table-bordered table-hover">
<thead className="thead-dark">
<tr>
<th scope="col"></th>
<th scope="col">{t('DOI')}</th>
<th scope="col">{t('Title')}</th>
</tr>
</thead>
<tbody>
{currentData.length > 0 ? currentData.map((el, idx) => (
<tr key={idx}>
<td>
{selectedData === el.id ?
<FaCheckCircle
className="text-center"
style={{ color: 'green' }}
/> :
<FaPlusSquare
className="text-center"
style={{ cursor: 'pointer' }}
onClick={() => setSelectedValue(el)} />
}
</td>
<td>{el.id}</td>
<td>{el.attributes.titles.at(0).title}</td>
</tr>
)) : (
<tr>
<td colSpan="5" style={{ textAlign: loading ? 'center': 'left' }}>
{ loading ? <CustomSpinner /> : t('No data available') }
</td>
</tr>
)}
</tbody>
</table>

{data.length > 0 && (
<div className="row text-center">
<div className="mx-auto"></div>
<div className="mx-auto">
<Pagination items={data} onChangePage={onChangePage} pageSize={pageSize} />
</div>
</div>
)}
</>
)}
</div>
);
}

export default Metadore;
3 changes: 2 additions & 1 deletion src/components/ExternalImport/index.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import ExternalImport from "./ImportExternal";
import Orcid from "./OrcidList";
import Ror from "./RorList";
import Metadore from "./Metadore";

export { ExternalImport, Orcid, Ror };
export { ExternalImport, Orcid, Ror, Metadore };
3 changes: 3 additions & 0 deletions src/services/externalServices.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ const getRor = async (query, filter) => axios.get("/api/v1/madmp/services/ror",

const getOrcid = async (search) => axios.get("/api/v1/madmp/services/orcid", { params: { search } });

const getMetadore = async (query) => axios.get("/api/v1/madmp/services/metadore", { params: { query } });

// eslint-disable-next-line import/no-anonymous-default-export
export default {
getRor,
getOrcid,
getMetadore,
};