diff --git a/client/src/config.js b/client/src/config.js new file mode 100644 index 00000000..34b11850 --- /dev/null +++ b/client/src/config.js @@ -0,0 +1,28 @@ +const status = { + validated: { + badgeType: 'success', + buttonClassName: 'btn-keep', + buttonIcon: 'ri-checkbox-circle-line', + buttonLabel: 'Validate', + id: 'validated', + label: 'Validated', + }, + excluded: { + badgeType: 'error', + buttonClassName: 'btn-hide', + buttonIcon: 'ri-indeterminate-circle-line', + buttonLabel: 'Exclude', + id: 'excluded', + label: 'Excluded', + }, + tobedecided: { + badgeType: 'info', + buttonClassName: 'btn-reset', + buttonIcon: 'ri-reply-fill', + buttonLabel: 'Reset status', + id: 'tobedecided', + label: 'To be decided', + }, +}; + +export { status }; diff --git a/client/src/pages/home/actions.jsx b/client/src/pages/home/actions.jsx index bfdce41d..62edfc78 100644 --- a/client/src/pages/home/actions.jsx +++ b/client/src/pages/home/actions.jsx @@ -6,6 +6,7 @@ import { Tooltip } from 'react-tooltip'; import Button from '../../components/button'; import { export2BsoCsv, export2json, importJson } from '../../utils/file'; +import { status } from '../../config'; export default function Actions({ allAffiliations, @@ -18,7 +19,7 @@ export default function Actions({ const [, setSearchParams] = useSearchParams(); const [displayFileUpload, setDisplayFileUpload] = useState(false); - const decidedAffiliations = allAffiliations.filter((affiliation) => ['excluded', 'validated'].includes(affiliation.status)); + const decidedAffiliations = allAffiliations.filter((affiliation) => affiliation.status !== status.tobedecided.id); return ( <> diff --git a/client/src/pages/home/index.jsx b/client/src/pages/home/index.jsx index 0670530c..bbddd3df 100644 --- a/client/src/pages/home/index.jsx +++ b/client/src/pages/home/index.jsx @@ -27,6 +27,7 @@ import { getOpenAlexPublications, mergePublications, } from '../../utils/works'; +import { status } from '../../config'; import 'primereact/resources/primereact.min.css'; import 'primereact/resources/themes/lara-light-indigo/theme.css'; @@ -39,9 +40,6 @@ const { const DATASOURCES = [{ key: 'bso', label: 'French OSM' }, { key: 'openalex', label: 'OpenAlex' }]; const FOSM_IDENTIFIERS = ['crossref', 'hal_id', 'datacite']; -const STATUS_EXCLUDED = 'excluded'; -const STATUS_TO_BE_DECIDED = 'to be decided'; -const STATUS_VALIDATED = 'validated'; const getData = async (options) => { const promises1 = [getBsoWorks({ options, index: VITE_BSO_PUBLICATIONS_INDEX }), getOpenAlexPublications(options)]; @@ -119,10 +117,10 @@ export default function Home() { const groupByAffiliations = (publications) => { setIsLoading(true); // Save already decided affiliations - const decidedAffiliations = Object.values(allAffiliations).filter((affiliation) => affiliation.status !== STATUS_TO_BE_DECIDED); - // Compute distinct affiliations of the undecided publications + const decidedAffiliations = Object.values(allAffiliations).filter((affiliation) => affiliation.status !== status.tobedecided.id); + // Compute distinct affiliations of the undecided works let allAffiliationsTmp = {}; - publications.filter((publication) => publication.status === STATUS_TO_BE_DECIDED).forEach((publication) => { + publications.filter((publication) => publication.status === status.tobedecided.id).forEach((publication) => { (publication?.affiliations ?? []) .filter((affiliation) => Object.keys(affiliation).length && affiliation?.name) .forEach((affiliation) => { @@ -141,7 +139,7 @@ export default function Home() { nameHtml: affiliation.name.replace(regexp, '$&'), ror, rorHtml: ror?.replace(regexp, '$&'), - status: STATUS_TO_BE_DECIDED, + status: status.tobedecided.id, publications: [], }; } @@ -193,7 +191,7 @@ export default function Home() { allIdsHtml: getAllIdsHtmlField(dataset), authorsHtml: getAuthorsHtmlField(dataset), authorsTooltip: getAuthorsTooltipField(dataset), - status: STATUS_TO_BE_DECIDED, + status: status.tobedecided.id, })); allPublicationsTmp = data.publications .map((publication) => ({ @@ -203,7 +201,7 @@ export default function Home() { allIdsHtml: getAllIdsHtmlField(publication), authorsHtml: getAuthorsHtmlField(publication), authorsTooltip: getAuthorsTooltipField(publication), - status: STATUS_TO_BE_DECIDED, + status: status.tobedecided.id, })); } setAllDatasets(allDatasetsTmp); @@ -227,7 +225,7 @@ export default function Home() { // eslint-disable-next-line react-hooks/exhaustive-deps }, [allPublications, regexp]); - const tagPublications = (publications, action) => { + const tagWorks = (publications, action) => { const allPublicationsTmp = [...allPublications]; const publicationsIds = publications.map((publication) => publication.id); allPublicationsTmp.filter((publication) => publicationsIds.includes(publication.id)).map((publication) => publication.status = action); @@ -236,7 +234,7 @@ export default function Home() { }; const tagAffiliations = (affiliations, action) => { - if (action !== STATUS_EXCLUDED) { + if (action !== status.excluded.id) { const allPublicationsTmp = [...allPublications]; const publicationsIds = affiliations.map((affiliation) => affiliation.publications).flat(); allPublicationsTmp.filter((publication) => publicationsIds.includes(publication.id)).map((publication) => publication.status = action); @@ -249,73 +247,35 @@ export default function Home() { setSelectedAffiliations([]); }; - const renderAffiliationsButtons = () => ( + const renderAffiliationsButtons = (selected) => ( <> - - - + {Object.values(status).map((st) => ( + + ))} ); const renderWorksButtons = (selected) => ( <> - - - + {Object.values(status).map((st) => ( + + ))} ); @@ -391,15 +351,16 @@ export default function Home() { )} - {renderAffiliationsButtons()} + {renderAffiliationsButtons(selectedAffiliations)} affiliation.status === STATUS_TO_BE_DECIDED).length }, - { className: 'validated', id: 'validated', label: 'Validated', value: allAffiliations.filter((affiliation) => affiliation.status === STATUS_VALIDATED).length }, - { className: 'excluded', id: 'excluded', label: 'Excluded', value: allAffiliations.filter((affiliation) => affiliation.status === STATUS_EXCLUDED).length }, - ]} + data={Object.values(status).map((st) => ({ + className: st.id, + id: st.id, + label: st.label, + value: allAffiliations.filter((affiliation) => affiliation.status === st.id).length, + }))} /> @@ -417,7 +378,7 @@ export default function Home() { - {renderAffiliationsButtons()} + {renderAffiliationsButtons(selectedAffiliations)} @@ -428,11 +389,12 @@ export default function Home() { publication.status === STATUS_TO_BE_DECIDED).length }, - { className: 'validated', id: 'validated', label: 'Validated', value: allPublications.filter((publication) => publication.status === STATUS_VALIDATED).length }, - { className: 'excluded', id: 'excluded', label: 'Excluded', value: allPublications.filter((publication) => publication.status === STATUS_EXCLUDED).length }, - ]} + data={Object.keys(status).map((st) => ({ + className: status[st].id, + id: status[st].id, + label: status[st].label, + value: allPublications.filter((publication) => publication.status === status[st].id).length, + }))} /> @@ -442,7 +404,7 @@ export default function Home() { {DATASOURCES.map((datasource) => ( dataset.status === STATUS_TO_BE_DECIDED).length }, - { className: 'validated', id: 'validated', label: 'Validated', value: allDatasets.filter((dataset) => dataset.status === STATUS_VALIDATED).length }, - { className: 'excluded', id: 'excluded', label: 'Excluded', value: allDatasets.filter((dataset) => dataset.status === STATUS_EXCLUDED).length }, - ]} + data={Object.keys(status).map((st) => ({ + className: status[st].id, + id: status[st].id, + label: status[st].label, + value: allDatasets.filter((dataset) => dataset.status === status[st].id).length, + }))} /> diff --git a/client/src/utils/file.js b/client/src/utils/file.js index ff61d0a0..6894661a 100644 --- a/client/src/utils/file.js +++ b/client/src/utils/file.js @@ -1,6 +1,8 @@ +import { status } from '../config'; + const export2BsoCsv = (allPublications) => { const csvHeader = ['doi', 'hal_id', 'nnt_id'].join(';'); - const validatedPublications = allPublications.filter((publication) => publication.status === 'validated'); + const validatedPublications = allPublications.filter((publication) => publication.status === status.validated.id); const getValue = (row, idType) => { if (!row) return ''; if (row.doi && idType === 'doi') return row.doi; @@ -49,10 +51,10 @@ const importJson = (e, optionsInit, setAllAffiliations, setAllPublications, setS setAllPublications(allPublications); } if (decidedAffiliations) { - const validatedAffiliations = decidedAffiliations.filter((decidedAffiliation) => decidedAffiliation.status === 'validated'); - tagAffiliations(validatedAffiliations, 'validated'); - const excludedAffiliations = decidedAffiliations.filter((decidedAffiliation) => decidedAffiliation.status === 'excluded'); - tagAffiliations(excludedAffiliations, 'excluded'); + const validatedAffiliations = decidedAffiliations.filter((decidedAffiliation) => decidedAffiliation.status === status.validated.id); + tagAffiliations(validatedAffiliations, status.validated.id); + const excludedAffiliations = decidedAffiliations.filter((decidedAffiliation) => decidedAffiliation.status === status.excluded.id); + tagAffiliations(excludedAffiliations, status.excluded.id); } setSearchParams(options); }; diff --git a/client/src/utils/templates.jsx b/client/src/utils/templates.jsx index 07b5082a..847f840e 100644 --- a/client/src/utils/templates.jsx +++ b/client/src/utils/templates.jsx @@ -5,6 +5,7 @@ import { Dropdown } from 'primereact/dropdown'; import { Tooltip } from 'react-tooltip'; import { getIdLink } from './works'; +import { status } from '../config'; const affiliationsTemplate = (rowData) => ( <> @@ -88,30 +89,14 @@ const getAuthorsTooltipField = (rowData) => { const nameTemplate = (rowData) => ; -const getBadgeTypeByStatus = (status) => { - let type; - switch (status) { - case 'validated': - type = 'success'; - break; - case 'excluded': - type = 'error'; - break; - default: - type = 'info'; - break; - } - return type; -}; - -const statusTemplate = (rowData) => ; +const statusTemplate = (rowData) => ; const statusFilterTemplate = (options) => ( options.filterApplyCallback(e.value)} - options={['to be decided', 'validated', 'excluded']} + options={Object.keys(status)} placeholder="" style={{ width: '6rem' }} showClear