Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: task improvements #2956

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
a1c7e67
feat: task prefilter sticky
stepan662 Feb 27, 2025
03ca322
fix: taskDone filter not in history
stepan662 Feb 27, 2025
e3e633d
feat: task prefilter sticky
stepan662 Feb 27, 2025
26d03b9
fix: move hide done filter to the prefilter
stepan662 Feb 27, 2025
2b03900
fix: floating panel respect namespace margin
stepan662 Feb 27, 2025
c531344
fix: floating panel respect namespace margin
stepan662 Feb 27, 2025
7de47fe
feat: task info messages
stepan662 Mar 5, 2025
2272eb9
feat: task info messages
stepan662 Mar 5, 2025
7c2ca5e
feat: task info messages
stepan662 Mar 5, 2025
754b4fd
feat: rename task states
stepan662 Mar 5, 2025
df5a303
fix: better task state indication
stepan662 Mar 6, 2025
76d4d5a
fix: better task state indication
stepan662 Mar 6, 2025
da19b4d
chore: fix BE tests
stepan662 Mar 6, 2025
b58a663
chore: fix e2e
stepan662 Mar 6, 2025
944b49f
chore: fix backend tests
stepan662 Mar 6, 2025
f5ae41e
fix: task prefiltered view improve
stepan662 Mar 6, 2025
0e130ec
fix: task prefiltered view improve
stepan662 Mar 6, 2025
a2e73a0
chore: fix tsc
stepan662 Mar 6, 2025
44d8dda
chore: fix tests
stepan662 Mar 6, 2025
68b4601
fix: task detail
stepan662 Mar 6, 2025
50558e2
chore: e2e fix
stepan662 Mar 6, 2025
b74395c
chore: add e2e tests
stepan662 Mar 6, 2025
1c02ae5
chore: fix ktlint
stepan662 Mar 6, 2025
6cf9f39
feat: rename task notifications
stepan662 Mar 7, 2025
a9656be
feat: rename task notifications
stepan662 Mar 7, 2025
adc2bf3
feat: rename task notifications
stepan662 Mar 7, 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
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ package io.tolgee.development.testDataBuilder.data

import io.tolgee.development.testDataBuilder.builders.*
import io.tolgee.model.Language
import io.tolgee.model.enums.OrganizationRoleType
import io.tolgee.model.enums.ProjectPermissionType
import io.tolgee.model.enums.Scope
import io.tolgee.model.enums.TaskType
import io.tolgee.model.enums.*
import io.tolgee.model.task.TaskKey

class TaskTestData : BaseTestData("tasksTestUser", "Project with tasks") {
Expand Down Expand Up @@ -234,6 +231,40 @@ class TaskTestData : BaseTestData("tasksTestUser", "Project with tasks") {
}
}

fun addTaskInState(
name: String,
state: TaskState,
type: TaskType,
number: Long,
) {
projectBuilder.apply {
blockedTask =
addTask {
this.number = number
this.name = name
this.type = type
assignees =
mutableSetOf(
projectUser.self,
user,
)
project = projectBuilder.self
language = englishLanguage
author = projectUser.self
this.state = state
}

keysInTask.forEach { it ->
addTaskKey {
task = blockedTask.self
key = it.self
done = true
author = user
}
}
}
}

fun createManyOutOfTaskKeys(): List<KeyBuilder> {
val keys =
(1 until 200).map {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ package io.tolgee.model.enums
enum class TaskState {
NEW,
IN_PROGRESS,
DONE,
CLOSED,
FINISHED,
CANCELED,
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import io.tolgee.model.notifications.NotificationTypeGroup.TASKS

enum class NotificationType(val group: NotificationTypeGroup) {
TASK_ASSIGNED(TASKS),
TASK_COMPLETED(TASKS),
TASK_CLOSED(TASKS),
TASK_FINISHED(TASKS),
TASK_CANCELED(TASKS),
MFA_ENABLED(ACCOUNT_SECURITY),
MFA_DISABLED(ACCOUNT_SECURITY),
PASSWORD_CHANGED(ACCOUNT_SECURITY),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ class EmailNotificationComposer(
fun composeEmailText(notification: Notification) =
when (notification.type) {
NotificationType.TASK_ASSIGNED,
NotificationType.TASK_COMPLETED,
NotificationType.TASK_CLOSED,
NotificationType.TASK_FINISHED,
NotificationType.TASK_CANCELED,
-> taskEmailComposer
NotificationType.MFA_ENABLED,
NotificationType.MFA_DISABLED,
Expand Down
8 changes: 4 additions & 4 deletions backend/data/src/main/resources/I18n_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ Tolgee
notifications.email.view-in-tolgee-link=View in Tolgee

notifications.email.subject.TASK_ASSIGNED=Task has been assigned to you
notifications.email.subject.TASK_COMPLETED=Task has been completed
notifications.email.subject.TASK_CLOSED=Task has been closed
notifications.email.subject.TASK_FINISHED=Task has been finished
notifications.email.subject.TASK_CANCELED=Task has been canceled
notifications.email.subject.MFA_ENABLED=Multi-factor authentication has been enabled for your account
notifications.email.subject.MFA_DISABLED=Multi-factor authentication has been disabled for your account
notifications.email.subject.PASSWORD_CHANGED=Password has been changed for your account
Expand All @@ -104,8 +104,8 @@ notifications.email.task-type.TRANSLATE=translate
notifications.email.task-type.REVIEW=review

notifications.email.task-header.TASK_ASSIGNED=You''ve been assigned to a task {0} of type {1}.
notifications.email.task-header.TASK_COMPLETED=Task {0} of type {1} you''ve created has been completed.
notifications.email.task-header.TASK_CLOSED=Task {0} of type {1} you''ve created has been closed.
notifications.email.task-header.TASK_FINISHED=Task {0} of type {1} you''ve created has been finished.
notifications.email.task-header.TASK_CANCELED=Task {0} of type {1} you''ve created has been canceled.

notifications.email.my-tasks-link=Check your tasks <a href="{0}">here</a>.
notifications.email.security-settings-link=Check your security settings <a href="{0}"}">here</a>.
Expand Down
12 changes: 12 additions & 0 deletions backend/data/src/main/resources/db/changelog/schema.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4167,4 +4167,16 @@
<changeSet author="stanov (generated)" id="1740659624320-9">
<addForeignKeyConstraint baseColumnNames="linked_task_id" baseTableName="notification" constraintName="FKm1auuxar5ovwae9fqjp1eo05k" deferrable="false" initiallyDeferred="false" referencedColumnNames="id" referencedTableName="task" validate="true"/>
</changeSet>
<changeSet author="stepangranat (generated)" id="1740659624320-10">
<sql dbms="postgresql">
UPDATE task SET state = 'CANCELED' WHERE state = 'CLOSED';
UPDATE task SET state = 'FINISHED' WHERE state = 'DONE';
</sql>
</changeSet>
<changeSet author="stepangranat (generated)" id="1740659624320-11">
<sql dbms="postgresql">
UPDATE notification SET type = 'TASK_CANCELED' WHERE type = 'TASK_CLOSED';
UPDATE notification SET type = 'TASK_FINISHED' WHERE type = 'TASK_COMPLETED';
</sql>
</changeSet>
</databaseChangeLog>
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package io.tolgee.controllers.internal.e2eData
import io.swagger.v3.oas.annotations.Hidden
import io.tolgee.development.testDataBuilder.builders.TestDataBuilder
import io.tolgee.development.testDataBuilder.data.TaskTestData
import io.tolgee.model.enums.TaskState
import io.tolgee.model.enums.TaskType
import org.springframework.transaction.annotation.Transactional
import org.springframework.web.bind.annotation.CrossOrigin
import org.springframework.web.bind.annotation.GetMapping
Expand All @@ -17,9 +19,13 @@ import org.springframework.web.bind.annotation.RestController
class TaskE2eDataController() : AbstractE2eDataController() {
@GetMapping(value = ["/generate"])
@Transactional
fun generateBasicTestData() {
fun generateBasicTestData(): StandardTestDataResult {
val data = TaskTestData()
data.addBlockedTask()
data.addTaskInState("Canceled review task", TaskState.CANCELED, TaskType.REVIEW, 4)
data.addTaskInState("Finished review task", TaskState.FINISHED, TaskType.REVIEW, 5)
testDataService.saveTestData(data.root)
return getStandardResult(data.root)
}

override val testData: TestDataBuilder
Expand Down
12 changes: 9 additions & 3 deletions e2e/cypress/common/permissions/translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,15 @@ export function testTranslations({ project, languages }: ProjectInfo) {
.findDcy('translations-task-indicator')
.should('be.visible')
.trigger('mouseover');
cy.gcy('task-tooltip-content')
.contains('Assigned translate task')
.should('be.visible');
if (scopes.includes('tasks.view')) {
cy.gcy('task-tooltip-content')
.contains('Unassigned review task')
.should('be.visible');
} else {
cy.gcy('task-tooltip-content')
.contains('You have no access to view this task')
.should('be.visible');
}
getCell('English text 1')
.findDcy('translations-task-indicator')
.trigger('mouseout');
Expand Down
13 changes: 8 additions & 5 deletions e2e/cypress/e2e/notifications/notifications.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,18 @@ describe('notifications', () => {
);
targetPageShouldHaveInUrl('/translations?task=');

generateNotification(userId, 'TASK_COMPLETED');
generateNotification(userId, 'TASK_FINISHED');
assertNewestEmail(
'Task has been completed',
"you've created has been completed"
'Task has been finished',
"you've created has been finished"
);
targetPageShouldHaveInUrl('/translations?task=');

generateNotification(userId, 'TASK_CLOSED');
assertNewestEmail('Task has been closed', "you've created has been closed");
generateNotification(userId, 'TASK_CANCELED');
assertNewestEmail(
'Task has been canceled',
"you've created has been canceled"
);
targetPageShouldHaveInUrl('/translations?task=');

generateNotification(userId, 'MFA_ENABLED');
Expand Down
8 changes: 4 additions & 4 deletions e2e/cypress/e2e/tasks/myTasks.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,15 @@ describe('my tasks', () => {
editCell('Translation 0', 'New translation 0');
getCell('Translation 1').findDcy('translations-cell-task-button').click();
cy.get('#alert-dialog-title')
.contains('All items in the task are finished')
.contains('All items in the task are done')
.should('be.visible');
cy.gcy('global-confirmation-confirm').click();
visitMyTasks();
cy.gcy('task-item')
.contains('Translate task')
.closestDcy('task-item')
.findDcy('task-state')
.should('contain', 'Done');
.should('contain', 'Finished');
});

it("Organization member can finish Review task (permissions elevated because he's assigned)", () => {
Expand All @@ -79,15 +79,15 @@ describe('my tasks', () => {
cy.waitForDom();
getCell('Překlad 1').findDcy('translations-cell-task-button').click();
cy.get('#alert-dialog-title')
.contains('All items in the task are finished')
.contains('All items in the task are done')
.should('be.visible');
cy.gcy('global-confirmation-confirm').click();
visitMyTasks();
cy.gcy('task-item')
.contains('Review task')
.closestDcy('task-item')
.findDcy('task-state')
.should('contain', 'Done');
.should('contain', 'Finished');
});

it("Organization member can add comments (permissions elevated because he's assigned)", () => {
Expand Down
75 changes: 75 additions & 0 deletions e2e/cypress/e2e/tasks/tasksEditAlerts.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { login } from '../../common/apiCalls/common';
import { TestDataStandardResponse } from '../../common/apiCalls/testData/generator';
import { tasks } from '../../common/apiCalls/testData/testData';
import { visitMyTasks, visitTasks } from '../../common/tasks';
import { getCell, visitTranslations } from '../../common/translations';

describe('tasks edit alerts', () => {
let testData: TestDataStandardResponse;
beforeEach(() => {
tasks.clean({ failOnStatusCode: false });
tasks
.generate()
.then((r) => r.body)
.then((data) => {
testData = data;
});
});

function loginAsUser(user: string) {
login(
testData.users.find((u) => [u.username, u.name].includes(user))?.username
);
}
function goToProject(name: string) {
visitTranslations(testData.projects.find((p) => p.name === name)?.id);
}

it('user not assigned to a task', () => {
loginAsUser('Organization owner');
goToProject('Project with tasks');
getCell('Překlad 1').click();

cy.gcy('task-info-message').contains('not assigned to you');
});

it('user assigned to task, but not in task view', () => {
loginAsUser('Tasks test user');
goToProject('Project with tasks');
getCell('Překlad 1').click();

cy.gcy('task-info-message').contains('This is part of a review task');
});

it('info about blocked task', () => {
loginAsUser('Tasks test user');
visitMyTasks();
cy.gcy('task-item').contains('Blocked task').click();
getCell('Translation 1').click();
cy.gcy('task-info-message').contains('is blocked');
});

it('info about finished task', () => {
loginAsUser('admin');
visitTasks(
testData.projects.find((p) => p.name === 'Project with tasks')?.id
);

cy.gcy('task-item').contains('Finished review task').click();

getCell('Translation 1').click();
cy.gcy('task-info-message').contains('is finished');
});

it('info about canceled task', () => {
loginAsUser('admin');
visitTasks(
testData.projects.find((p) => p.name === 'Project with tasks')?.id
);

cy.gcy('task-item').contains('Canceled review task').click();

getCell('Translation 1').click();
cy.gcy('task-info-message').contains('is canceled');
});
});
5 changes: 5 additions & 0 deletions e2e/cypress/support/dataCyType.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,7 @@ declare namespace DataCy {
"task-detail" |
"task-detail-author" |
"task-detail-characters" |
"task-detail-close" |
"task-detail-closed-at" |
"task-detail-created-at" |
"task-detail-download-report" |
Expand All @@ -554,10 +555,14 @@ declare namespace DataCy {
"task-detail-user-keys" |
"task-detail-user-words" |
"task-detail-words" |
"task-info-message" |
"task-item" |
"task-item-detail" |
"task-item-menu" |
"task-label-name" |
"task-menu-item-cancel-task" |
"task-menu-item-mark-as-done" |
"task-menu-item-reopen" |
"task-number" |
"task-preview" |
"task-preview-alert" |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,12 +176,12 @@ class TaskController(
): TaskModel {
// users can only finish tasks assigned to them
securityService.hasTaskEditScopeOrIsAssigned(projectHolder.project.id, taskNumber)
val task = taskService.setTaskState(projectHolder.project.id, taskNumber, TaskState.DONE)
val task = taskService.setTaskState(projectHolder.project.id, taskNumber, TaskState.FINISHED)
return taskModelAssembler.toModel(task)
}

@PutMapping("/{taskNumber}/close")
@Operation(summary = "Close task")
@Operation(summary = "Close task", deprecated = true)
@RequiresProjectPermissions([Scope.TASKS_EDIT])
@AllowApiAccess
@RequestActivity(ActivityType.TASK_CLOSE)
Expand All @@ -190,7 +190,21 @@ class TaskController(
@PathVariable
taskNumber: Long,
): TaskModel {
val task = taskService.setTaskState(projectHolder.project.id, taskNumber, TaskState.CLOSED)
val task = taskService.setTaskState(projectHolder.project.id, taskNumber, TaskState.CANCELED)
return taskModelAssembler.toModel(task)
}

@PutMapping("/{taskNumber}/cancel")
@Operation(summary = "Close task")
@RequiresProjectPermissions([Scope.TASKS_EDIT])
@AllowApiAccess
@RequestActivity(ActivityType.TASK_CLOSE)
@OpenApiOrderExtension(7)
fun cancelTask(
@PathVariable
taskNumber: Long,
): TaskModel {
val task = taskService.setTaskState(projectHolder.project.id, taskNumber, TaskState.CANCELED)
return taskModelAssembler.toModel(task)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,6 @@ open class TaskFilters {
)
var filterAgency: List<Long>? = null

@Deprecated("Confusing logic and naming", ReplaceWith("filterNotClosedBefore"))
@field:Parameter(
description = """Exclude "done" tasks which are older than specified timestamp""",
)
var filterDoneMinClosedAt: Long? = null

@field:Parameter(
description = """Exclude tasks which were closed before specified timestamp""",
)
Expand Down
Loading
Loading