diff --git a/frontend/src/components/quick-actions/quick-actions.vue b/frontend/src/components/quick-actions/quick-actions.vue index 34f8ec947..46e47dd80 100644 --- a/frontend/src/components/quick-actions/quick-actions.vue +++ b/frontend/src/components/quick-actions/quick-actions.vue @@ -350,26 +350,6 @@ const isNewTaskCommand = computed(() => ( const taskSearchTimeout = ref | null>(null) -type Filter = { by: string, value: string | number, comparator: string } - -function filtersToParams(filters: Filter[]) { - const filter_by: Filter['by'][] = [] - const filter_value: Filter['value'][] = [] - const filter_comparator: Filter['comparator'][] = [] - - filters.forEach(({by, value, comparator}) => { - filter_by.push(by) - filter_value.push(value) - filter_comparator.push(comparator) - }) - - return { - filter_by, - filter_value, - filter_comparator, - } -} - function searchTasks() { if ( searchMode.value !== SEARCH_MODE.ALL && @@ -391,40 +371,27 @@ function searchTasks() { const {text, project: projectName, labels} = parsedQuery.value - const filters: Filter[] = [] - - // FIXME: improve types - function addFilter( - by: Filter['by'], - value: Filter['value'], - comparator: Filter['comparator'], - ) { - filters.push({ - by, - value, - comparator, - }) - } - + let filter = '' + if (projectName !== null) { const project = projectStore.findProjectByExactname(projectName) console.log({project}) if (project !== null) { - addFilter('project_id', project.id, 'equals') + filter += ' project = ' + project.id } } if (labels.length > 0) { const labelIds = labelStore.getLabelsByExactTitles(labels).map((l) => l.id) if (labelIds.length > 0) { - addFilter('labels', labelIds.join(), 'in') + filter += 'labels in ' + labelIds.join(', ') } } const params = { s: text, sort_by: 'done', - ...filtersToParams(filters), + filter, } taskSearchTimeout.value = setTimeout(async () => { diff --git a/frontend/src/composables/useTaskList.ts b/frontend/src/composables/useTaskList.ts index 7b05375e8..ef048dc7a 100644 --- a/frontend/src/composables/useTaskList.ts +++ b/frontend/src/composables/useTaskList.ts @@ -2,7 +2,7 @@ import {ref, shallowReactive, watch, computed, type ComputedGetter} from 'vue' import {useRoute} from 'vue-router' import {useRouteQuery} from '@vueuse/router' -import TaskCollectionService, {getDefaultTaskFilterParams} from '@/services/taskCollection' +import TaskCollectionService, {getDefaultTaskFilterParams, type TaskFilterParams} from '@/services/taskCollection' import type {ITask} from '@/modelTypes/ITask' import {error} from '@/message' import type {IProject} from '@/modelTypes/IProject' @@ -58,7 +58,7 @@ export function useTaskList(projectIdGetter: ComputedGetter, sor const projectId = computed(() => projectIdGetter()) - const params = ref({...getDefaultTaskFilterParams()}) + const params = ref({...getDefaultTaskFilterParams()}) const search = ref('') const page = useRouteQuery('page', '1', { transform: Number }) diff --git a/frontend/src/services/taskCollection.ts b/frontend/src/services/taskCollection.ts index 965ad05b3..2cc39773f 100644 --- a/frontend/src/services/taskCollection.ts +++ b/frontend/src/services/taskCollection.ts @@ -4,12 +4,13 @@ import TaskModel from '@/models/task' import type {ITask} from '@/modelTypes/ITask' export interface TaskFilterParams { - sort_by: ('start_date' | 'done' | 'id' | 'position')[], + sort_by: ('start_date' | 'done' | 'id' | 'position' | 'kanban_position')[], order_by: ('asc' | 'desc')[], filter: string, filter_include_nulls: boolean, - filter_timezone: string, + filter_timezone?: string, s: string, + per_page?: number, } export function getDefaultTaskFilterParams(): TaskFilterParams { diff --git a/frontend/src/stores/kanban.ts b/frontend/src/stores/kanban.ts index 6c0e7b044..997cdeac2 100644 --- a/frontend/src/stores/kanban.ts +++ b/frontend/src/stores/kanban.ts @@ -1,5 +1,5 @@ import {computed, readonly, ref} from 'vue' -import {defineStore, acceptHMRUpdate} from 'pinia' +import {acceptHMRUpdate, defineStore} from 'pinia' import {klona} from 'klona/lite' import {findById, findIndexById} from '@/helpers/utils' @@ -20,7 +20,7 @@ const TASKS_PER_BUCKET = 25 function getTaskIndicesById(buckets: IBucket[], taskId: ITask['id']) { let taskIndex - const bucketIndex = buckets.findIndex(({ tasks }) => { + const bucketIndex = buckets.findIndex(({tasks}) => { taskIndex = findIndexById(tasks, taskId) return taskIndex !== -1 }) @@ -28,12 +28,12 @@ function getTaskIndicesById(buckets: IBucket[], taskId: ITask['id']) { return { bucketIndex: bucketIndex !== -1 ? bucketIndex : null, taskIndex: taskIndex !== -1 ? taskIndex : null, - } + } } const addTaskToBucketAndSort = (buckets: IBucket[], task: ITask) => { const bucketIndex = findIndexById(buckets, task.bucketId) - if(typeof buckets[bucketIndex] === 'undefined') { + if (typeof buckets[bucketIndex] === 'undefined') { return } buckets[bucketIndex].tasks.push(task) @@ -46,19 +46,19 @@ const addTaskToBucketAndSort = (buckets: IBucket[], task: ITask) => { */ export const useKanbanStore = defineStore('kanban', () => { const authStore = useAuthStore() - + const buckets = ref([]) const projectId = ref(0) - const bucketLoading = ref<{[id: IBucket['id']]: boolean}>({}) - const taskPagesPerBucket = ref<{[id: IBucket['id']]: number}>({}) - const allTasksLoadedForBucket = ref<{[id: IBucket['id']]: boolean}>({}) + const bucketLoading = ref<{ [id: IBucket['id']]: boolean }>({}) + const taskPagesPerBucket = ref<{ [id: IBucket['id']]: number }>({}) + const allTasksLoadedForBucket = ref<{ [id: IBucket['id']]: boolean }>({}) const isLoading = ref(false) const getBucketById = computed(() => (bucketId: IBucket['id']): IBucket | undefined => findById(buckets.value, bucketId)) const getTaskById = computed(() => { return (id: ITask['id']) => { - const { bucketIndex, taskIndex } = getTaskIndicesById(buckets.value, id) - + const {bucketIndex, taskIndex} = getTaskIndicesById(buckets.value, id) + return { bucketIndex, taskIndex, @@ -98,9 +98,9 @@ export const useKanbanStore = defineStore('kanban', () => { } function setBucketByIndex({ - bucketIndex, - bucket, - } : { + bucketIndex, + bucket, + }: { bucketIndex: number, bucket: IBucket }) { @@ -108,10 +108,10 @@ export const useKanbanStore = defineStore('kanban', () => { } function setTaskInBucketByIndex({ - bucketIndex, - taskIndex, - task, - } : { + bucketIndex, + taskIndex, + task, + }: { bucketIndex: number, taskIndex: number, task: ITask @@ -201,7 +201,7 @@ export const useKanbanStore = defineStore('kanban', () => { return } - const { bucketIndex, taskIndex } = getTaskIndicesById(buckets.value, task.id) + const {bucketIndex, taskIndex} = getTaskIndicesById(buckets.value, task.id) if ( bucketIndex === null || @@ -211,16 +211,16 @@ export const useKanbanStore = defineStore('kanban', () => { ) { return } - + buckets.value[bucketIndex].tasks.splice(taskIndex, 1) buckets.value[bucketIndex].count-- } - function setBucketLoading({bucketId, loading}: {bucketId: IBucket['id'], loading: boolean}) { + function setBucketLoading({bucketId, loading}: { bucketId: IBucket['id'], loading: boolean }) { bucketLoading.value[bucketId] = loading } - function setTasksLoadedForBucketPage({bucketId, page}: {bucketId: IBucket['id'], page: number}) { + function setTasksLoadedForBucketPage({bucketId, page}: { bucketId: IBucket['id'], page: number }) { taskPagesPerBucket.value[bucketId] = page } @@ -228,7 +228,7 @@ export const useKanbanStore = defineStore('kanban', () => { allTasksLoadedForBucket.value[bucketId] = true } - async function loadBucketsForProject({projectId, params}: {projectId: IProject['id'], params}) { + async function loadBucketsForProject({projectId, params}: { projectId: IProject['id'], params }) { const cancel = setModuleLoading(setIsLoading) // Clear everything to prevent having old buckets in the project if loading the buckets from this project takes a few moments @@ -269,29 +269,11 @@ export const useKanbanStore = defineStore('kanban', () => { setBucketLoading({bucketId: bucketId, loading: true}) const params: TaskFilterParams = JSON.parse(JSON.stringify(ps)) - - params.sort_by = 'kanban_position' - params.order_by = 'asc' - - let hasBucketFilter = false - for (const f in params.filter_by) { - if (params.filter_by[f] === 'bucket_id') { - hasBucketFilter = true - if (params.filter_value[f] !== bucketId) { - params.filter_value[f] = bucketId - } - break - } - } - - if (!hasBucketFilter) { - params.filter_by = [...(params.filter_by ?? []), 'bucket_id'] - params.filter_value = [...(params.filter_value ?? []), bucketId] - params.filter_comparator = [...(params.filter_comparator ?? []), 'equals'] - } + params.sort_by = ['kanban_position'] + params.order_by = ['asc'] + params.filter = `${params.filter === '' ? '' : params.filter + ' && '}bucket_id = ${bucketId}` params.filter_timezone = authStore.settings.timezone - params.per_page = TASKS_PER_BUCKET const taskService = new TaskCollectionService() @@ -322,7 +304,7 @@ export const useKanbanStore = defineStore('kanban', () => { } } - async function deleteBucket({bucket, params}: {bucket: IBucket, params}) { + async function deleteBucket({bucket, params}: { bucket: IBucket, params }) { const cancel = setModuleLoading(setIsLoading) const bucketService = new BucketService() @@ -349,13 +331,13 @@ export const useKanbanStore = defineStore('kanban', () => { } setBucketByIndex({bucketIndex, bucket: updatedBucket}) - + const bucketService = new BucketService() try { const returnedBucket = await bucketService.update(updatedBucket) setBucketByIndex({bucketIndex, bucket: returnedBucket}) return returnedBucket - } catch(e) { + } catch (e) { // restore original state setBucketByIndex({bucketIndex, bucket: oldBucket}) @@ -365,7 +347,7 @@ export const useKanbanStore = defineStore('kanban', () => { } } - async function updateBucketTitle({ id, title }: { id: IBucket['id'], title: IBucket['title'] }) { + async function updateBucketTitle({id, title}: { id: IBucket['id'], title: IBucket['title'] }) { const bucket = findById(buckets.value, id) if (bucket?.title === title) { @@ -373,14 +355,14 @@ export const useKanbanStore = defineStore('kanban', () => { return } - await updateBucket({ id, title }) + await updateBucket({id, title}) success({message: i18n.global.t('project.kanban.bucketTitleSavedSuccess')}) } - + return { buckets, isLoading: readonly(isLoading), - + getBucketById, getTaskById, @@ -401,5 +383,5 @@ export const useKanbanStore = defineStore('kanban', () => { // support hot reloading if (import.meta.hot) { - import.meta.hot.accept(acceptHMRUpdate(useKanbanStore, import.meta.hot)) + import.meta.hot.accept(acceptHMRUpdate(useKanbanStore, import.meta.hot)) } \ No newline at end of file diff --git a/frontend/src/stores/tasks.ts b/frontend/src/stores/tasks.ts index 9cf3ab3e5..332a4eba7 100644 --- a/frontend/src/stores/tasks.ts +++ b/frontend/src/stores/tasks.ts @@ -126,7 +126,7 @@ export const useTaskStore = defineStore('task', () => { async function loadTasks(params: TaskFilterParams, projectId: IProject['id'] | null = null) { - if (params.filter_timezone === '') { + if (!params.filter_timezone || params.filter_timezone === '') { params.filter_timezone = authStore.settings.timezone } diff --git a/frontend/src/views/project/ProjectTable.vue b/frontend/src/views/project/ProjectTable.vue index 8ed043f8a..48255f9c1 100644 --- a/frontend/src/views/project/ProjectTable.vue +++ b/frontend/src/views/project/ProjectTable.vue @@ -333,9 +333,7 @@ const { const tasks: Ref = taskList.tasks Object.assign(params.value, { - filter_by: [], - filter_value: [], - filter_comparator: [], + filter: '', }) // FIXME: by doing this we can have multiple sort orders diff --git a/frontend/src/views/tasks/ShowTasks.vue b/frontend/src/views/tasks/ShowTasks.vue index 899edd04c..f947696db 100644 --- a/frontend/src/views/tasks/ShowTasks.vue +++ b/frontend/src/views/tasks/ShowTasks.vue @@ -182,29 +182,21 @@ async function loadPendingTasks(from: string, to: string) { return } - const params = { - sortBy: ['due_date', 'id'], - orderBy: ['asc', 'desc'], - filterTimezone: authStore.settings.timezone, - filterBy: ['done'], - filterValue: ['false'], - filterComparator: ['equals'], - filterConcat: 'and', - filterIncludeNulls: showNulls, + const params: TaskFilterParams = { + sort_by: ['due_date', 'id'], + order_by: ['asc', 'desc'], + filter: 'done = false', + filter_include_nulls: showNulls, } if (!showAll.value) { - params.filterBy.push('due_date') - params.filterValue.push(to) - params.filterComparator.push('less') + params.filter += ` && due_date < '${to}'` // NOTE: Ideally we could also show tasks with a start or end date in the specified range, but the api // is not capable (yet) of combining multiple filters with 'and' and 'or'. if (!showOverdue) { - params.filterBy.push('due_date') - params.filterValue.push(from) - params.filterComparator.push('greater') + params.filter += ` && due_date > '${from}'` } }