From bd6ca5bfedb83a29cee128573e972c9e2631a531 Mon Sep 17 00:00:00 2001 From: Marc Farra Date: Wed, 11 Oct 2023 16:33:32 +0300 Subject: [PATCH 1/6] Remove reviewer info UI --- .../single-edit/step-contacts/index.js | 73 +------------------ .../components/documents/single-edit/steps.js | 1 - 2 files changed, 2 insertions(+), 72 deletions(-) diff --git a/app/assets/scripts/components/documents/single-edit/step-contacts/index.js b/app/assets/scripts/components/documents/single-edit/step-contacts/index.js index 99498875..985b0097 100644 --- a/app/assets/scripts/components/documents/single-edit/step-contacts/index.js +++ b/app/assets/scripts/components/documents/single-edit/step-contacts/index.js @@ -1,12 +1,9 @@ import React, { useCallback, useEffect } from 'react'; import T from 'prop-types'; import set from 'lodash.set'; -import get from 'lodash.get'; -import { FieldArray, Formik, Form as FormikForm } from 'formik'; +import { Formik, Form as FormikForm } from 'formik'; import { Form } from '@devseed-ui/form'; import { GlobalLoading } from '@devseed-ui/global-loading'; -import { glsp } from '@devseed-ui/theme-provider'; -import styled from 'styled-components'; import { Inpage, InpageBody } from '../../../../styles/inpage'; import { @@ -14,10 +11,7 @@ import { FormBlockHeading, FormSectionNotes } from '../../../../styles/form-block'; -import { - FormikSectionFieldset, - SectionFieldset -} from '../../../common/forms/section-fieldset'; +import { FormikSectionFieldset } from '../../../common/forms/section-fieldset'; import ContactsList from './contacts-list'; import { Link } from '../../../../styles/clean/link'; @@ -29,17 +23,6 @@ import { getDocumentSectionLabel } from '../sections'; import { documentEdit } from '../../../../utils/url-creator'; import { LocalStore } from '../local-store'; import { FormikUnloadPrompt } from '../../../common/unload-prompt'; -import { FormikInputText } from '../../../common/forms/input-text'; -import { DeletableFieldset } from '../../../common/forms/deletable-fieldset'; -import { FieldMultiItem } from '../../../common/forms/field-multi-item'; - -const emptyAffiliation = ''; - -const BasicInfoSection = styled.div` - display: grid; - grid-gap: ${glsp()}; - grid-template-columns: 1fr 1fr; -`; export default function StepContacts(props) { const { renderInpageHeader, renderFormFooter, atbd, id, version, step } = @@ -148,58 +131,6 @@ export default function StepContacts(props) { - - - - - - - { - const fieldValues = get(form.values, affFieldName) || []; - return ( - push(emptyAffiliation)} - > - {fieldValues.map((field, index) => ( - remove(index)} - > - - - ))} - - ); - }} - /> - {renderFormFooter()} diff --git a/app/assets/scripts/components/documents/single-edit/steps.js b/app/assets/scripts/components/documents/single-edit/steps.js index b2ed1875..8939d97d 100644 --- a/app/assets/scripts/components/documents/single-edit/steps.js +++ b/app/assets/scripts/components/documents/single-edit/steps.js @@ -58,7 +58,6 @@ const STEP_CONTACTS = { // affiliations: [] // } ], - reviewer_info: atbd?.reviewer_info, sections_completed: { contacts: 'incomplete' } From 3c9fd7d601495136e07a15f5acea19c403e91329 Mon Sep 17 00:00:00 2001 From: Marc Farra Date: Wed, 11 Oct 2023 16:33:42 +0300 Subject: [PATCH 2/6] Add 'Document Reviewer' Role type --- .../step-contacts/contact-fieldset.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/assets/scripts/components/documents/single-edit/step-contacts/contact-fieldset.js b/app/assets/scripts/components/documents/single-edit/step-contacts/contact-fieldset.js index 1af683cb..908b50c7 100644 --- a/app/assets/scripts/components/documents/single-edit/step-contacts/contact-fieldset.js +++ b/app/assets/scripts/components/documents/single-edit/step-contacts/contact-fieldset.js @@ -38,7 +38,8 @@ const roleTypes = [ 'Supervision', 'Investigation', 'Funding acquisition', - 'Corresponding Author' + 'Corresponding Author', + 'Document Reviewer' ]; const emptyAffiliation = ''; @@ -96,6 +97,21 @@ export default function ContactsFieldset(props) { ))} + + + Mark contact as reviewer for this document + + + { From 51ec18597eadd17c18af4c6733f54ad553d58305 Mon Sep 17 00:00:00 2001 From: Marc Farra Date: Thu, 12 Oct 2023 17:52:39 +0300 Subject: [PATCH 3/6] Add corresponding authors --- .../documents/journal-pdf-preview/index.js | 68 +++++++++++++------ 1 file changed, 48 insertions(+), 20 deletions(-) diff --git a/app/assets/scripts/components/documents/journal-pdf-preview/index.js b/app/assets/scripts/components/documents/journal-pdf-preview/index.js index f71e3103..f26f0ebb 100644 --- a/app/assets/scripts/components/documents/journal-pdf-preview/index.js +++ b/app/assets/scripts/components/documents/journal-pdf-preview/index.js @@ -427,38 +427,58 @@ function JournalPdfPreview() { const hasAffiliation = contactAffiliations && contactAffiliations.length > 0; - let contactEmail = contact.mechanisms.find( - (mechanism) => mechanism.mechanism_type === 'Email' - )?.mechanism_value; - const item = ( {getContactName(contact, { full: true })} - {contactEmail && ` (${contactEmail})`} + {hasAffiliation && + contactAffiliations.map((affiliation, j) => { + return ( + <> + + {Array.from(affiliations).indexOf(affiliation) + 1} + + + {j < contactAffiliations.length - 1 && , } + + + ); + })} + {i < contacts_link.length - 1 && , } + {i === contacts_link.length - 2 && and } - {hasAffiliation && - contactAffiliations.map((affiliation, j) => { - return ( - <> - - {Array.from(affiliations).indexOf(affiliation) + 1} - - - {j < contactAffiliations.length - 1 && , } - - - ); - })} - {i < contacts_link.length - 1 && , } - {i === contacts_link.length - 2 && and } ); contacts.push(item); } ); + + // create corresponding authors list component + const correspondingAuthors = + contacts_link + ?.filter((c) => + c.roles?.find((r) => r.toLowerCase() === 'corresponding author') + ) + .map(({ contact }) => { + let contactEmail = contact.mechanisms.find( + (mechanism) => mechanism.mechanism_type === 'Email' + )?.mechanism_value; + + let contactName = getContactName(contact, { full: true }); + + return `${contactName} ${contactEmail ? `(${contactEmail})` : ''}`; + }) || []; + + const correspondingAuthorsString = correspondingAuthors.map((author, i) => ( + <> + {author} + {i < correspondingAuthors.length - 1 && , } + {i === correspondingAuthors.length - 2 && and } + + )); return { items: contacts, + correspondingAuthors: correspondingAuthorsString, affiliations_list: Array.from(affiliations), maxIndex: (contacts_link?.length ?? 0) - 1 }; @@ -581,6 +601,14 @@ function JournalPdfPreview() { ))} +
+ {contacts?.correspondingAuthors?.length > 0 && ( +
+ Corresponding Author(s): + {contacts?.correspondingAuthors} +
+ )} +
    From e8b12f3aa16ffadf3271661435665945558bd4f0 Mon Sep 17 00:00:00 2001 From: Marc Farra Date: Thu, 12 Oct 2023 21:51:34 +0300 Subject: [PATCH 4/6] Replace reviewer_info with new component --- .../documents/single-view/document-body.js | 197 ++++++++---------- 1 file changed, 90 insertions(+), 107 deletions(-) diff --git a/app/assets/scripts/components/documents/single-view/document-body.js b/app/assets/scripts/components/documents/single-view/document-body.js index a3a6ca3d..5c7d2655 100644 --- a/app/assets/scripts/components/documents/single-view/document-body.js +++ b/app/assets/scripts/components/documents/single-view/document-body.js @@ -27,7 +27,6 @@ import { useCommentCenter } from '../../../context/comment-center'; import { isJournalPublicationIntended } from '../status'; import serializeSlateToString from '../../slate/serialize-to-string'; import { useContextualAbility } from '../../../a11n'; -import { isDefined, isTruthyString } from '../../../utils/common'; import { formatDocumentTableCaptions } from '../../../utils/format-table-captions'; const PDFPreview = styled.iframe` @@ -1051,58 +1050,48 @@ const htmlAtbdContentSections = [ ), children: ({ atbd }) => { const contactsLink = atbd?.contacts_link || []; - return contactsLink.map(({ contact, roles, affiliations }, idx) => ({ - label: getContactName(contact), - id: `contacts_${idx + 1}`, - render: ({ element }) => ( - + return contactsLink + .filter( + ({ roles }) => + // Remove reviewers that have the role 'Document Reviewer' + !roles.includes('Document Reviewer') ) - })); + .map(({ contact, roles, affiliations }, idx) => ({ + label: getContactName(contact), + id: `contacts_${idx + 1}`, + render: ({ element }) => ( + + ) + })); } }, { label: 'Reviewer Information', id: 'reviewer_info', shouldRender: ({ atbd }) => { - if (!atbd || !atbd.reviewer_info) { - return false; - } + if (!atbd) return false; - const { - reviewer_info: { first_name, last_name } - } = atbd; + // Render if there are reviewers with the role 'Document Reviewer' + const contactsLink = atbd?.contacts_link || []; - if (!isTruthyString(first_name) && !isTruthyString(last_name)) { - return false; + for (const { roles } of contactsLink) { + if (roles.includes('Document Reviewer')) { + return true; + } } - - return true; + return false; }, - render: ({ element, atbd, printMode }) => { - if (!atbd || !atbd.reviewer_info) { - return null; - } - - const { - reviewer_info: { first_name, last_name, email, affiliations } - } = atbd; - - let fullName; - if (isTruthyString(first_name) || isTruthyString(last_name)) { - fullName = [first_name, last_name].filter(isDefined).join(' '); - } - - if (!isTruthyString(fullName) && !isTruthyString(email)) { + render: ({ element, atbd, printMode, children }) => { + if (!atbd) { return null; } - return ( - -

    {fullName}

    - -
    Email
    -
    {email}
    -
    Affiliations
    - {affiliations.length ? ( -
    {renderMultipleStringValues(affiliations)}
    - ) : ( -
    No affiliations for the reviewer
    - )} -
    -
    + {React.Children.count(children) ? ( + children + ) : ( +

    There are no reviewers associated with this document

    + )}
    ); + }, + children: ({ atbd }) => { + const contactsLink = atbd?.contacts_link || []; + return contactsLink + .filter(({ roles }) => + // Include reviewers that have the role 'Document Reviewer' + roles.includes('Document Reviewer') + ) + .map(({ contact, roles, affiliations }, idx) => ({ + label: getContactName(contact), + id: `contacts_${idx + 1}`, + render: ({ element }) => ( + + ) + })); } }, { @@ -1268,58 +1270,48 @@ const pdfAtbdContentSections = [ ), children: ({ atbd }) => { const contactsLink = atbd?.contacts_link || []; - return contactsLink.map(({ contact, roles, affiliations }, idx) => ({ - label: getContactName(contact), - id: `contacts_${idx + 1}`, - render: ({ element }) => ( - + return contactsLink + .filter( + ({ roles }) => + // Remove reviewers that have the role 'Document Reviewer' + !roles.includes('Document Reviewer') ) - })); + .map(({ contact, roles, affiliations }, idx) => ({ + label: getContactName(contact), + id: `contacts_${idx + 1}`, + render: ({ element }) => ( + + ) + })); } }, { label: 'Reviewer Information', id: 'reviewer_info', shouldRender: ({ atbd }) => { - if (!atbd || !atbd.reviewer_info) { - return false; - } + if (!atbd) return false; - const { - reviewer_info: { first_name, last_name } - } = atbd; + // Render if there are reviewers with the role 'Document Reviewer' + const contactsLink = atbd?.contacts_link || []; - if (!isTruthyString(first_name) && !isTruthyString(last_name)) { - return false; + for (const { roles } of contactsLink) { + if (roles.includes('Document Reviewer')) { + return true; + } } - - return true; + return false; }, - render: ({ element, atbd, printMode }) => { - if (!atbd || !atbd.reviewer_info) { + render: ({ element, atbd, printMode, children }) => { + if (!atbd) { return null; } - - const { - reviewer_info: { first_name, last_name, email, affiliations } - } = atbd; - - let fullName; - if (isTruthyString(first_name) || isTruthyString(last_name)) { - fullName = [first_name, last_name].filter(isDefined).join(' '); - } - - if (!isTruthyString(fullName) && !isTruthyString(email)) { - return null; - } - return ( - -

    {fullName}

    - -
    Email
    -
    {email}
    -
    Affiliations
    - {affiliations.length ? ( -
    {renderMultipleStringValues(affiliations)}
    - ) : ( -
    No affiliations for the reviewer
    - )} -
    -
    + {React.Children.count(children) ? ( + children + ) : ( +

    There are no reviewers associated with this document

    + )}
    ); } From 6b4d51e581cd70d8f0a46455a6450e07ba92441c Mon Sep 17 00:00:00 2001 From: Marc Farra Date: Thu, 12 Oct 2023 22:25:13 +0300 Subject: [PATCH 5/6] add sort contacts method --- .../documents/single-view/document-body.js | 3 +++ app/assets/scripts/utils/sort-contacts.js | 20 +++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 app/assets/scripts/utils/sort-contacts.js diff --git a/app/assets/scripts/components/documents/single-view/document-body.js b/app/assets/scripts/components/documents/single-view/document-body.js index 5c7d2655..20c3abe5 100644 --- a/app/assets/scripts/components/documents/single-view/document-body.js +++ b/app/assets/scripts/components/documents/single-view/document-body.js @@ -28,6 +28,7 @@ import { isJournalPublicationIntended } from '../status'; import serializeSlateToString from '../../slate/serialize-to-string'; import { useContextualAbility } from '../../../a11n'; import { formatDocumentTableCaptions } from '../../../utils/format-table-captions'; +import { sortContacts } from '../../../utils/sort-contacts'; const PDFPreview = styled.iframe` width: 100%; @@ -1056,6 +1057,7 @@ const htmlAtbdContentSections = [ // Remove reviewers that have the role 'Document Reviewer' !roles.includes('Document Reviewer') ) + .sort(sortContacts) .map(({ contact, roles, affiliations }, idx) => ({ label: getContactName(contact), id: `contacts_${idx + 1}`, @@ -1115,6 +1117,7 @@ const htmlAtbdContentSections = [ // Include reviewers that have the role 'Document Reviewer' roles.includes('Document Reviewer') ) + .sort(sortContacts) .map(({ contact, roles, affiliations }, idx) => ({ label: getContactName(contact), id: `contacts_${idx + 1}`, diff --git a/app/assets/scripts/utils/sort-contacts.js b/app/assets/scripts/utils/sort-contacts.js new file mode 100644 index 00000000..348f5565 --- /dev/null +++ b/app/assets/scripts/utils/sort-contacts.js @@ -0,0 +1,20 @@ +/** + * Sorts an array of contacts based on whether they have the "Corresponding Author" role. + * Corresponding authors appear first followed by other contacts + * @param {Object} a - The first contact object to compare. + * @param {Object} b - The second contact object to compare. + * @returns {number} - Returns -1 if a has the "Corresponding Author" role and b does not, 1 if b has the "Corresponding Author" role and a does not, and 0 if both have or do not have the "Corresponding Author" role. + */ +export function sortContacts(a, b) { + // Sort so that corresponding author roles are first + const hasCorrespondingAuthorRole = (roles) => { + return roles.includes('Corresponding Author'); + }; + const aHasCorrespondingAuthorRole = hasCorrespondingAuthorRole(a.roles); + const bHasCorrespondingAuthorRole = hasCorrespondingAuthorRole(b.roles); + return aHasCorrespondingAuthorRole === bHasCorrespondingAuthorRole + ? 0 + : aHasCorrespondingAuthorRole + ? -1 + : 1; +} From 2f68fbafc4b614d8a4f1a815f32b75a706d65a81 Mon Sep 17 00:00:00 2001 From: Marc Farra Date: Fri, 13 Oct 2023 11:27:05 +0300 Subject: [PATCH 6/6] Remove redundant checkbox --- .../single-edit/step-contacts/contact-fieldset.js | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/app/assets/scripts/components/documents/single-edit/step-contacts/contact-fieldset.js b/app/assets/scripts/components/documents/single-edit/step-contacts/contact-fieldset.js index 908b50c7..9cffb06e 100644 --- a/app/assets/scripts/components/documents/single-edit/step-contacts/contact-fieldset.js +++ b/app/assets/scripts/components/documents/single-edit/step-contacts/contact-fieldset.js @@ -97,21 +97,6 @@ export default function ContactsFieldset(props) { ))} - - - Mark contact as reviewer for this document - - - {