Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pkp/pkp-lib#4787 Reviewer suggestions #426

Merged
merged 19 commits into from
Feb 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
8813e91
pkp/pkp-lib#4787 Reviewer suggestions
touhidurabir Oct 14, 2024
ed62183
pkp/pkp-lib#4787 Reviewer suggestions api issues fixed
touhidurabir Oct 18, 2024
bc19fad
pkp/pkp-lib#4787 removed dead codes
touhidurabir Oct 22, 2024
4c4feb8
pkp/pkp-lib#4787 WIP : suggestion lists to reviewers list in review s…
touhidurabir Oct 25, 2024
b3ed5fb
pkp/pkp-lib#4787 reviewer adding from suggestion working
touhidurabir Oct 30, 2024
34621aa
pkp/pkp-lib#4787 leftover ui changes and simple orcid id field add
touhidurabir Nov 1, 2024
564c243
pkp/pkp-lib#4787 temp use of v-html
touhidurabir Nov 1, 2024
a706bad
pkp/pkp-lib#4787 Not to show suggestion side panel in review stage in…
touhidurabir Nov 11, 2024
685f713
pkp/pkp-lib#4787 enable suggestion list in workflow submission page w…
touhidurabir Nov 20, 2024
8aceea8
pkp/pkp-lib#4787 Reviewer suggestions 3 case consider WIP
touhidurabir Nov 21, 2024
f9111da
pkp/pkp-lib#4787 reactive update of suggestion list in add reviewer m…
touhidurabir Nov 28, 2024
b404aaa
pkp/pkp-lib#4787 WIP : suggestion lists to reviewers list in review s…
touhidurabir Dec 2, 2024
26c559e
pkp/pkp-lib#4787 consider case when adding new reviewer who is an exi…
touhidurabir Dec 2, 2024
8800e5a
pkp/pkp-lib#4787 WIP: most todos resolved
touhidurabir Feb 7, 2025
6a3b38b
pkp/pkp-lib#4787 compatibility for OMP added
touhidurabir Feb 9, 2025
a365819
pkp/pkp-lib#4787 added better logic to determine if at active review …
touhidurabir Feb 10, 2025
b9cb899
pkp/pkp-lib#4787 OMP restrict suggestion at internal review stage
touhidurabir Feb 10, 2025
4a5becd
pkp/pkp-lib#4787 fixed issue with review round files details viewing
touhidurabir Feb 17, 2025
18f36a3
pkp/pkp-lib#4787 replace v-html with v-strip-unsafe-html
touhidurabir Feb 23, 2025
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
9 changes: 9 additions & 0 deletions src/components/Container/SubmissionWizardPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import Page from '@/components/Container/Page.vue';
import ButtonRow from '../ButtonRow/ButtonRow.vue';
import ContributorsListPanel from '../ListPanel/contributors/ContributorsListPanel.vue';
import ReviewerSuggestionsListPanel from '../ListPanel/reviewerSuggestions/ReviewerSuggestionsListPanel.vue';
import File from '../File/File.vue';
import Modal from '../Modal/Modal.vue';
import ReconfigureSubmissionModal from '@/pages/submissionWizard/ReconfigureSubmissionModal.vue';
Expand All @@ -19,6 +20,7 @@ export default {
components: {
ButtonRow,
ContributorsListPanel,
ReviewerSuggestionsListPanel,
File,
Modal,
SubmissionFilesListPanel,
Expand Down Expand Up @@ -601,6 +603,13 @@ export default {
this.publication.authors = newContributors;
},

/**
* Update the reviewer suggestions in the submission
*/
setReviewerSuggestion(newReviewerSuggestions) {
this.submission.reviewerSuggestions = newReviewerSuggestions;
},

/**
* Update the publication in the submission
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<template>
<SideModalBody>
<template #title>
{{ title }}
</template>
<SideModalLayoutBasic>
<PkpForm
v-bind="activeForm"
@set="(...args) => emit('updateForm', ...args)"
@success="(...args) => emit('formSuccess', ...args)"
/>
</SideModalLayoutBasic>
</SideModalBody>
</template>

<script setup>
import SideModalBody from '@/components/Modal/SideModalBody.vue';
import SideModalLayoutBasic from '@/components/Modal/SideModalLayoutBasic.vue';
import PkpForm from '@/components/Form/Form.vue';

defineProps({
title: {type: String, required: true},
activeForm: {type: Object, required: true},
});
const emit = defineEmits(['updateForm', 'formSuccess']);
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,325 @@
<template>
<div class="reviewerSuggestionsListPanel">
<slot>
<ListPanel :items="items" class="listPanel--reviewerSuggestions">
<template #header>
<PkpHeader>
<h2>{{ title }}</h2>
<Spinner v-if="isLoading" />
<template #actions>
<PkpButton
v-if="
publication.status !== getConstant('STATUS_PUBLISHED') &&
canEditPublication
"
:disabled="isLoading"
@click="openAddModal"
>
{{ t('grid.action.addReviewerSuggestion') }}
</PkpButton>
</template>
</PkpHeader>
</template>

<template #item-title="{item}">
{{ localize(item.fullName) }}
<Badge>
{{ localize(item.affiliation) }}
</Badge>
</template>

<template #item-subtitle="{item}">
{{ item.email }}
</template>

<template
v-if="
publication.status !== getConstant('STATUS_PUBLISHED') &&
canEditPublication
"
#item-actions="{item}"
>
<PkpButton :disabled="isLoading" @click="openEditModal(item.id)">
{{ t('common.edit') }}
</PkpButton>
<PkpButton
:disabled="isLoading"
:is-warnable="true"
@click="openDeleteModal(item.id)"
>
{{ t('common.delete') }}
</PkpButton>
</template>
</ListPanel>
</slot>
</div>
</template>

<script>
import Spinner from '@/components/Spinner/Spinner.vue';
import PkpButton from '@/components/Button/Button.vue';
import Badge from '@/components/Badge/Badge.vue';
import ListPanel from '@/components/ListPanel/ListPanel.vue';
import PkpHeader from '@/components/Header/Header.vue';
import ReviewerSuggestionsEditModal from './ReviewerSuggestionsEditModal.vue';

import ajaxError from '@/mixins/ajaxError';
import dialog from '@/mixins/dialog.js';
import cloneDeep from 'clone-deep';
import {useModal} from '@/composables/useModal';

export default {
components: {
Spinner,
PkpButton,
Badge,
ListPanel,
PkpHeader,
},
mixins: [ajaxError, dialog],
props: {
canEditPublication: {
type: Boolean,
required: true,
},
form: {
type: Object,
required: true,
},
id: {
type: String,
required: true,
},
items: {
type: Array,
default() {
return [];
},
},
title: {
type: String,
required: true,
},
reviewerSuggestionsApiUrl: {
type: String,
required: true,
},
submission: {
type: Object,
required: true,
},
publication: {
type: Object,
required: true,
},
},
emits: [
'updated:reviewerSuggestions'
],
data() {
return {
activeForm: null,
activeFormTitle: '',
isLoading: false,
};
},
computed: {
/**
* Unique ID for the form modal
*/
formModal() {
return this.id + 'form';
},
},
methods: {
/**
* Helper method to access a global constant in the template
*
* @return {Object}
*/
getConstant(constant) {
return pkp.const[constant];
},

/**
* Clear the active form when the modal is closed
*
* @param {Object} event
*/
closeFormModal(event) {
const {closeSideModal} = useModal();
closeSideModal(ReviewerSuggestionsEditModal);
this.activeForm = null;
this.activeFormTitle = '';
},

/**
* The add/edit form has been successfully
* submitted.
*
* @param {Object} item
*/
formSuccess(reviewerSuggestion) {
if (this.activeForm.method === 'POST') {
this.offset = 0;

const newReviewerSuggestions = [...this.submission.reviewerSuggestions];
newReviewerSuggestions.push(reviewerSuggestion);

this.$emit('updated:reviewerSuggestions', newReviewerSuggestions);
} else {
const newReviewerSuggestions = this.submission.reviewerSuggestions.map(
(suggestion) => {
if (suggestion.id === reviewerSuggestion.id) {
return reviewerSuggestion;
}
return suggestion;
},
);
this.$emit('updated:reviewerSuggestions', newReviewerSuggestions);
}

this.closeFormModal();
},

/**
* Open the modal to add an item
*/
openAddModal() {
let activeForm = cloneDeep(this.form);
activeForm.action = this.reviewerSuggestionsApiUrl;
activeForm.method = 'POST';
this.activeForm = activeForm;
this.activeFormTitle = this.t('grid.action.addReviewerSuggestion');
const {openSideModal} = useModal();

openSideModal(ReviewerSuggestionsEditModal, {
title: this.activeFormTitle,
activeForm: this.activeForm,
onUpdateForm: this.updateForm,
onFormSuccess: this.formSuccess,
});
},

/**
* Open delete modal
*
* @param {Number} id
*/
openDeleteModal(id) {
const reviewerSuggestion = this.items.find((a) => a.id === id);

this.openDialog({
name: 'delete',
title: this.t('grid.action.deleteReviewerSuggestion'),
message: this.t(
'grid.action.deleteReviewerSuggestion.confirmationMessage',
{
name: reviewerSuggestion.familyName,
},
),
actions: [
{
label: this.t('grid.action.deleteReviewerSuggestion'),
isWarnable: true,
callback: (close) => {
this.isLoading = true;

$.ajax({
url: this.reviewerSuggestionsApiUrl + '/' + id,
type: 'POST',
context: this,
headers: {
'X-Csrf-Token': pkp.currentUser.csrfToken,
'X-Http-Method-Override': 'DELETE',
},
error: this.ajaxErrorCallback,
success(r) {
close();
this.setFocusIn(this.$el);

const newReviewerSuggestions =
this.submission.reviewerSuggestions.filter(
(reviewerSuggestion) => {
return reviewerSuggestion.id !== id;
},
);
this.$emit(
'updated:reviewerSuggestions',
newReviewerSuggestions,
);
},
complete(r) {
this.isLoading = false;
},
});
},
},
{
label: this.t('common.cancel'),
callback: (close) => close(),
},
],
});
},

/**
* Open the modal to edit an item
*
* @param {Number} id
*/
openEditModal(id) {
this.isLoading = true;
const apiUrl = this.reviewerSuggestionsApiUrl + '/' + id;

$.ajax({
url: apiUrl,
type: 'GET',
error: this.ajaxErrorCallback,
context: this,
success(reviewerSuggestion) {
let activeForm = cloneDeep(this.form);
activeForm.action = apiUrl;
activeForm.method = 'PUT';
activeForm.fields = activeForm.fields.map((field) => {
if (Object.keys(reviewerSuggestion).includes(field.name)) {
field.value = reviewerSuggestion[field.name];
}
return field;
});
this.activeForm = activeForm;
this.activeFormTitle = this.t('grid.action.edit');
const {openSideModal} = useModal();
openSideModal(ReviewerSuggestionsEditModal, {
title: this.activeFormTitle,
activeForm: this.activeForm,
onUpdateForm: this.updateForm,
onFormSuccess: this.formSuccess,
});
},
complete(r) {
this.isLoading = false;
},
});
},

/**
* Update form values when they change
*
* @param {String} formId
* @param {Object} data
*/
updateForm(formId, data) {
if (!this.activeForm) {
return;
}

let activeForm = this.activeForm;
Object.keys(data).forEach(function (key) {
activeForm[key] = data[key];
});
this.activeForm = activeForm;
},
},
};
</script>
Loading