Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
2b42458
pkp/pkp-lib#11826 Add TaskTemplateManager components
blesildaramirez Sep 15, 2025
b1ea963
pkp/pkp-lib#11826 Get the templatesList from useFetch
blesildaramirez Sep 15, 2025
3a8f643
pkp/pkp-lib#11826 Add locale keys for task template manager
blesildaramirez Sep 15, 2025
4e2f61b
pkp/pkp-lib#11826 Update mock data for task templates
blesildaramirez Sep 15, 2025
2e3aa8f
pkp/pkp-lib#11826 Rename prop name from template to taskTemplate
blesildaramirez Sep 15, 2025
4ab189f
pkp/pkp-lib#11826 Add task template manager form
blesildaramirez Sep 15, 2025
4d42487
pkp/pkp-lib#11826 Support updating the auto-add toggle in the table
blesildaramirez Sep 15, 2025
8c96b99
pkp/pkp-lib#11826 Add group component for email templates
blesildaramirez Sep 18, 2025
7e025dd
pkp/pkp-lib#11826 Add mock data for the email templates per submissio…
blesildaramirez Sep 18, 2025
e2d61d8
pkp/pkp-lib#11826 Handle selecting email template in task template form
blesildaramirez Sep 18, 2025
d0d05f9
pkp/pkp-lib#11826 Update padding for TableColGroup use
blesildaramirez Sep 18, 2025
7aa27f0
pkp/pkp-lib#11826 Add ids for task template mock data
blesildaramirez Sep 19, 2025
2d7fd62
pkp/pkp-lib#11826 Remove onFinishFn prop
blesildaramirez Sep 19, 2025
c565fd7
pkp/pkp-lib#11826 Confirm email content override only once
blesildaramirez Sep 29, 2025
51185b2
pkp/pkp-lib#11826 Add TaskTemplateManager component to Settings page
blesildaramirez Sep 29, 2025
eaed7e0
Test: Support adding new email template on task template form
blesildaramirez Oct 2, 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
32 changes: 30 additions & 2 deletions public/globals.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ window.pkp = {
'common.loading': 'Loading',
'common.me': 'Me',
'common.moreActions': 'More Actions',
'common.months': 'months',
'common.name': 'Name',
'common.navigation.user': 'User Navigation',
'common.new': 'New',
Expand All @@ -243,6 +244,8 @@ window.pkp = {
'common.orderDown': 'Decrease position of {$itemTitle}',
'common.orderUp': 'Increase position of {$itemTitle}',
'common.overdue': 'Overdue',
'common.oneWeek': '1 week',
'common.oneMonth': '1 month',
'common.pageNumber': 'Page {$pageNumber}',
'common.pagination.goToPage': 'Go to {$page}',
'common.pagination.label': 'View additional pages',
Expand Down Expand Up @@ -287,6 +290,7 @@ window.pkp = {
'common.inProgress': 'In Progress',
'common.closed': 'Closed',
'common.warning': 'Warning',
'common.weeks': 'weeks',
'common.confirmUnsavedChanges':
'You have unsaved changes. Are you sure you want to cancel?',
'context.context': 'Journal',
Expand Down Expand Up @@ -415,7 +419,7 @@ window.pkp = {
'discussion.form.detailsDescription':
'Use this space to share essential information.',
'discussion.form.detailsNameDescription':
'Please enter the name for this task and discussion.',
'Please enter the name for the task and discussion.',
'discussion.form.detailsParticipantsDescription':
'You have the option to assign participants or allocate it solely to yourself.',
'discussion.form.discussionDescription':
Expand Down Expand Up @@ -539,6 +543,7 @@ window.pkp = {
'Are you sure you want to change to {$localeName} to compose this email? Any changes you have made to the subject and body of the email will be lost.',
'email.email': 'Email',
'email.subject': 'Subject',
'email.body': 'Body',
'email.to': 'To',
'fileManager.copyeditedFiles': 'Copyedited Files',
'fileManager.copyeditedFilesDescription':
Expand Down Expand Up @@ -823,6 +828,10 @@ window.pkp = {
'reviewerManager.reviewerStatus': 'Reviewer status',
'search.searchResults': 'Search Results',
semicolon: '{$label}:',
'stage.submission': 'Submission Stage',
'stage.review': 'Review Stage',
'stage.copyediting': 'Copyediting Stage',
'stage.production': 'Production 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 @@ -925,7 +934,26 @@ window.pkp = {
'taskTemplate.apply': 'Apply Template',
'taskTemplate.applyConfirmation':
"Applying this template will replace data in related fields on the form. These changes won't be saved unless you choose to save. Continue?",

'taskTemplates.title': 'Tasks and Discussions Templates',
'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.templateName': 'Task and discussion template name',
'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.add': 'Add template',
'taskTemplates.addInStage': 'Add Task and Discussion Template in {$stage}',
'taskTemplates.confirmAutoAdd': 'Confirm Automatic Addition',
'taskTemplates.confirmAutoAddEnable':
'Are you sure you want this task/discussion template to be automatically added when a submission reaches the {$stage}?',
'taskTemplates.confirmAutoAddDisable':
'Are you sure you want to stop automatically adding this task/discussion template when a submission reaches the {$stage}?',
'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.edit': 'Edit Task and Discussion Template',
'taskTemplates.dueDateFromCreationDate':
'{$dueDate} from the creation date',
'task.closeThisTask': 'Close this Task',
'task.startedBy': 'Task started by',
'task.startThisTask': 'Start this task',
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
11 changes: 3 additions & 8 deletions src/managers/DiscussionManager/useDiscussionManagerForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,7 @@ export function useDiscussionManagerForm(

const selectedParticipants =
allParticipants.value
.filter((p) =>
template.taskDetails?.participantRoles?.includes(p.roleId),
)
.filter((p) => template.participantRoles?.includes(p.roleId))
.map((p) => p.id) || [];
setValue('participants', selectedParticipants);

Expand All @@ -217,11 +215,8 @@ export function useDiscussionManagerForm(
if (isTask.value) {
setValue('taskInfoAssignee', selectedParticipants);

if (template.taskDetails.dueDate) {
setValue(
'taskInfoDueDate',
getRelativeTargetDate(template.taskDetails.dueDate),
);
if (template.dueDate) {
setValue('taskInfoDueDate', getRelativeTargetDate(template.dueDate));
}
} else {
setValue('taskInfoDueDate', 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 />
147 changes: 147 additions & 0 deletions src/managers/TaskTemplateManager/TaskTemplateManager.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
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,
name: 'Ethical Approval',
stageId: 'Submission',
participantRoles: [65536],
dueDate: 'P3M',
}),
getTemplate({
id: 5,
name: 'Adherence to Policy and Guidelines',
stageId: 'Submission',
autoAdd: false,
}),
getTemplate({id: 6, name: 'Language Review', stageId: 'Submission'}),
getTemplate({
id: 7,
name: 'Analysis of the Method',
stageId: 'Submission',
autoAdd: false,
}),
getTemplate({id: 8, name: 'Lorem ipsum dolor sit amet', stageId: 'Review'}),
getTemplate({
id: 9,
name: 'Consectetur adipiscing elit',
stageId: 'Review',
autoAdd: false,
}),
getTemplate({
id: 10,
name: 'Sed do eiusmod tempor incididunt ut',
stageId: 'Copyediting',
autoAdd: false,
}),
getTemplate({
id: 11,
name: 'labore et dolore magna aliqua',
stageId: 'Production',
}),
getTemplate({
id: 12,
name: 'Ut enim ad minim veniam',
stageId: 'Production',
autoAdd: false,
}),
getTemplate({
id: 13,
name: 'Quis nostrud exercitation ullamco',
stageId: 'Production',
autoAdd: 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/templates', () => {
return HttpResponse.json(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