From decee24e12014609c239aa8066e5c3e3c89f5816 Mon Sep 17 00:00:00 2001 From: kolaente Date: Thu, 11 Sep 2025 22:26:52 +0200 Subject: [PATCH] fix(task): provide back button when opening task detail (#1475) --- frontend/cypress/e2e/task/task.spec.ts | 63 +++++++++++++++++++++ frontend/src/i18n/lang/en.json | 1 + frontend/src/views/tasks/TaskDetailView.vue | 16 +++++- 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/frontend/cypress/e2e/task/task.spec.ts b/frontend/cypress/e2e/task/task.spec.ts index f36fa4814..ac778f1ed 100644 --- a/frontend/cypress/e2e/task/task.spec.ts +++ b/frontend/cypress/e2e/task/task.spec.ts @@ -194,6 +194,68 @@ describe('Task', () => { LabelTaskFactory.truncate() TaskAttachmentFactory.truncate() }) + + it('provides back navigation to the project in the list view', () => { + const tasks = TaskFactory.create(1) + cy.intercept('**/projects/1/views/*/tasks**').as('loadTasks') + cy.visit('/projects/1/1') + cy.wait('@loadTasks') + cy.get('.list-view .task') + .first() + .find('a.task-link') + .click() + cy.get('.task-view .back-button') + .should('be.visible') + .click() + cy.location('pathname').should('match', /\/projects\/1\/\d+/) + }) + + it('provides back navigation to the project in the table view', () => { + const tasks = TaskFactory.create(1) + cy.intercept('**/projects/1/views/*/tasks**').as('loadTasks') + cy.visit('/projects/1/3') + cy.wait('@loadTasks') + cy.get('tbody tr') + .first() + .find('a') + .first() + .click() + cy.get('.task-view .back-button') + .should('be.visible') + .click() + cy.location('pathname').should('match', /\/projects\/1\/\d+/) + }) + + it('provides back navigation to the project in the kanban view on mobile', () => { + cy.viewport('iphone-8') + + const tasks = TaskFactory.create(1) + cy.intercept('**/projects/1/views/*/tasks**').as('loadTasks') + cy.visit('/projects/1/4') + cy.wait('@loadTasks') + cy.get('.kanban-view .tasks .task') + .first() + .click() + cy.get('.task-view .back-button') + .should('be.visible') + .click() + cy.location('pathname').should('match', /\/projects\/1\/\d+/) + }) + + it('does not provide back navigation to the project in the kanban view on desktop', () => { + cy.viewport('macbook-15') + + const tasks = TaskFactory.create(1) + cy.intercept('**/projects/1/views/*/tasks**').as('loadTasks') + cy.visit('/projects/1/4') + cy.wait('@loadTasks') + cy.get('.kanban-view .tasks .task') + .first() + .click() + cy.get('.task-view .back-button') + .should('not.exist') + }) + it('Shows a 404 page for nonexisting tasks', () => { cy.visit('/tasks/9999') @@ -201,6 +263,7 @@ describe('Task', () => { cy.contains('Not found') .should('be.visible') }) + it('Shows all task details', () => { const tasks = TaskFactory.create(1, { id: 1, diff --git a/frontend/src/i18n/lang/en.json b/frontend/src/i18n/lang/en.json index 6810c4a64..400942c70 100644 --- a/frontend/src/i18n/lang/en.json +++ b/frontend/src/i18n/lang/en.json @@ -813,6 +813,7 @@ "updateSuccess": "The task was saved successfully.", "deleteSuccess": "The task has been deleted successfully.", "belongsToProject": "This task belongs to project '{project}'", + "back": "Back to project", "due": "Due {at}", "closePopup": "Close popup", "organization": "Organization", diff --git a/frontend/src/views/tasks/TaskDetailView.vue b/frontend/src/views/tasks/TaskDetailView.vue index 42373c4e0..59658eacb 100644 --- a/frontend/src/views/tasks/TaskDetailView.vue +++ b/frontend/src/views/tasks/TaskDetailView.vue @@ -11,6 +11,14 @@ v-if="visible" class="task-view" > + + + {{ $t('task.detail.back') }} + projectStore.projects[task.value.projectId]) +const projectRoute = computed(() => ({ + name: 'project.index', + params: {projectId: task.value.projectId}, +})) + const canWrite = computed(() => ( task.value.maxPermission !== null && task.value.maxPermission > PERMISSIONS.READ @@ -744,6 +757,7 @@ const color = computed(() => { }) const isModal = computed(() => Boolean(props.backdropView)) +const isMobile = useMediaQuery('(max-width: 1024px)') function attachmentUpload(file: File, onSuccess?: (url: string) => void) { return uploadFile(props.taskId, file, onSuccess)