Skip to content

Commit

Permalink
fix(openalex): Try to improve UX ...
Browse files Browse the repository at this point in the history
  • Loading branch information
annelhote committed Dec 6, 2024
1 parent 73d2296 commit 20fb46a
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 111 deletions.
45 changes: 23 additions & 22 deletions client/src/pages/openalex-affiliations/results/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ModalContent,
ModalTitle,
Row,
Spinner,
Tag,
Text,
TextInput,
Expand Down Expand Up @@ -51,14 +52,14 @@ export default function Affiliations() {
status.validated.id,
status.excluded.id,
]);
const [isLoading, setIsLoading] = useState(false);
const [isLoadingRorData, setIsLoadingRorData] = useState(false); // TODO: spinner dans modal
const [isAddModalOpen, setIsAddModalOpen] = useState(false);
const [isRemoveModalOpen, setIsRemoveModalOpen] = useState(false);
const [ror, setRor] = useState('');
const [rorMessage, setRorMessage] = useState('');
const [rorMessageType, setRorMessageType] = useState('');
const [rorsToRemove, setRorsToRemove] = useState([]);
const [timer, setTimer] = useState();
const [uniqueRors, setUniqueRors] = useState({});

const { data, error, isFetched, isFetching, refetch } = useQuery({
Expand Down Expand Up @@ -172,26 +173,22 @@ export default function Affiliations() {
}, [data]);

useEffect(() => {
if (timer) clearTimeout(timer);
const timerTmp = setTimeout(() => {
const regex = new RegExp(removeDiacritics(filteredAffiliationName));
const filteredAffiliationsTmp = affiliations.filter(
(affiliation) => regex.test(
`${affiliation.key.replace('[ source: ', '').replace(' ]', '')} ${affiliation.rors.map((_ror) => _ror.rorId).join(' ')}`,
),
);
// Recompute corrections only when the array has changed
if (filteredAffiliationsTmp.length !== filteredAffiliations.length) {
setAllOpenalexCorrections([
...allOpenalexCorrections,
...getAffiliationsCorrections(filteredAffiliationsTmp),
]);
}
setFilteredAffiliations(filteredAffiliationsTmp);
}, 500);
setTimer(timerTmp);
// The timer should not be tracked
// eslint-disable-next-line react-hooks/exhaustive-deps
setIsLoading(true);
const regex = new RegExp(removeDiacritics(filteredAffiliationName));
const filteredAffiliationsTmp = affiliations.filter(
(affiliation) => regex.test(
`${affiliation.key.replace('[ source: ', '').replace(' ]', '')} ${affiliation.rors.map((_ror) => _ror.rorId).join(' ')}`,
),
);
// Recompute corrections only when the array has changed
if (filteredAffiliationsTmp.length !== filteredAffiliations.length) {
setAllOpenalexCorrections([
...allOpenalexCorrections,
...getAffiliationsCorrections(filteredAffiliationsTmp),
]);
}
setFilteredAffiliations(filteredAffiliationsTmp);
setIsLoading(false);
}, [affiliations, allOpenalexCorrections, filteredAffiliationName, filteredAffiliations.length, filteredStatus]);

useEffect(() => {
Expand Down Expand Up @@ -353,7 +350,7 @@ export default function Affiliations() {
<>
<Header id="openalex-tile-title" />
<Container fluid as="main" className="wm-bg">
{isFetching && (
{(isFetching || isLoading) && (
<Container
style={{ textAlign: 'center', minHeight: '600px' }}
className="fr-pt-5w wm-font"
Expand All @@ -372,6 +369,10 @@ export default function Affiliations() {
</Container>
)}

{isLoading && (
<Spinner size={48} />
)}

{error && (
<Row gutters className="fr-mb-16w">
<Col xs="12">
Expand Down
196 changes: 107 additions & 89 deletions client/src/pages/openalex-affiliations/results/list-view.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Col,
Modal, ModalContent, ModalFooter, ModalTitle,
Row,
Spinner,
Text,
} from '@dataesr/dsfr-plus';
import PropTypes from 'prop-types';
Expand All @@ -23,8 +24,10 @@ export default function ListView({
setSelectAffiliations,
toggleRemovedRor,
}) {
const [isLoading, setIsLoading] = useState(false);
const [isModalOpen, setIsModalOpen] = useState(false);
const [isColorInfoModalOpen, setIsColorInfoModalOpen] = useState(false);
const [search, setSearch] = useState('');
const [selectSortOnNumberOfRors, setSelectSortOnNumberOfRors] = useState('default');
const [selectShowAffiliations, setSelectShowAffiliations] = useState('all');
const [selectRorCountry, setSelectRorCountry] = useState('all');
Expand All @@ -51,6 +54,7 @@ export default function ListView({
defineRorColor.push(...sortedRor.slice(0, 5).map((ror, index) => ({ ror, color: dsColors[index % dsColors.length] })));

useEffect(() => {
setIsLoading(true);
// Deep copy of filteredAffiliations object
let initialAffiliations = JSON.parse(JSON.stringify(filteredAffiliations));
if (sortsAndFilters.sortOnNumberOfRors === 'numberASC') {
Expand All @@ -72,6 +76,7 @@ export default function ListView({
}

setSortedOrFilteredAffiliations(initialAffiliations);
setIsLoading(false);
}, [filteredAffiliations, sortsAndFilters]);

return (
Expand Down Expand Up @@ -100,22 +105,31 @@ export default function ListView({
<Col xs="7">
<span className="fr-icon-search-line fr-mx-1w" />
<input
onChange={(e) => setFilteredAffiliationName(e.target.value)}
onChange={(e) => setSearch(e.target.value)}
style={{
border: '1px solid #ced4da',
borderRadius: '4px',
padding: '0.375rem 0.75rem',
width: '600px',
backgroundColor: 'white',
}}
value={filteredAffiliationName}
value={search}
/>
<Button
className="fr-ml-1w"
color="blue-ecume"
disabled={!search.length}
onClick={() => setFilteredAffiliationName(search)}
size="sm"
>
Search in affiliations
</Button>
<button
aria-label="Clear search"
className=" fr-ml-1w"
disabled={!filteredAffiliationName.length}
disabled={!search.length}
icon="delete-line"
onClick={() => setFilteredAffiliationName('')}
onClick={() => { setSearch(''); setFilteredAffiliationName(''); }}
size="sm"
title="Clear search"
type="button"
Expand Down Expand Up @@ -206,92 +220,96 @@ export default function ListView({
</Col>
</Row>
</div>
<div>
<ul className="wm-list">
{
sortedOrFilteredAffiliations.map((affiliation) => (
<li
className={affiliation.selected ? 'selected' : ''}
key={affiliation.key}
>
<Row>
<Col>
<div style={{ display: 'inline-flex' }}>
<div style={{ display: 'inline-block', width: '20px' }}>
<Checkbox
checked={affiliation.selected}
name="affiliations"
onChange={() => setSelectAffiliations([affiliation.id])}
/>
<br />
{
(affiliation.hasCorrection) && (
<span className="fr-icon-warning-fill fr-icon--sm" style={{ color: '#B34000' }} />
)
}
</div>
<div className="fr-ml-1w" style={{ display: 'inline-block', maxWidth: '95%' }}>
<Text
as="span"
onClick={() => setSelectAffiliations([affiliation.id])}
style={{ cursor: 'pointer' }}
>
<div dangerouslySetInnerHTML={{ __html: affiliation.nameHtml.replace(' [ source: OpenAlex ]', '') }} />
</Text>
<WorksList works={affiliation.works} />
{isLoading ? (
<Spinner size={48} />
) : (
<div>
<ul className="wm-list">
{
sortedOrFilteredAffiliations.map((affiliation) => (
<li
className={affiliation.selected ? 'selected' : ''}
key={affiliation.key}
>
<Row>
<Col>
<div style={{ display: 'inline-flex' }}>
<div style={{ display: 'inline-block', width: '20px' }}>
<Checkbox
checked={affiliation.selected}
name="affiliations"
onChange={() => setSelectAffiliations([affiliation.id])}
/>
<br />
{
(affiliation.hasCorrection) && (
<span className="fr-icon-warning-fill fr-icon--sm" style={{ color: '#B34000' }} />
)
}
</div>
<div className="fr-ml-1w" style={{ display: 'inline-block', maxWidth: '95%' }}>
<Text
as="span"
onClick={() => setSelectAffiliations([affiliation.id])}
style={{ cursor: 'pointer' }}
>
<div dangerouslySetInnerHTML={{ __html: affiliation.nameHtml.replace(' [ source: OpenAlex ]', '') }} />
</Text>
<WorksList works={affiliation.works} />
</div>
</div>
</div>
</Col>
<Col md={4}>
<table className="wm-table">
<tbody>
{affiliation.rorsToCorrect.map((rorToCorrect) => (
<tr key={`openalex-affiliations-affiliations-${rorToCorrect.rorId}`}>
<td>
<RorBadge
isRemoved={affiliation.removeList.includes(rorToCorrect.rorId)}
ror={rorToCorrect}
rorColor={defineRorColor.find((item) => item.ror === rorToCorrect.rorId)?.color || 'beige-gris-galet'}
setFilteredAffiliationName={setFilteredAffiliationName}
removeRor={() => toggleRemovedRor(affiliation.id, rorToCorrect.rorId)}
/>
<br />
<RorName
isRemoved={affiliation.removeList.includes(rorToCorrect.rorId)}
ror={rorToCorrect}
/>
</td>
</tr>
))}
{affiliation.addList.map((ror) => (
<tr key={`openalex-affiliations-affiliations-${ror.rorId}`}>
<td>
<RorBadge
ror={ror}
rorColor={defineRorColor.find((item) => item.ror === ror.rorId)?.color || 'beige-gris-galet'}
setFilteredAffiliationName={setFilteredAffiliationName}
removeRor={() => removeRorFromAddList(affiliation.id, ror.rorId)}
/>
<br />
<RorName ror={ror} />
<Badge
className="fr-ml-1w"
color="blue-cumulus"
>
Added
</Badge>
</td>
</tr>
))}
</tbody>
</table>
</Col>
</Row>
</li>
))
}
</ul>
</div>
</Col>
<Col md={4}>
<table className="wm-table">
<tbody>
{affiliation.rorsToCorrect.map((rorToCorrect) => (
<tr key={`openalex-affiliations-affiliations-${rorToCorrect.rorId}`}>
<td>
<RorBadge
isRemoved={affiliation.removeList.includes(rorToCorrect.rorId)}
ror={rorToCorrect}
rorColor={defineRorColor.find((item) => item.ror === rorToCorrect.rorId)?.color || 'beige-gris-galet'}
setFilteredAffiliationName={setFilteredAffiliationName}
removeRor={() => toggleRemovedRor(affiliation.id, rorToCorrect.rorId)}
/>
<br />
<RorName
isRemoved={affiliation.removeList.includes(rorToCorrect.rorId)}
ror={rorToCorrect}
/>
</td>
</tr>
))}
{affiliation.addList.map((ror) => (
<tr key={`openalex-affiliations-affiliations-${ror.rorId}`}>
<td>
<RorBadge
ror={ror}
rorColor={defineRorColor.find((item) => item.ror === ror.rorId)?.color || 'beige-gris-galet'}
setFilteredAffiliationName={setFilteredAffiliationName}
removeRor={() => removeRorFromAddList(affiliation.id, ror.rorId)}
/>
<br />
<RorName ror={ror} />
<Badge
className="fr-ml-1w"
color="blue-cumulus"
>
Added
</Badge>
</td>
</tr>
))}
</tbody>
</table>
</Col>
</Row>
</li>
))
}
</ul>
</div>
)}
<Modal isOpen={isModalOpen} hide={() => setIsModalOpen((prev) => !prev)} size="md">
<ModalTitle>
Sorts & filters
Expand Down

0 comments on commit 20fb46a

Please sign in to comment.