diff --git a/src/components/Modal/ModalManager.vue b/src/components/Modal/ModalManager.vue index 0b6e88c20..d6efce49e 100644 --- a/src/components/Modal/ModalManager.vue +++ b/src/components/Modal/ModalManager.vue @@ -70,8 +70,9 @@ import {storeToRefs} from 'pinia'; import SideModal from '@/components/Modal/SideModal.vue'; import LegacyAjax from '@/components/Modal/SideModalBodyLegacyAjax.vue'; import PkpDialog from '@/components/Modal/Dialog.vue'; +import WorkflowLogResponseModal from '@/managers/ReviewerManager/modals/WorkflowLogResponseModal.vue'; -const GlobalModals = {LegacyAjax}; +const GlobalModals = {LegacyAjax, WorkflowLogResponseModal}; const modalStore = useModalStore(); const { diff --git a/src/managers/ReviewerManager/modals/WorkflowLogResponseModal.mdx b/src/managers/ReviewerManager/modals/WorkflowLogResponseModal.mdx new file mode 100644 index 000000000..a37fa3842 --- /dev/null +++ b/src/managers/ReviewerManager/modals/WorkflowLogResponseModal.mdx @@ -0,0 +1,11 @@ +import {Meta, ArgTypes} from '@storybook/blocks'; + +import * as WorkflowLogResponseModalStories from './WorkflowLogResponseModal.stories'; + + + +# Log Response Modal + +A vue modal containing a form for and admin to log a response to a review request on behalf of the reviewer. + + diff --git a/src/managers/ReviewerManager/modals/WorkflowLogResponseModal.stories.js b/src/managers/ReviewerManager/modals/WorkflowLogResponseModal.stories.js new file mode 100644 index 000000000..f1651251a --- /dev/null +++ b/src/managers/ReviewerManager/modals/WorkflowLogResponseModal.stories.js @@ -0,0 +1,77 @@ +import WorkflowLogResponseModal from './WorkflowLogResponseModal.vue'; +import PkpButton from '@/components/Button/Button.vue'; +import {useModal} from '@/composables/useModal'; +import {within, userEvent} from '@storybook/test'; + +export default { + title: 'Pages/Workflow/LogResponse', + component: WorkflowLogResponseModal, +}; + +export const Base = { + render: (args) => ({ + components: {WorkflowLogResponseModal, PkpButton}, + setup() { + const {openSideModal} = useModal(); + + function logResponse() { + openSideModal(WorkflowLogResponseModal, args.modalProps); + } + return {logResponse, ...args}; + }, + template: 'Log Response', + }), + args: { + modalProps: { + description: + 'Sodium butyrate improves growth performance of weaned piglets during the first period after weaning', + submissionId: 12, + logResponseForm: { + id: 'logResponseForm', + method: 'POST', + action: + 'http://localhost:7003/index.php/publicknowledge/$$$call$$$/grid/users/reviewer/reviewer-grid/add-log?submissionId=12&reviewAssignmentId=17&stageId=3&round=0', + fields: [ + { + name: 'acceptReview', + isRequired: true, + description: + 'If the reviewer contacts you through email or any other means, you can record their response for them', + component: 'field-options', + label: 'Record the response on behalf of the reviewer', + value: false, + type: 'radio', + options: [ + { + value: 1, + label: 'Reviewer has accepted the invitation to review', + }, + { + value: 0, + label: 'Reviewer has declined the invitation to review', + }, + ], + groupId: 'default', + }, + ], + groups: [{id: 'default', pageId: 'default'}], + pages: [{id: 'default', submitButton: {label: 'Log Response'}}], + primaryLocale: 'en', + visibleLocales: ['en'], + supportedFormLocales: ['en', 'fr_CA'], + }, + }, + }, + play: async ({canvasElement}) => { + // Assigns canvas to the component root element + const canvas = within(canvasElement); + const user = userEvent.setup(); + + await user.click(canvas.getByText('Log Response')); + }, + decorators: [ + () => ({ + template: '
', + }), + ], +}; diff --git a/src/managers/ReviewerManager/modals/WorkflowLogResponseModal.vue b/src/managers/ReviewerManager/modals/WorkflowLogResponseModal.vue new file mode 100644 index 000000000..f1ba431fa --- /dev/null +++ b/src/managers/ReviewerManager/modals/WorkflowLogResponseModal.vue @@ -0,0 +1,35 @@ + + + diff --git a/src/managers/ReviewerManager/modals/workflowLogResponseModalStore.js b/src/managers/ReviewerManager/modals/workflowLogResponseModalStore.js new file mode 100644 index 000000000..5ba8fcce9 --- /dev/null +++ b/src/managers/ReviewerManager/modals/workflowLogResponseModalStore.js @@ -0,0 +1,17 @@ +import {inject} from 'vue'; +import {defineComponentStore} from '@/utils/defineComponentStore'; +import {useForm} from '@/composables/useForm'; + +export const useWorkflowLogResponseModalStore = defineComponentStore( + 'workflowLogResponseModal', + (props) => { + const closeModal = inject('closeModal'); + const {set: updateForm, form} = useForm(props.logResponseForm); + + function formSuccess() { + closeModal(); + } + + return {form, formSuccess, updateForm}; + }, +); diff --git a/src/managers/ReviewerManager/reviewerManagerStore.js b/src/managers/ReviewerManager/reviewerManagerStore.js index 0a3bfa350..1e1e90339 100644 --- a/src/managers/ReviewerManager/reviewerManagerStore.js +++ b/src/managers/ReviewerManager/reviewerManagerStore.js @@ -157,6 +157,13 @@ export const useReviewerManagerStore = defineComponentStore( ); } + function reviewerLogResponse({reviewAssignment}) { + _actionFns.reviewerLogResponse( + getActionArgs({reviewAssignment}), + dataUpdateCallback, + ); + } + function getItemActions(args) { return _actionFns.getItemActions(args); } @@ -190,6 +197,7 @@ export const useReviewerManagerStore = defineComponentStore( reviewerThankReviewer, reviewerRevertConsider, reviewerSendReminder, + reviewerLogResponse, _reviewerManagerActionFns: _actionFns, }; }, diff --git a/src/managers/ReviewerManager/useReviewerManagerActions.js b/src/managers/ReviewerManager/useReviewerManagerActions.js index cc454e2df..add0eab3c 100644 --- a/src/managers/ReviewerManager/useReviewerManagerActions.js +++ b/src/managers/ReviewerManager/useReviewerManagerActions.js @@ -4,6 +4,9 @@ import {useModal} from '@/composables/useModal'; import {useLocalize} from '@/composables/useLocalize'; import {useSubmission} from '@/composables/useSubmission'; import {useFetch, getCSRFToken} from '@/composables/useFetch'; +import WorkflowLogResponseModal from '@/managers/ReviewerManager/modals/WorkflowLogResponseModal.vue'; +import {useDashboardPageStore} from '@/pages/dashboard/dashboardPageStore'; +import {useApiUrl} from '@/composables/useApiUrl'; export const Actions = { REVIEWER_ADD_REVIEWER: 'reviewerAddReviewer', @@ -22,6 +25,7 @@ export const Actions = { REVIEWER_THANK_REVIEWER: 'reviewerThankReviewer', REVIEWER_REVERT_CONSIDER: 'reviewerRevertConsider', REVIEWER_SEND_REMINDER: 'reviewerSendReminder', + REVIEWER_LOG_RESPONSE: 'reviewerLogResponse', }; export function useReviewerManagerActions() { @@ -186,6 +190,14 @@ export function useReviewerManagerActions() { icon: 'DefaultDocument', }); + if (!reviewAssignment.dateConfirmed) { + actions.push({ + label: t('editor.review.logResponse'), + name: Actions.REVIEWER_LOG_RESPONSE, + icon: 'ReviewAssignments', + }); + } + return actions; } @@ -564,6 +576,37 @@ export function useReviewerManagerActions() { openLegacyModal({title: t('editor.review.reminder')}, finishedCallback); } + function reviewerLogResponse( + {submission, reviewAssignment, submissionStageId}, + finishedCallback, + ) { + const {openSideModal} = useModal(); + + const dashboardStore = useDashboardPageStore(); + let form = dashboardStore.componentForms.logResponseForm; + let submissionId = submission.id; + + const {getCurrentPublication} = useSubmission(); + const currentPublication = getCurrentPublication(submission); + const title = `${localizeSubmission(currentPublication.fullTitle, currentPublication.locale)}`; + + const {apiUrl} = useApiUrl( + `reviews/${submissionId}/${reviewAssignment.id}/confirmReview`, + ); + + form.action = apiUrl; + + openSideModal( + WorkflowLogResponseModal, + { + title: title, + submissionId: submissionId, + logResponseForm: dashboardStore.componentForms.logResponseForm, + }, + {onClose: finishedCallback}, + ); + } + return { getTopActions, getItemActions, @@ -584,5 +627,6 @@ export function useReviewerManagerActions() { reviewerThankReviewer, reviewerRevertConsider, reviewerSendReminder, + reviewerLogResponse, }; }