From 9e1f97da808107fc06ca2313f7ea882b2d1ade01 Mon Sep 17 00:00:00 2001 From: Lars de Ridder Date: Fri, 13 Feb 2026 22:52:32 +0100 Subject: [PATCH] feat(task): allow changing bucket from task detail view Show the current kanban bucket in the task detail subtitle after the project name. Clicking it opens a dropdown to move the task to a different bucket without leaving the detail view. Closes #2167 --- .../tasks/partials/BucketSelect.vue | 206 ++++++++++++++++++ frontend/src/i18n/lang/en.json | 2 + frontend/src/modelTypes/ITask.ts | 1 + frontend/src/models/task.ts | 1 + frontend/src/views/tasks/TaskDetailView.vue | 8 +- 5 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 frontend/src/components/tasks/partials/BucketSelect.vue diff --git a/frontend/src/components/tasks/partials/BucketSelect.vue b/frontend/src/components/tasks/partials/BucketSelect.vue new file mode 100644 index 000000000..4bbdfd07f --- /dev/null +++ b/frontend/src/components/tasks/partials/BucketSelect.vue @@ -0,0 +1,206 @@ + + + + + diff --git a/frontend/src/i18n/lang/en.json b/frontend/src/i18n/lang/en.json index 90b19a1ab..62f81b234 100644 --- a/frontend/src/i18n/lang/en.json +++ b/frontend/src/i18n/lang/en.json @@ -852,6 +852,8 @@ "updateSuccess": "The task was saved successfully.", "deleteSuccess": "The task has been deleted successfully.", "duplicateSuccess": "The task was duplicated successfully.", + "noBucket": "No bucket", + "bucketChangedSuccess": "The task bucket has been changed successfully.", "belongsToProject": "This task belongs to project '{project}'", "back": "Back to project", "due": "Due {at}", diff --git a/frontend/src/modelTypes/ITask.ts b/frontend/src/modelTypes/ITask.ts index dc5c60e03..f8504c30a 100644 --- a/frontend/src/modelTypes/ITask.ts +++ b/frontend/src/modelTypes/ITask.ts @@ -58,6 +58,7 @@ export interface ITask extends IAbstract { projectId: IProject['id'] // Meta, only used when creating a new task bucketId: IBucket['id'] + buckets: IBucket[] } export type ITaskPartialWithId = PartialWithId diff --git a/frontend/src/models/task.ts b/frontend/src/models/task.ts index 0464103d9..ecec237b6 100644 --- a/frontend/src/models/task.ts +++ b/frontend/src/models/task.ts @@ -96,6 +96,7 @@ export default class TaskModel extends AbstractModel implements ITask { projectId: IProject['id'] = 0 bucketId: IBucket['id'] = 0 + buckets: IBucket[] = [] constructor(data: Partial = {}) { super() diff --git a/frontend/src/views/tasks/TaskDetailView.vue b/frontend/src/views/tasks/TaskDetailView.vue index 8a61a498a..0a63e0e56 100644 --- a/frontend/src/views/tasks/TaskDetailView.vue +++ b/frontend/src/views/tasks/TaskDetailView.vue @@ -55,6 +55,11 @@ class="has-text-grey-light" > > + @@ -658,6 +663,7 @@ import RepeatAfter from '@/components/tasks/partials/RepeatAfter.vue' import TaskSubscription from '@/components/misc/Subscription.vue' import CustomTransition from '@/components/misc/CustomTransition.vue' import AssigneeList from '@/components/tasks/partials/AssigneeList.vue' +import BucketSelect from '@/components/tasks/partials/BucketSelect.vue' import Reactions from '@/components/input/Reactions.vue' import {uploadFile} from '@/helpers/attachments' @@ -888,7 +894,7 @@ watch( } try { - const loaded = await taskService.get({id}, {expand: ['reactions', 'comments', 'is_unread']}) + const loaded = await taskService.get({id}, {expand: ['reactions', 'comments', 'is_unread', 'buckets']}) Object.assign(task.value, loaded) attachmentStore.set(task.value.attachments) taskColor.value = task.value.hexColor