fix(tasks): use correct filter query when filtering
This commit is contained in:
parent
a66e26678e
commit
e097721817
@ -350,26 +350,6 @@ const isNewTaskCommand = computed(() => (
|
|||||||
|
|
||||||
const taskSearchTimeout = ref<ReturnType<typeof setTimeout> | null>(null)
|
const taskSearchTimeout = ref<ReturnType<typeof setTimeout> | 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() {
|
function searchTasks() {
|
||||||
if (
|
if (
|
||||||
searchMode.value !== SEARCH_MODE.ALL &&
|
searchMode.value !== SEARCH_MODE.ALL &&
|
||||||
@ -391,40 +371,27 @@ function searchTasks() {
|
|||||||
|
|
||||||
const {text, project: projectName, labels} = parsedQuery.value
|
const {text, project: projectName, labels} = parsedQuery.value
|
||||||
|
|
||||||
const filters: Filter[] = []
|
let filter = ''
|
||||||
|
|
||||||
// FIXME: improve types
|
|
||||||
function addFilter(
|
|
||||||
by: Filter['by'],
|
|
||||||
value: Filter['value'],
|
|
||||||
comparator: Filter['comparator'],
|
|
||||||
) {
|
|
||||||
filters.push({
|
|
||||||
by,
|
|
||||||
value,
|
|
||||||
comparator,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (projectName !== null) {
|
if (projectName !== null) {
|
||||||
const project = projectStore.findProjectByExactname(projectName)
|
const project = projectStore.findProjectByExactname(projectName)
|
||||||
console.log({project})
|
console.log({project})
|
||||||
if (project !== null) {
|
if (project !== null) {
|
||||||
addFilter('project_id', project.id, 'equals')
|
filter += ' project = ' + project.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (labels.length > 0) {
|
if (labels.length > 0) {
|
||||||
const labelIds = labelStore.getLabelsByExactTitles(labels).map((l) => l.id)
|
const labelIds = labelStore.getLabelsByExactTitles(labels).map((l) => l.id)
|
||||||
if (labelIds.length > 0) {
|
if (labelIds.length > 0) {
|
||||||
addFilter('labels', labelIds.join(), 'in')
|
filter += 'labels in ' + labelIds.join(', ')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
s: text,
|
s: text,
|
||||||
sort_by: 'done',
|
sort_by: 'done',
|
||||||
...filtersToParams(filters),
|
filter,
|
||||||
}
|
}
|
||||||
|
|
||||||
taskSearchTimeout.value = setTimeout(async () => {
|
taskSearchTimeout.value = setTimeout(async () => {
|
||||||
|
@ -2,7 +2,7 @@ import {ref, shallowReactive, watch, computed, type ComputedGetter} from 'vue'
|
|||||||
import {useRoute} from 'vue-router'
|
import {useRoute} from 'vue-router'
|
||||||
import {useRouteQuery} from '@vueuse/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 type {ITask} from '@/modelTypes/ITask'
|
||||||
import {error} from '@/message'
|
import {error} from '@/message'
|
||||||
import type {IProject} from '@/modelTypes/IProject'
|
import type {IProject} from '@/modelTypes/IProject'
|
||||||
@ -58,7 +58,7 @@ export function useTaskList(projectIdGetter: ComputedGetter<IProject['id']>, sor
|
|||||||
|
|
||||||
const projectId = computed(() => projectIdGetter())
|
const projectId = computed(() => projectIdGetter())
|
||||||
|
|
||||||
const params = ref({...getDefaultTaskFilterParams()})
|
const params = ref<TaskFilterParams>({...getDefaultTaskFilterParams()})
|
||||||
|
|
||||||
const search = ref('')
|
const search = ref('')
|
||||||
const page = useRouteQuery('page', '1', { transform: Number })
|
const page = useRouteQuery('page', '1', { transform: Number })
|
||||||
|
@ -4,12 +4,13 @@ import TaskModel from '@/models/task'
|
|||||||
import type {ITask} from '@/modelTypes/ITask'
|
import type {ITask} from '@/modelTypes/ITask'
|
||||||
|
|
||||||
export interface TaskFilterParams {
|
export interface TaskFilterParams {
|
||||||
sort_by: ('start_date' | 'done' | 'id' | 'position')[],
|
sort_by: ('start_date' | 'done' | 'id' | 'position' | 'kanban_position')[],
|
||||||
order_by: ('asc' | 'desc')[],
|
order_by: ('asc' | 'desc')[],
|
||||||
filter: string,
|
filter: string,
|
||||||
filter_include_nulls: boolean,
|
filter_include_nulls: boolean,
|
||||||
filter_timezone: string,
|
filter_timezone?: string,
|
||||||
s: string,
|
s: string,
|
||||||
|
per_page?: number,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDefaultTaskFilterParams(): TaskFilterParams {
|
export function getDefaultTaskFilterParams(): TaskFilterParams {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {computed, readonly, ref} from 'vue'
|
import {computed, readonly, ref} from 'vue'
|
||||||
import {defineStore, acceptHMRUpdate} from 'pinia'
|
import {acceptHMRUpdate, defineStore} from 'pinia'
|
||||||
import {klona} from 'klona/lite'
|
import {klona} from 'klona/lite'
|
||||||
|
|
||||||
import {findById, findIndexById} from '@/helpers/utils'
|
import {findById, findIndexById} from '@/helpers/utils'
|
||||||
@ -20,7 +20,7 @@ const TASKS_PER_BUCKET = 25
|
|||||||
|
|
||||||
function getTaskIndicesById(buckets: IBucket[], taskId: ITask['id']) {
|
function getTaskIndicesById(buckets: IBucket[], taskId: ITask['id']) {
|
||||||
let taskIndex
|
let taskIndex
|
||||||
const bucketIndex = buckets.findIndex(({ tasks }) => {
|
const bucketIndex = buckets.findIndex(({tasks}) => {
|
||||||
taskIndex = findIndexById(tasks, taskId)
|
taskIndex = findIndexById(tasks, taskId)
|
||||||
return taskIndex !== -1
|
return taskIndex !== -1
|
||||||
})
|
})
|
||||||
@ -28,12 +28,12 @@ function getTaskIndicesById(buckets: IBucket[], taskId: ITask['id']) {
|
|||||||
return {
|
return {
|
||||||
bucketIndex: bucketIndex !== -1 ? bucketIndex : null,
|
bucketIndex: bucketIndex !== -1 ? bucketIndex : null,
|
||||||
taskIndex: taskIndex !== -1 ? taskIndex : null,
|
taskIndex: taskIndex !== -1 ? taskIndex : null,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const addTaskToBucketAndSort = (buckets: IBucket[], task: ITask) => {
|
const addTaskToBucketAndSort = (buckets: IBucket[], task: ITask) => {
|
||||||
const bucketIndex = findIndexById(buckets, task.bucketId)
|
const bucketIndex = findIndexById(buckets, task.bucketId)
|
||||||
if(typeof buckets[bucketIndex] === 'undefined') {
|
if (typeof buckets[bucketIndex] === 'undefined') {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
buckets[bucketIndex].tasks.push(task)
|
buckets[bucketIndex].tasks.push(task)
|
||||||
@ -46,19 +46,19 @@ const addTaskToBucketAndSort = (buckets: IBucket[], task: ITask) => {
|
|||||||
*/
|
*/
|
||||||
export const useKanbanStore = defineStore('kanban', () => {
|
export const useKanbanStore = defineStore('kanban', () => {
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
|
|
||||||
const buckets = ref<IBucket[]>([])
|
const buckets = ref<IBucket[]>([])
|
||||||
const projectId = ref<IProject['id']>(0)
|
const projectId = ref<IProject['id']>(0)
|
||||||
const bucketLoading = ref<{[id: IBucket['id']]: boolean}>({})
|
const bucketLoading = ref<{ [id: IBucket['id']]: boolean }>({})
|
||||||
const taskPagesPerBucket = ref<{[id: IBucket['id']]: number}>({})
|
const taskPagesPerBucket = ref<{ [id: IBucket['id']]: number }>({})
|
||||||
const allTasksLoadedForBucket = ref<{[id: IBucket['id']]: boolean}>({})
|
const allTasksLoadedForBucket = ref<{ [id: IBucket['id']]: boolean }>({})
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
|
|
||||||
const getBucketById = computed(() => (bucketId: IBucket['id']): IBucket | undefined => findById(buckets.value, bucketId))
|
const getBucketById = computed(() => (bucketId: IBucket['id']): IBucket | undefined => findById(buckets.value, bucketId))
|
||||||
const getTaskById = computed(() => {
|
const getTaskById = computed(() => {
|
||||||
return (id: ITask['id']) => {
|
return (id: ITask['id']) => {
|
||||||
const { bucketIndex, taskIndex } = getTaskIndicesById(buckets.value, id)
|
const {bucketIndex, taskIndex} = getTaskIndicesById(buckets.value, id)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
bucketIndex,
|
bucketIndex,
|
||||||
taskIndex,
|
taskIndex,
|
||||||
@ -98,9 +98,9 @@ export const useKanbanStore = defineStore('kanban', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setBucketByIndex({
|
function setBucketByIndex({
|
||||||
bucketIndex,
|
bucketIndex,
|
||||||
bucket,
|
bucket,
|
||||||
} : {
|
}: {
|
||||||
bucketIndex: number,
|
bucketIndex: number,
|
||||||
bucket: IBucket
|
bucket: IBucket
|
||||||
}) {
|
}) {
|
||||||
@ -108,10 +108,10 @@ export const useKanbanStore = defineStore('kanban', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setTaskInBucketByIndex({
|
function setTaskInBucketByIndex({
|
||||||
bucketIndex,
|
bucketIndex,
|
||||||
taskIndex,
|
taskIndex,
|
||||||
task,
|
task,
|
||||||
} : {
|
}: {
|
||||||
bucketIndex: number,
|
bucketIndex: number,
|
||||||
taskIndex: number,
|
taskIndex: number,
|
||||||
task: ITask
|
task: ITask
|
||||||
@ -201,7 +201,7 @@ export const useKanbanStore = defineStore('kanban', () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const { bucketIndex, taskIndex } = getTaskIndicesById(buckets.value, task.id)
|
const {bucketIndex, taskIndex} = getTaskIndicesById(buckets.value, task.id)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
bucketIndex === null ||
|
bucketIndex === null ||
|
||||||
@ -211,16 +211,16 @@ export const useKanbanStore = defineStore('kanban', () => {
|
|||||||
) {
|
) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
buckets.value[bucketIndex].tasks.splice(taskIndex, 1)
|
buckets.value[bucketIndex].tasks.splice(taskIndex, 1)
|
||||||
buckets.value[bucketIndex].count--
|
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
|
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
|
taskPagesPerBucket.value[bucketId] = page
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,7 +228,7 @@ export const useKanbanStore = defineStore('kanban', () => {
|
|||||||
allTasksLoadedForBucket.value[bucketId] = true
|
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)
|
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
|
// 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})
|
setBucketLoading({bucketId: bucketId, loading: true})
|
||||||
|
|
||||||
const params: TaskFilterParams = JSON.parse(JSON.stringify(ps))
|
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.filter_timezone = authStore.settings.timezone
|
||||||
|
|
||||||
params.per_page = TASKS_PER_BUCKET
|
params.per_page = TASKS_PER_BUCKET
|
||||||
|
|
||||||
const taskService = new TaskCollectionService()
|
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 cancel = setModuleLoading(setIsLoading)
|
||||||
|
|
||||||
const bucketService = new BucketService()
|
const bucketService = new BucketService()
|
||||||
@ -349,13 +331,13 @@ export const useKanbanStore = defineStore('kanban', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setBucketByIndex({bucketIndex, bucket: updatedBucket})
|
setBucketByIndex({bucketIndex, bucket: updatedBucket})
|
||||||
|
|
||||||
const bucketService = new BucketService()
|
const bucketService = new BucketService()
|
||||||
try {
|
try {
|
||||||
const returnedBucket = await bucketService.update(updatedBucket)
|
const returnedBucket = await bucketService.update(updatedBucket)
|
||||||
setBucketByIndex({bucketIndex, bucket: returnedBucket})
|
setBucketByIndex({bucketIndex, bucket: returnedBucket})
|
||||||
return returnedBucket
|
return returnedBucket
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
// restore original state
|
// restore original state
|
||||||
setBucketByIndex({bucketIndex, bucket: oldBucket})
|
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)
|
const bucket = findById(buckets.value, id)
|
||||||
|
|
||||||
if (bucket?.title === title) {
|
if (bucket?.title === title) {
|
||||||
@ -373,14 +355,14 @@ export const useKanbanStore = defineStore('kanban', () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await updateBucket({ id, title })
|
await updateBucket({id, title})
|
||||||
success({message: i18n.global.t('project.kanban.bucketTitleSavedSuccess')})
|
success({message: i18n.global.t('project.kanban.bucketTitleSavedSuccess')})
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
buckets,
|
buckets,
|
||||||
isLoading: readonly(isLoading),
|
isLoading: readonly(isLoading),
|
||||||
|
|
||||||
getBucketById,
|
getBucketById,
|
||||||
getTaskById,
|
getTaskById,
|
||||||
|
|
||||||
@ -401,5 +383,5 @@ export const useKanbanStore = defineStore('kanban', () => {
|
|||||||
|
|
||||||
// support hot reloading
|
// support hot reloading
|
||||||
if (import.meta.hot) {
|
if (import.meta.hot) {
|
||||||
import.meta.hot.accept(acceptHMRUpdate(useKanbanStore, import.meta.hot))
|
import.meta.hot.accept(acceptHMRUpdate(useKanbanStore, import.meta.hot))
|
||||||
}
|
}
|
@ -126,7 +126,7 @@ export const useTaskStore = defineStore('task', () => {
|
|||||||
|
|
||||||
async function loadTasks(params: TaskFilterParams, projectId: IProject['id'] | null = null) {
|
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
|
params.filter_timezone = authStore.settings.timezone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,9 +333,7 @@ const {
|
|||||||
const tasks: Ref<ITask[]> = taskList.tasks
|
const tasks: Ref<ITask[]> = taskList.tasks
|
||||||
|
|
||||||
Object.assign(params.value, {
|
Object.assign(params.value, {
|
||||||
filter_by: [],
|
filter: '',
|
||||||
filter_value: [],
|
|
||||||
filter_comparator: [],
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// FIXME: by doing this we can have multiple sort orders
|
// FIXME: by doing this we can have multiple sort orders
|
||||||
|
@ -182,29 +182,21 @@ async function loadPendingTasks(from: string, to: string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const params = {
|
const params: TaskFilterParams = {
|
||||||
sortBy: ['due_date', 'id'],
|
sort_by: ['due_date', 'id'],
|
||||||
orderBy: ['asc', 'desc'],
|
order_by: ['asc', 'desc'],
|
||||||
filterTimezone: authStore.settings.timezone,
|
filter: 'done = false',
|
||||||
filterBy: ['done'],
|
filter_include_nulls: showNulls,
|
||||||
filterValue: ['false'],
|
|
||||||
filterComparator: ['equals'],
|
|
||||||
filterConcat: 'and',
|
|
||||||
filterIncludeNulls: showNulls,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!showAll.value) {
|
if (!showAll.value) {
|
||||||
params.filterBy.push('due_date')
|
params.filter += ` && due_date < '${to}'`
|
||||||
params.filterValue.push(to)
|
|
||||||
params.filterComparator.push('less')
|
|
||||||
|
|
||||||
// NOTE: Ideally we could also show tasks with a start or end date in the specified range, but the api
|
// 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'.
|
// is not capable (yet) of combining multiple filters with 'and' and 'or'.
|
||||||
|
|
||||||
if (!showOverdue) {
|
if (!showOverdue) {
|
||||||
params.filterBy.push('due_date')
|
params.filter += ` && due_date > '${from}'`
|
||||||
params.filterValue.push(from)
|
|
||||||
params.filterComparator.push('greater')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user