Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 8 additions & 1 deletion public/globals.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ window.pkp = {
*
*/
context: {
id: 1,
apiBaseUrl: 'https://mock/index.php/publicknowledge/api/v1/',
pageBaseUrl: 'https://mock/index.php/publicknowledge/',
currentLocale: 'en',
Expand Down Expand Up @@ -189,6 +188,14 @@ window.pkp = {
'admin.jobs.failed.action.redispatch': 'Try Again',
'admin.jobs.failed.action.redispatch.all': 'Requeue All Failed Jobs',
'admin.jobs.list.actions': 'Actions',
'admin.workflow.email.userGroup.assign.unrestricted':
'Mark as unrestricted',
'admin.workflow.email.userGroup.limitAccess':
'Limit access to specific roles',
'admin.workflow.email.userGroup.limitAccess.template.note':
'Select the roles that can access this template.',
'admin.workflow.email.userGroup.unrestricted.template.note':
'Unrestricted templates will be accessible to all roles.',
'article.article': 'Article',
'article.metadata': 'Metadata',
'author.users.contributor.principalContact': 'Primary Contact',
Expand Down
91 changes: 16 additions & 75 deletions src/managers/DiscussionManager/useDiscussionManagerForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {useUrl} from '@/composables/useUrl';
import {useLocalize} from '@/composables/useLocalize';
import {useFetch} from '@/composables/useFetch';
import {useCurrentUser} from '@/composables/useCurrentUser';
import {useSubmission} from '@/composables/useSubmission';
import {useDiscussionMessages} from './useDiscussionMessages';
import {
useDiscussionManagerStatusUpdater,
Expand Down Expand Up @@ -35,7 +34,6 @@ export function useDiscussionManagerForm(
const {updateStatus, startWorkItem} = useDiscussionManagerStatusUpdater(
submission.id,
);
const {getCurrentReviewAssignments} = useSubmission();
const closeModal = inject('closeModal');

const currentUser = useCurrentUser();
Expand Down Expand Up @@ -73,86 +71,36 @@ export function useDiscussionManagerForm(

function mapParticipantOptions(withSubLabel) {
return (participant) => {
const userName = participant.userName && `(${participant.userName})`;
let label = `${participant.fullName} ${userName}`;
const username = participant.username && `(${participant.username})`;
let label = `${participant.fullName} ${username}`;

if (participant.userName === currentUser.getCurrentUserName()) {
if (participant.userId === currentUser.getCurrentUserId()) {
label += ` (${t('common.me')})`;
}

const participantRoles = participant.roles
?.map((role) => role.name)
.join(t('common.commaListSeparator'));

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We actually have a localisation key for that - common.commaListSeparator. Its minor thing - but technically some languages might use different separator - thats I think the motivation for using that.

return {
label,
subLabel: withSubLabel ? participant.roleName : null,
value: participant.id,
subLabel: withSubLabel ? participantRoles : null,
value: participant.userId,
};
};
}

async function getAllParticipants() {
const reviewers = getCurrentReviewAssignments(
submission,
submissionStageId,
).map((r) => ({
fullName: r.reviewerFullName,
userName: r.reviewerUserName || '',
id: r.reviewerId,
roleName: t('user.role.reviewer'),
roleId: pkp.const.ROLE_ID_REVIEWER,
}));

const {apiUrl: participantsApiUrl} = useUrl(
`submissions/${encodeURIComponent(submission.id)}/participants/${submissionStageId}`,
`submissions/${encodeURIComponent(submission.id)}/stages/${submissionStageId}/tasks/participants`,
);

const {data: participantsData, fetch: fetchParticipants} =
useFetch(participantsApiUrl);

await fetchParticipants();

const isParticipant = participantsData.value?.some(
(p) => p.id === currentUser.getCurrentUserId(),
);

// If the current user is a site admin but not already in the participants list, add them as "Unassigned"
// This ensures site admins can always assign tasks to themselves
const siteAdmin =
currentUser.isCurrentUserSiteAdmin() && !isParticipant
? [
{
fullName: currentUser.getCurrentUserFullName(),
userName: currentUser.getCurrentUserName(),
id: currentUser.getCurrentUserId(),
roleName: t('submission.status.unassigned'),
roleId: pkp.const.ROLE_ID_SITE_ADMIN,
},
]
: [];

const list = [];
participantsData.value?.forEach((participant) => {
participant.stageAssignments?.forEach((stageAssignment) => {
list.push({
id: participant.id,
fullName: participant.fullName,
userName: participant.userName,
roleName: stageAssignment.stageAssignmentUserGroup.name,
roleId: stageAssignment.stageAssignmentUserGroup.roleId,
userGroupId: stageAssignment.stageAssignmentUserGroup.id,
});
});
});

list.sort((participantA, participantB) => {
// First, compare by roleId
if (participantA.roleId !== participantB.roleId) {
return participantA.roleId - participantB.roleId;
}

// If roleIds are equal, compare by userGroupId
return participantA.userGroupId - participantB.userGroupId;
});

return list.concat(siteAdmin).concat(reviewers);
return participantsData.value || [];
}

const participantOptions = computed(() =>
Expand All @@ -162,7 +110,7 @@ export function useDiscussionManagerForm(
const assigneeOptions = computed(() => {
return participants.value
.filter((participant) =>
selectedParticipants.value.includes(participant.id),
selectedParticipants.value.includes(participant.userId),
)
.map(mapParticipantOptions());
});
Expand Down Expand Up @@ -224,19 +172,16 @@ export function useDiscussionManagerForm(
return [];
}

const {apiUrl: taskTemplatesApiUrl} = useUrl('editTaskTemplates');
const {apiUrl: taskTemplatesApiUrl} = useUrl(
`editTaskTemplates?stageId=${submissionStageId}`,
);

const {data: taskTemplatesData, fetch: fetchTaskTemplates} =
useFetch(taskTemplatesApiUrl);

fetchTaskTemplates();

return computed(
() =>
taskTemplatesData.value?.data?.filter(
(data) => data.stageId === submissionStageId,
) || [],
);
return computed(() => taskTemplatesData.value?.data || []);
}

async function setValuesFromTemplate(template) {
Expand All @@ -254,10 +199,6 @@ export function useDiscussionManagerForm(
isTask.value =
templateData.value.type === pkp.const.EDITORIAL_TASK_TYPE_TASK;
setValue('title', templateData.value.title);
setValue(
'participants',
templateData.value.participants.map((p) => p.userId) || [],
);

setValue('taskInfoAdd', isTask.value);
await nextTick(); // wait for the date due & assignee fields to re-render based on isTask value
Expand Down
37 changes: 34 additions & 3 deletions src/managers/TaskTemplateManager/TaskTemplateManager.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,40 @@ const mswHandlers = [
},
),

http.get('https://mock/index.php/publicknowledge/api/v1/userGroups', () => {
return HttpResponse.json(UserGroupMock);
}),
http.get(
'https://mock/index.php/publicknowledge/api/v1/userGroups',
({request}) => {
const url = new URL(request.url);
const stageId = url.searchParams.get('stageIds');
switch (stageId) {
case '1':
return HttpResponse.json({
items: UserGroupMock.WORKFLOW_STAGE_ID_SUBMISSION,
itemsMax: UserGroupMock.WORKFLOW_STAGE_ID_SUBMISSION.length,
});
case '3':
return HttpResponse.json({
items: UserGroupMock.WORKFLOW_STAGE_ID_EXTERNAL_REVIEW,
itemsMax: UserGroupMock.WORKFLOW_STAGE_ID_EXTERNAL_REVIEW.length,
});
case '4':
return HttpResponse.json({
items: UserGroupMock.WORKFLOW_STAGE_ID_EDITING,
itemsMax: UserGroupMock.WORKFLOW_STAGE_ID_EDITING.length,
});
case '5':
return HttpResponse.json({
items: UserGroupMock.WORKFLOW_STAGE_ID_PRODUCTION,
itemsMax: UserGroupMock.WORKFLOW_STAGE_ID_PRODUCTION.length,
});
default:
return HttpResponse.json({
items: [],
itemsMax: 0,
});
}
},
),
];

export const Default = {
Expand Down
34 changes: 28 additions & 6 deletions src/managers/TaskTemplateManager/useTaskTemplateManagerForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ export function useTaskTemplateManagerForm({
const dataBody = {
title: formData.title,
stageId,
userGroupIds: formData.userGroupIds,
restrictToUserGroups: formData.restrictToUserGroups,
userGroupIds: formData.restrictToUserGroups
? formData.userGroupIds
: null,
include: formData.include,
dueInterval: isTask.value ? formData.dueInterval : null,
description: formData.description,
Expand Down Expand Up @@ -78,8 +81,8 @@ export function useTaskTemplateManagerForm({
};
}

function getParticipantOptions() {
const {apiUrl: userGroupsApiUrl} = useUrl('userGroups');
function getUserGroupOptions() {
const {apiUrl: userGroupsApiUrl} = useUrl(`userGroups?stageIds=${stageId}`);

const {items: userGroupsData, fetch: fetchUserGroups} = useFetchPaginated(
userGroupsApiUrl,
Expand Down Expand Up @@ -202,14 +205,33 @@ export function useTaskTemplateManagerForm({
isRequired: true,
});

addFieldOptions('restrictToUserGroups', 'radio', {
groupId: 'details',
label: t('admin.workflow.email.userGroup.assign.unrestricted'),
description: t('admin.workflow.email.userGroup.unrestricted.template.note'),
name: 'restrictToUserGroups',
options: [
{
value: false,
label: t('admin.workflow.email.userGroup.assign.unrestricted'),
},
{
value: true,
label: t('admin.workflow.email.userGroup.limitAccess'),
},
],
value: !!taskTemplate?.restrictToUserGroups,
});

addFieldOptions('userGroupIds', 'checkbox', {
groupId: 'details',
label: t('editor.submission.stageParticipants'),
description: t('discussion.form.detailsParticipantsDescription'),
label: t('admin.workflow.email.userGroup.limitAccess'),
description: t('admin.workflow.email.userGroup.limitAccess.template.note'),
name: 'userGroupIds',
options: getParticipantOptions(),
options: getUserGroupOptions(),
value: taskTemplate?.userGroups?.map(({id}) => id) || [],
isRequired: true,
showWhen: ['restrictToUserGroups', true],
});

addGroup('taskInformation', {
Expand Down
Loading