Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
715a34a
pkp/pkp-lib#11826 Add TaskTemplateManager components
blesildaramirez Sep 15, 2025
b4fca27
pkp/pkp-lib#11826 Get the templatesList from useFetch
blesildaramirez Sep 15, 2025
90e12a8
pkp/pkp-lib#11826 Add locale keys for task template manager
blesildaramirez Sep 15, 2025
23f063b
pkp/pkp-lib#11826 Update mock data for task templates
blesildaramirez Sep 15, 2025
d4d6b13
pkp/pkp-lib#11826 Rename prop name from template to taskTemplate
blesildaramirez Sep 15, 2025
0a744bd
pkp/pkp-lib#11826 Add task template manager form
blesildaramirez Sep 15, 2025
7409a6d
pkp/pkp-lib#11826 Support updating the auto-add toggle in the table
blesildaramirez Sep 15, 2025
3cdd6c7
pkp/pkp-lib#11826 Add group component for email templates
blesildaramirez Sep 18, 2025
79b5d1d
pkp/pkp-lib#11826 Add mock data for the email templates per submissio…
blesildaramirez Sep 18, 2025
e040bec
pkp/pkp-lib#11826 Handle selecting email template in task template form
blesildaramirez Sep 18, 2025
af6168e
pkp/pkp-lib#11826 Update padding for TableColGroup use
blesildaramirez Sep 18, 2025
ef2639d
pkp/pkp-lib#11826 Add ids for task template mock data
blesildaramirez Sep 19, 2025
29d83ec
pkp/pkp-lib#11826 Remove onFinishFn prop
blesildaramirez Sep 19, 2025
16e934a
pkp/pkp-lib#11826 Confirm email content override only once
blesildaramirez Sep 29, 2025
9602b67
pkp/pkp-lib#11826 Add TaskTemplateManager component to Settings page
blesildaramirez Sep 29, 2025
2a03545
pkp/pkp-lib#11826 Update templates api url
blesildaramirez Oct 3, 2025
a480a01
pkp/pkp-lib#11826 Update data references for task templates
blesildaramirez Oct 3, 2025
a9be9bc
pkp/pkp-lib#11826 Update field ids for task template form
blesildaramirez Oct 3, 2025
7e695f4
pkp/pkp-lib#11826 Update task template listing and reload data when m…
blesildaramirez Oct 4, 2025
59b2f56
pkp/pkp-lib#11826 Handle adding new task template
blesildaramirez Oct 4, 2025
07328ab
pkp/pkp-lib#11826 Update allowed access to task templates
blesildaramirez Oct 7, 2025
43a6380
pkp/pkp-lib#11826 Code cleanup
blesildaramirez Oct 7, 2025
e831b8a
pkp/pkp-lib#11826 Rename TaskTemplateManagerForm with TaskTemplateMan…
blesildaramirez Oct 14, 2025
f79ee56
pkp/pkp-lib#11826 Include br element in taskTemplates.confirmAutoAddD…
blesildaramirez Oct 14, 2025
c3935e7
pkp/pkp-lib#11826 Add index prop in table cell components for task te…
blesildaramirez Oct 14, 2025
7ede077
pkp/pkp-lib#11826 Rename DiscussionManagerForm with DiscussionManager…
blesildaramirez Oct 14, 2025
96a6377
pkp/pkp-lib#11826 Polish locale key order related to task templates
blesildaramirez Oct 16, 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
28 changes: 28 additions & 0 deletions public/globals.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ window.pkp = {
'common.loaded': 'Loaded',
'common.loading': 'Loading',
'common.me': 'Me',
'common.months': 'months',
'common.moreActions': 'More Actions',
'common.name': 'Name',
'common.navigation.user': 'User Navigation',
Expand All @@ -242,6 +243,8 @@ window.pkp = {
'common.numberedMore': '{$number} more',
'common.numero': 'No',
'common.ok': 'OK',
'common.oneMonth': '1 month',
'common.oneWeek': '1 week',
'common.order': 'Order',
'common.orderDown': 'Decrease position of {$itemTitle}',
'common.orderUp': 'Increase position of {$itemTitle}',
Expand Down Expand Up @@ -287,6 +290,7 @@ window.pkp = {
'common.viewMoreDetails': 'View more details',
'common.viewWithName': 'View {$name}',
'common.warning': 'Warning',
'common.weeks': 'weeks',
'common.yes': 'Yes',
'common.yesContinue': 'Yes, Continue',
'common.yetToBegin': 'Yet to begin',
Expand Down Expand Up @@ -916,6 +920,10 @@ window.pkp = {
'reviewerManager.reviewerStatus': 'Reviewer status',
'search.searchResults': 'Search Results',
semicolon: '{$label}: ',
'stage.copyediting': 'Copyediting Stage',
'stage.production': 'Production Stage',
'stage.review': 'Review Stage',
'stage.submission': 'Submission Stage',
'stageParticipants.notify.message': 'Message',
'stats.context.downloadReport.description':
'Download a CSV/Excel spreadsheet with usage statistics for this journal matching the following parameters.',
Expand Down Expand Up @@ -1088,6 +1096,26 @@ window.pkp = {
'taskTemplate.apply': 'Apply Template',
'taskTemplate.applyConfirmation':
"Applying this template will replace information in related fields on the form. These changes won't be saved unless you choose to save. Continue?",
'taskTemplates.add': 'Add template',
'taskTemplates.addInStage': 'Add Task and Discussion Template in {$stage}',
'taskTemplates.confirmAutoAdd': 'Confirm Automatic Addition',
'taskTemplates.confirmAutoAddDisable':
'Are you sure you want to stop automatically adding this task/discussion template when a submission reaches the <b>{$stage}</b>?',
'taskTemplates.confirmAutoAddEnable':
'Are you sure you want this task/discussion template to be automatically added when a submission reaches the <b>{$stage}</b>?',
'taskTemplates.confirmEmailTemplate':
'Applying this email template will replace the discussion text in the form. The changes will not be saved unless you choose to save. Do you want to continue?',
'taskTemplates.description':
'Use this space to create templates for tasks and discussions. These templates automatically fill in the task name, due date, description, and roles, giving you a head start.',
'taskTemplates.dueDateFromCreationDate':
'{$dueDate} from the creation date',
'taskTemplates.edit': 'Edit Task and Discussion Template',
'taskTemplates.templateAutoAdd':
'Automatically add this task and discussion when a submission reaches a specific stage',
'taskTemplates.templateAutoAddInStage':
'Automatically add this task and/or discussion when a submission reaches the stage',
'taskTemplates.templateName': 'Task and discussion template name',
'taskTemplates.title': 'Tasks and Discussions Templates',
'user.affiliation': 'Affiliation',
'user.affiliations': 'Affiliations',
'user.affiliations.deleteModal.message':
Expand Down
2 changes: 2 additions & 0 deletions src/components/Container/SettingsPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import DateTimeForm from '@/components/Form/context/DateTimeForm.vue';
import DoiSetupSettingsForm from '@/components/Form/context/DoiSetupSettingsForm.vue';
import DoiRegistrationSettingsForm from '@/components/Form/context/DoiRegistrationSettingsForm.vue';
import ReviewerRecommendationManager from '@/managers/ReviewerRecommendationManager/ReviewerRecommendationManager.vue';
import TaskTemplateManager from '@/managers/TaskTemplateManager/TaskTemplateManager.vue';

export default {
name: 'SettingsPage',
Expand All @@ -22,6 +23,7 @@ export default {
DoiSetupSettingsForm,
DoiRegistrationSettingsForm,
ReviewerRecommendationManager,
TaskTemplateManager,
},
extends: Page,
data() {
Expand Down
10 changes: 8 additions & 2 deletions src/components/Table/TableColGroup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@
:id="groupId"
:colspan="tableContext.columnsCount.value"
scope="colgroup"
class="whitespace-nowrap border-b border-light bg-tertiary px-2 py-4 text-start text-lg-medium text-secondary first:border-s first:ps-3 last:border-e last:pe-3"
class="whitespace-nowrap border-b border-light bg-tertiary p-2 text-start text-lg-medium text-secondary first:border-s first:ps-3 last:border-e last:pe-3"
>
<slot />
<div class="flex w-full items-center gap-2">
<slot />

<div v-if="$slots.action" class="ms-auto">
<slot name="action" :group-id="groupId" />
</div>
</div>
</th>
</template>

Expand Down
2 changes: 1 addition & 1 deletion src/managers/DiscussionManager/DiscussionManager.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"
>
<TableRow>
<TableColGroup>
<TableColGroup class="py-4">
<Icon
:icon="itemStatus.icon"
class="h-5 w-5"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
: t('discussion.name')
}}
</span>
- {{ template.name }}
- {{ template.title }}
</div>
<div class="mt-1 text-base-normal text-secondary">
{{
Expand Down
10 changes: 5 additions & 5 deletions src/managers/DiscussionManager/useDiscussionManagerActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {useModal} from '@/composables/useModal';
import {useFetch} from '../../composables/useFetch';
import {useUrl} from '../../composables/useUrl';
import {useDiscussionManagerStatusUpdater} from './useDiscussionManagerStatusUpdater';
import DiscussionManagerForm from './DiscussionManagerForm.vue';
import DiscussionManagerFormModal from './DiscussionManagerFormModal.vue';
import DiscussionManagerFormDisplay from './DiscussionManagerFormDisplay.vue';

export const Actions = {
Expand Down Expand Up @@ -50,11 +50,11 @@ export function useDiscussionManagerActions() {
const {openSideModal, closeSideModal} = useModal();

function onCloseFn() {
closeSideModal(DiscussionManagerForm);
closeSideModal(DiscussionManagerFormModal);
}

openSideModal(
DiscussionManagerForm,
DiscussionManagerFormModal,
{
submission,
submissionStageId,
Expand Down Expand Up @@ -96,11 +96,11 @@ export function useDiscussionManagerActions() {
const {openSideModal, closeSideModal} = useModal();

function onCloseFn() {
closeSideModal(DiscussionManagerForm);
closeSideModal(DiscussionManagerFormModal);
}

openSideModal(
DiscussionManagerForm,
DiscussionManagerFormModal,
{
status: workItem.status,
submission,
Expand Down
11 changes: 4 additions & 7 deletions src/managers/DiscussionManager/useDiscussionManagerForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,13 +208,13 @@ export function useDiscussionManagerForm(

function setValuesFromTemplate(template) {
isTask.value = template.type === 'Task';
setValue('title', template.name);
setValue('title', template.title);
setValue('description', template.content);

const selectedParticipants =
allParticipants.value
.filter((p) =>
template.taskDetails?.participantRoles?.includes(p.roleId),
template.userGroups?.find((userGroup) => userGroup.id === p.roleId),
)
.map((p) => p.id) || [];
setValue('participants', selectedParticipants);
Expand All @@ -224,11 +224,8 @@ export function useDiscussionManagerForm(
if (isTask.value) {
setValue('taskInfoAssignee', selectedParticipants);

if (template.taskDetails.dueDate) {
setValue(
'dateDue',
getRelativeTargetDate(template.taskDetails.dueDate),
);
if (template.dueDate) {
setValue('dateDue', getRelativeTargetDate(template.dueDate));
}
} else {
setValue('dateDue', null);
Expand Down
17 changes: 17 additions & 0 deletions src/managers/TaskTemplateManager/TaskTemplateManager.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {
Primary,
Controls,
Stories,
Meta,
ArgTypes,
} from '@storybook/addon-docs/blocks';

import * as TaskTemplateManager from './TaskTemplateManager.stories.js';

<Meta of={TaskTemplateManager} />

# Tasks and Discussions Templates

This component displays the tasks and discussions templates in the settings area.

<ArgTypes />
154 changes: 154 additions & 0 deletions src/managers/TaskTemplateManager/TaskTemplateManager.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import {within, userEvent} from 'storybook/test';
import {http, HttpResponse} from 'msw';
import TaskTemplateManager from './TaskTemplateManager.vue';
import {
TemplatesDataMock,
getTemplate,
} from '@/mockFactories/taskDiscussionTemplates';
import {emailTemplateMock} from '@/mockFactories/emailTemplateMock';

export default {
title: 'Managers/TaskTemplateManager',
component: TaskTemplateManager,
};

const baseArgs = {
templates: [
...TemplatesDataMock,
getTemplate({
id: 4,
title: 'Ethical Approval',
stageId: 1,
userGroups: [{id: 65536}],
dueDate: 'P3M',
}),
getTemplate({
id: 5,
title: 'Adherence to Policy and Guidelines',
stageId: 1,
include: false,
}),
getTemplate({id: 6, title: 'Language Review', stageId: 1}),
getTemplate({
id: 7,
title: 'Analysis of the Method',
stageId: 1,
include: false,
}),
getTemplate({
id: 8,
title: 'Lorem ipsum dolor sit amet',
stageId: 3,
}),
getTemplate({
id: 9,
title: 'Consectetur adipiscing elit',
stageId: 3,
include: false,
}),
getTemplate({
id: 10,
title: 'Sed do eiusmod tempor incididunt ut',
stageId: 4,
include: false,
}),
getTemplate({
id: 11,
title: 'labore et dolore magna aliqua',
stageId: 5,
}),
getTemplate({
id: 12,
title: 'Ut enim ad minim veniam',
stageId: 5,
include: false,
}),
getTemplate({
id: 13,
title: 'Quis nostrud exercitation ullamco',
stageId: 5,
include: false,
}),
],
};

const renderComponent = (args) => ({
components: {TaskTemplateManager},
setup() {
return {args};
},
template: `<TaskTemplateManager v-bind="args" />`,
});

const mswHandlers = [
http.get(
'https://mock/index.php/publicknowledge/api/v1/editTaskTemplates',
() => {
return HttpResponse.json({data: baseArgs.templates});
},
),
http.get(
'https://mock/index.php/publicknowledge/api/v1/mailables/DISCUSSION_NOTIFICATION_SUBMISSION',
() => {
return HttpResponse.json(
emailTemplateMock['DISCUSSION_NOTIFICATION_SUBMISSION'],
);
},
),
http.get(
'https://mock/index.php/publicknowledge/api/v1/mailables/DISCUSSION_NOTIFICATION_REVIEW',
() => {
return HttpResponse.json(
emailTemplateMock['DISCUSSION_NOTIFICATION_REVIEW'],
);
},
),
http.get(
'https://mock/index.php/publicknowledge/api/v1/mailables/DISCUSSION_NOTIFICATION_COPYEDITING',
() => {
return HttpResponse.json(
emailTemplateMock['DISCUSSION_NOTIFICATION_COPYEDITING'],
);
},
),

http.get(
'https://mock/index.php/publicknowledge/api/v1/mailables/DISCUSSION_NOTIFICATION_PRODUCTION',
() => {
return HttpResponse.json(
emailTemplateMock['DISCUSSION_NOTIFICATION_PRODUCTION'],
);
},
),
];

export const Default = {
render: renderComponent,
args: baseArgs,
parameters: {
msw: {
handlers: mswHandlers,
},
},
};

export const AddNewTemplate = {
render: renderComponent,
args: baseArgs,
parameters: {
msw: {
handlers: mswHandlers,
},
},
play: async ({canvasElement}) => {
// Assigns canvas to the component root element
const canvas = within(canvasElement);
const user = userEvent.setup();

await user.click(
within(canvas.getByText('Submission Stage').closest('th')).getByText(
'Add template',
),
);
},
};
Loading