Skip to content

Commit f3e71d6

Browse files
I10363 minimal review count (#563)
* pkp/pkp-lib#10363 Minimal reviews count feature messaging * pkp/pkp-lib#10363 Adjust styling for warning dialog for minimum reviews
1 parent 35c8299 commit f3e71d6

File tree

16 files changed

+303
-73
lines changed

16 files changed

+303
-73
lines changed

src/composables/useSubmission.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ export const CompletedReviewAssignmentStatuses = [
7575
pkp.const.REVIEW_ASSIGNMENT_STATUS_VIEWED,
7676
];
7777

78+
// Confirmed reviews
79+
export const ConfirmedReviewAssignmentStatuses = [
80+
pkp.const.REVIEW_ASSIGNMENT_STATUS_COMPLETE,
81+
pkp.const.REVIEW_ASSIGNMENT_STATUS_THANKED,
82+
];
83+
7884
const IgnoredReviewAssignmentStatuses = [
7985
pkp.const.REVIEW_ASSIGNMENT_STATUS_DECLINED,
8086
pkp.const.REVIEW_ASSIGNMENT_STATUS_CANCELLED,
@@ -229,6 +235,13 @@ export function useSubmission() {
229235
);
230236
}
231237

238+
function getConfirmedReviewAssignments(reviewAssignments = []) {
239+
return getActiveReviewAssignments(reviewAssignments).filter(
240+
(reviewAssignment) =>
241+
ConfirmedReviewAssignmentStatuses.includes(reviewAssignment.statusId),
242+
);
243+
}
244+
232245
function getOpenReviewAssignments(reviewAssignments) {
233246
return reviewAssignments.filter(
234247
(reviewAssignment) =>
@@ -265,6 +278,38 @@ export function useSubmission() {
265278
);
266279
}
267280

281+
function checkMinimumConsideredReviews(
282+
submission,
283+
stageId,
284+
reviewRoundId,
285+
minReviewsCount,
286+
) {
287+
if (
288+
!minReviewsCount ||
289+
stageId !== pkp.const.WORKFLOW_STAGE_ID_EXTERNAL_REVIEW
290+
) {
291+
return {
292+
shouldMinimumReviewsBeConsidered: false,
293+
hasMinimumReviewsCount: false,
294+
};
295+
}
296+
const reviewAssignments = getReviewAssignmentsForRound(
297+
submission.reviewAssignments,
298+
reviewRoundId,
299+
);
300+
301+
const confirmedReviewAssignments =
302+
getConfirmedReviewAssignments(reviewAssignments);
303+
304+
const hasMinimumReviewsCount =
305+
confirmedReviewAssignments.length >= minReviewsCount;
306+
307+
return {
308+
shouldMinimumReviewsBeConsidered: true,
309+
hasMinimumReviewsCount,
310+
};
311+
}
312+
268313
return {
269314
getSubmissionById,
270315
getActiveStage,
@@ -283,7 +328,9 @@ export function useSubmission() {
283328
// review assignments
284329
getReviewAssignmentsForRound,
285330
getActiveReviewAssignments,
331+
getConfirmedReviewAssignments,
286332
getCompletedReviewAssignments,
333+
checkMinimumConsideredReviews,
287334
getOpenReviewAssignments,
288335
getOpenReviewAssignmentsForRound,
289336
getOpenAndCompletedReviewAssignmentsForRound,

src/pages/dashboard/DashboardPage.vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ const props = defineProps({
9393
type: Array,
9494
required: true,
9595
},
96+
/** Minimum reviews per submission */
97+
contextMinReviewsPerSubmission: {
98+
type: Number,
99+
required: true,
100+
},
96101
/** Filters form config */
97102
filtersForm: {
98103
type: Object,

src/pages/dashboard/components/DashboardTable/DashboardCellReviewAssignmentActivity/DashboardCellReviewAssignmentActivity.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ const dashboardStore = useDashboardPageStore();
2626
const props = defineProps({item: {type: Object, required: true}});
2727
2828
const cellConfig = computed(() => {
29-
const config = dashboardStore.getEditorialActivityForMyReviewAssignments(
30-
props.item,
31-
);
29+
const config = dashboardStore.getEditorialActivityForMyReviewAssignments({
30+
reviewAssignment: props.item,
31+
});
3232
return config;
3333
});
3434
</script>

src/pages/dashboard/components/DashboardTable/DashboardCellSubmissionActivity/DashboardCellSubmissionActivity.vue

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,15 @@ const props = defineProps({item: {type: Object, required: true}});
3737
3838
const cellConfig = computed(() => {
3939
if (dashboardStore.dashboardPage === DashboardPageTypes.EDITORIAL_DASHBOARD) {
40-
return dashboardStore.getEditorialActivityForEditorialDashboard(props.item);
40+
return dashboardStore.getEditorialActivityForEditorialDashboard({
41+
submission: props.item,
42+
});
4143
} else if (
4244
dashboardStore.dashboardPage === DashboardPageTypes.MY_SUBMISSIONS
4345
) {
44-
return dashboardStore.getEditorialActivityForMySubmissions(props.item);
46+
return dashboardStore.getEditorialActivityForMySubmissions({
47+
submission: props.item,
48+
});
4549
}
4650
4751
return [];

src/pages/dashboard/composables/useDashboardConfigEditorialActivity.js

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const {
2020
getActiveReviewAssignments,
2121
getReviewAssignmentsForRound,
2222
getStageLabel,
23+
checkMinimumConsideredReviews,
2324
} = useSubmission();
2425

2526
const {
@@ -28,7 +29,10 @@ const {
2829
} = useCurrentUser();
2930

3031
export function useDashboardConfigEditorialActivity() {
31-
function getEditorialActivityForEditorialDashboard(submission) {
32+
function getEditorialActivityForEditorialDashboard({
33+
submission,
34+
contextMinReviewsPerSubmission,
35+
}) {
3236
const activeStage = getActiveStage(submission);
3337

3438
if (submission.status === pkp.const.STATUS_DECLINED) {
@@ -119,6 +123,15 @@ export function useDashboardConfigEditorialActivity() {
119123
const activeRound = getCurrentReviewRound(submission, activeStage.id);
120124
let activeRoundStatusId = activeRound.statusId;
121125

126+
// #10363 Calculate additional status for minimum reviews
127+
const {shouldMinimumReviewsBeConsidered, hasMinimumReviewsCount} =
128+
checkMinimumConsideredReviews(
129+
submission,
130+
activeStage.id,
131+
activeRound.id,
132+
contextMinReviewsPerSubmission,
133+
);
134+
122135
// Workaround to customise deciding editor messaging, until we have way to provide personalised on backend
123136
if (activeStage.isCurrentUserDecidingEditor) {
124137
// Avoid overloading deciding editor with details about review assignments
@@ -323,7 +336,9 @@ export function useDashboardConfigEditorialActivity() {
323336
},
324337
];
325338
} else if (
326-
activeRoundStatusId === pkp.const.REVIEW_ROUND_STATUS_REVIEWS_COMPLETED
339+
activeRoundStatusId ===
340+
pkp.const.REVIEW_ROUND_STATUS_REVIEWS_COMPLETED &&
341+
!shouldMinimumReviewsBeConsidered
327342
) {
328343
return [
329344
{
@@ -343,6 +358,25 @@ export function useDashboardConfigEditorialActivity() {
343358
},
344359
},
345360
];
361+
} else if (shouldMinimumReviewsBeConsidered && hasMinimumReviewsCount) {
362+
return [
363+
{
364+
component: 'DashboardCellSubmissionActivityActionAlert',
365+
props: {
366+
alert: t('dashboard.minimumReviewsConfirmedDecisionNeeded'),
367+
},
368+
},
369+
{
370+
component: 'DashboardCellSubmissionActivityReviews',
371+
props: {
372+
submissionId: submission.id,
373+
reviewAssignments: getCurrentReviewAssignments(
374+
submission,
375+
activeStage.id,
376+
),
377+
},
378+
},
379+
];
346380
} else {
347381
return [
348382
{
@@ -392,7 +426,7 @@ export function useDashboardConfigEditorialActivity() {
392426
return {};
393427
}
394428

395-
function getEditorialActivityForMySubmissions(submission) {
429+
function getEditorialActivityForMySubmissions({submission}) {
396430
const activeStage = getActiveStage(submission);
397431

398432
// Not checking for submission stage, as OPS does not have it
@@ -496,7 +530,7 @@ export function useDashboardConfigEditorialActivity() {
496530
return {};
497531
}
498532

499-
function getEditorialActivityForMyReviewAssignments(reviewAssignment) {
533+
function getEditorialActivityForMyReviewAssignments({reviewAssignment}) {
500534
// When declined always show the same status regardless of the stage
501535
if (
502536
reviewAssignment.status === pkp.const.REVIEW_ASSIGNMENT_STATUS_DECLINED

src/pages/dashboard/dashboardPageStore.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ export const useDashboardPageStore = defineComponentStore(
5151
const extender = useExtender();
5252

5353
const dashboardPage = pageInitConfig.dashboardPage;
54+
55+
/**
56+
* Minimal reviews count feature
57+
*/
58+
const contextMinReviewsPerSubmission =
59+
pageInitConfig.contextMinReviewsPerSubmission;
60+
5461
/**
5562
* ModalStore
5663
*/
@@ -433,6 +440,7 @@ export const useDashboardPageStore = defineComponentStore(
433440
{
434441
...args,
435442
dashboardPage,
443+
contextMinReviewsPerSubmission,
436444
},
437445
);
438446
}
@@ -576,6 +584,9 @@ export const useDashboardPageStore = defineComponentStore(
576584
getReviewActivityIndicatorProps,
577585
getReviewActivityIndicatorPopoverProps,
578586

587+
// Minimal reviews count feature
588+
contextMinReviewsPerSubmission,
589+
579590
// Expose component forms, so managers and other dashboard/workflow component can access them
580591
componentForms: pageInitConfig.componentForms,
581592

src/pages/workflow/components/action/WorkflowRecommendOnlyControls.vue

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ import {useWorkflowStore} from '@/pages/workflow/workflowStore';
4646
import {useSubmission} from '@/composables/useSubmission';
4747
4848
import {Actions as DecisionActions} from '../../composables/useWorkflowDecisions';
49-
import {Actions as WorkflowActions} from '../../composables/useWorkflowActions';
5049
5150
const {t} = useLocalize();
5251
@@ -88,7 +87,7 @@ function getRecommendationActions() {
8887
if (props.stageId === pkp.const.WORKFLOW_STAGE_ID_EXTERNAL_REVIEW) {
8988
actions.push({
9089
label: t('editor.submission.recommend.revisions'),
91-
action: WorkflowActions.WORKFLOW_RECOMMEND_REVISION,
90+
action: DecisionActions.DECISION_RECOMMEND_REVISION,
9291
});
9392
9493
actions.push({

src/pages/workflow/components/primary/WorkflowSubmissionStatus.vue

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<h3 v-if="message.heading" class="mb-2 text-lg-bold text-heading">
44
{{ message.heading }}
55
</h3>
6+
<p v-if="message.body1" class="mb-2 text-sm-normal">{{ message.body1 }}</p>
67
<p v-if="message.body" class="text-sm-normal">{{ message.body }}</p>
78
</div>
89
</template>
@@ -18,6 +19,7 @@ const props = defineProps({
1819
},
1920
selectedStageId: {type: Number, required: true},
2021
selectedReviewRoundId: {type: Number, required: false, default: null},
22+
contextMinReviewsPerSubmission: {type: Number, required: true},
2123
});
2224
2325
const {t, tk} = useLocalize();
@@ -26,6 +28,7 @@ const {
2628
hasNotSubmissionStartedStage,
2729
hasSubmissionPassedStage,
2830
getCurrentReviewRound,
31+
checkMinimumConsideredReviews,
2932
} = useSubmission();
3033
3134
//const activeStage = computed(() => getActiveStage(props.submission));
@@ -98,10 +101,47 @@ const message = computed(() => {
98101
};
99102
}
100103
104+
// #10363 Add minimum reviewers status
105+
const {shouldMinimumReviewsBeConsidered, hasMinimumReviewsCount} =
106+
checkMinimumConsideredReviews(
107+
props.submission,
108+
props.selectedStageId,
109+
props.selectedReviewRoundId,
110+
props.contextMinReviewsPerSubmission,
111+
);
112+
113+
if (
114+
// only for these statuses the minimum reviews completed status should take precedence
115+
shouldMinimumReviewsBeConsidered &&
116+
hasMinimumReviewsCount &&
117+
[
118+
pkp.const.REVIEW_ROUND_STATUS_PENDING_REVIEWERS,
119+
pkp.const.REVIEW_ROUND_STATUS_PENDING_REVIEWS,
120+
pkp.const.REVIEW_ROUND_STATUS_REVIEWS_READY,
121+
pkp.const.REVIEW_ROUND_STATUS_REVIEWS_COMPLETED,
122+
pkp.const.REVIEW_ROUND_STATUS_REVIEWS_OVERDUE,
123+
].includes(currentReviewRound.statusId)
124+
) {
125+
return {
126+
heading: t('notification.type.roundStatusTitle', {
127+
round: currentReviewRound.round,
128+
}),
129+
body1: t('dashboard.minimumConfirmedReviewsRequired', {
130+
number: props.contextMinReviewsPerSubmission,
131+
}),
132+
body: t('dashboard.minimumReviewsConfirmedDecisionNeeded'),
133+
};
134+
}
135+
101136
return {
102137
heading: t('notification.type.roundStatusTitle', {
103138
round: currentReviewRound.round,
104139
}),
140+
body1: shouldMinimumReviewsBeConsidered
141+
? t('dashboard.minimumConfirmedReviewsRequired', {
142+
number: props.contextMinReviewsPerSubmission,
143+
})
144+
: null,
105145
body: currentReviewRound.status,
106146
};
107147
} else if (props.selectedStageId === pkp.const.WORKFLOW_STAGE_ID_PRODUCTION) {

0 commit comments

Comments
 (0)