diff --git a/frontend/src/components/input/Password.vue b/frontend/src/components/input/Password.vue index ed1a0700c..9b490d3da 100644 --- a/frontend/src/components/input/Password.vue +++ b/frontend/src/components/input/Password.vue @@ -36,7 +36,7 @@ import {ref, watchEffect} from 'vue' import {useDebounceFn} from '@vueuse/core' import {useI18n} from 'vue-i18n' import BaseButton from '@/components/base/BaseButton.vue' -import {validatePassword} from '@/helpers/validatePasswort'; +import {validatePassword} from '@/helpers/validatePasswort' const props = withDefaults(defineProps<{ modelValue: string, diff --git a/frontend/src/components/tasks/AddTask.vue b/frontend/src/components/tasks/AddTask.vue index 4a022bda2..d7aaf173e 100644 --- a/frontend/src/components/tasks/AddTask.vue +++ b/frontend/src/components/tasks/AddTask.vue @@ -51,6 +51,7 @@ import {computed, ref} from 'vue' import {useI18n} from 'vue-i18n' import {useElementHover} from '@vueuse/core' +import { useRouter } from 'vue-router' import {RELATION_KIND} from '@/types/IRelationKind' import type {ITask} from '@/modelTypes/ITask' @@ -66,6 +67,8 @@ import {useAuthStore} from '@/stores/auth' import {useTaskStore} from '@/stores/tasks' import {useAutoHeightTextarea} from '@/composables/useAutoHeightTextarea' +import TaskService from '@/services/task' +import TaskModel from '@/models/task' const props = withDefaults(defineProps<{ defaultPosition?: number, @@ -81,6 +84,7 @@ const {textarea: newTaskInput} = useAutoHeightTextarea(newTaskTitle) const {t} = useI18n({useScope: 'global'}) const authStore = useAuthStore() const taskStore = useTaskStore() +const router = useRouter() // enable only if we don't have a modal // onStartTyping(() => { @@ -129,21 +133,56 @@ async function addTask() { const allLabels = tasksToCreate.map(({title}) => getLabelsFromPrefix(title, authStore.settings.frontendSettings.quickAddMagicMode) ?? []) await taskStore.ensureLabelsExist(allLabels.flat()) - const newTasks = tasksToCreate.map(async ({title, project}) => { + const taskCollectionService = new TaskService() + const projectIndices = new Map() + + let currentProjectId = authStore.settings.defaultProjectId + if (typeof router.currentRoute.value.params.projectId !== 'undefined') { + currentProjectId = Number(router.currentRoute.value.params.projectId) + } + + // Create a map of project indices before creating tasks + if (tasksToCreate.length > 1) { + for (const {project} of tasksToCreate) { + const projectId = project !== null + ? await taskStore.findProjectId({project, projectId: 0}) + : currentProjectId + + if (!projectIndices.has(projectId)) { + const newestTask = await taskCollectionService.getAll(new TaskModel({}), { + sort_by: ['id'], + order_by: ['desc'], + per_page: 1, + filter: `project_id = ${projectId}`, + }) + projectIndices.set(projectId, newestTask[0]?.index || 0) + } + } +} + + const newTasks = tasksToCreate.map(async ({title, project}, index) => { if (title === '') { return } // If the task has a project specified, make sure to use it - let projectId = null - if (project !== null) { - projectId = await taskStore.findProjectId({project, projectId: 0}) + const projectId = project !== null + ? await taskStore.findProjectId({project, projectId: 0}) + : currentProjectId + + // Calculate new index for this task per project + let taskIndex: number | undefined + if (tasksToCreate.length > 1) { + const lastIndex = projectIndices.get(projectId) + taskIndex = lastIndex + index + 1 } + console.log('many tasks to create', taskIndex) const task = await taskStore.createNewTask({ title, projectId: projectId || authStore.settings.defaultProjectId, position: props.defaultPosition, + index: taskIndex, }) createdTasks[title] = task return task diff --git a/frontend/src/stores/tasks.ts b/frontend/src/stores/tasks.ts index 67b092f9e..ab124c8b2 100644 --- a/frontend/src/stores/tasks.ts +++ b/frontend/src/stores/tasks.ts @@ -414,6 +414,7 @@ export const useTaskStore = defineStore('task', () => { bucketId, projectId, position, + index, } : Partial, ) { @@ -429,6 +430,7 @@ export const useTaskStore = defineStore('task', () => { projectId, bucketId, position, + index, })) } finally { cancel() @@ -467,6 +469,7 @@ export const useTaskStore = defineStore('task', () => { assignees, bucketId: bucketId || 0, position, + index, }) task.repeatAfter = parsedTask.repeats diff --git a/pkg/models/tasks.go b/pkg/models/tasks.go index 514ea195c..bc170dc39 100644 --- a/pkg/models/tasks.go +++ b/pkg/models/tasks.go @@ -738,10 +738,25 @@ func createTask(s *xorm.Session, t *Task, a web.Auth, updateAssignees bool, setB t.UID = uuid.NewString() } - // Get the index for this task - t.Index, err = getNextTaskIndex(s, t.ProjectID) - if err != nil { - return err + // Check if an index was provided, otherwise calculate a new one + if t.Index == 0 { + t.Index, err = getNextTaskIndex(s, t.ProjectID) + if err != nil { + return err + } + } else { + // Check if the provided index is already taken + exists, err := s.Where("project_id = ? AND `index` = ?", t.ProjectID, t.Index).Exist(&Task{}) + if err != nil { + return err + } + if exists { + // If the index is taken, calculate a new one + t.Index, err = getNextTaskIndex(s, t.ProjectID) + if err != nil { + return err + } + } } t.HexColor = utils.NormalizeHex(t.HexColor)