2020-06-11 15:34:13 +00:00
|
|
|
<template>
|
2022-05-08 20:39:23 +00:00
|
|
|
<card class="filters has-overflow" :title="hasTitle ? $t('filters.title') : ''">
|
2022-09-30 19:09:24 +00:00
|
|
|
<div class="field is-flex is-flex-direction-column">
|
2022-09-24 13:20:40 +00:00
|
|
|
<fancycheckbox
|
|
|
|
v-model="params.filter_include_nulls"
|
|
|
|
@update:model-value="change()"
|
|
|
|
>
|
2021-12-21 16:29:49 +00:00
|
|
|
{{ $t('filters.attributes.includeNulls') }}
|
|
|
|
</fancycheckbox>
|
|
|
|
<fancycheckbox
|
|
|
|
v-model="filters.requireAllFilters"
|
2022-09-24 13:20:40 +00:00
|
|
|
@update:model-value="setFilterConcat()"
|
2021-12-21 16:29:49 +00:00
|
|
|
>
|
|
|
|
{{ $t('filters.attributes.requireAll') }}
|
|
|
|
</fancycheckbox>
|
2022-11-06 20:03:43 +00:00
|
|
|
<fancycheckbox
|
|
|
|
v-model="filters.done"
|
|
|
|
@update:model-value="setDoneFilter"
|
|
|
|
>
|
2021-06-23 23:24:57 +00:00
|
|
|
{{ $t('filters.attributes.showDoneTasks') }}
|
2021-12-21 16:29:49 +00:00
|
|
|
</fancycheckbox>
|
|
|
|
<fancycheckbox
|
2022-11-13 21:04:57 +00:00
|
|
|
v-if="!['project.kanban', 'project.table'].includes($route.name as string)"
|
2021-12-21 16:29:49 +00:00
|
|
|
v-model="sortAlphabetically"
|
2022-09-24 13:20:40 +00:00
|
|
|
@update:model-value="change()"
|
2021-12-21 16:29:49 +00:00
|
|
|
>
|
|
|
|
{{ $t('filters.attributes.sortAlphabetically') }}
|
|
|
|
</fancycheckbox>
|
2021-01-17 17:57:57 +00:00
|
|
|
</div>
|
2021-03-10 13:13:28 +00:00
|
|
|
<div class="field">
|
2021-06-23 23:24:57 +00:00
|
|
|
<label class="label">{{ $t('misc.search') }}</label>
|
2021-03-10 13:13:28 +00:00
|
|
|
<div class="control">
|
|
|
|
<input
|
|
|
|
class="input"
|
2021-06-23 23:24:57 +00:00
|
|
|
:placeholder="$t('misc.search')"
|
2021-03-10 13:13:28 +00:00
|
|
|
v-model="params.s"
|
|
|
|
@blur="change()"
|
|
|
|
@keyup.enter="change()"
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
2021-01-17 17:57:57 +00:00
|
|
|
<div class="field">
|
2021-06-23 23:24:57 +00:00
|
|
|
<label class="label">{{ $t('task.attributes.priority') }}</label>
|
2021-01-17 17:57:57 +00:00
|
|
|
<div class="control single-value-control">
|
|
|
|
<priority-select
|
|
|
|
v-model.number="filters.priority"
|
2022-09-13 15:30:33 +00:00
|
|
|
@update:model-value="setPriority"
|
2022-11-06 20:03:43 +00:00
|
|
|
:disabled="!filters.usePriority || undefined"
|
2021-01-17 17:57:57 +00:00
|
|
|
/>
|
|
|
|
<fancycheckbox
|
|
|
|
v-model="filters.usePriority"
|
2022-09-24 13:20:40 +00:00
|
|
|
@update:model-value="setPriority"
|
2021-01-17 17:57:57 +00:00
|
|
|
>
|
2021-06-23 23:24:57 +00:00
|
|
|
{{ $t('filters.attributes.enablePriority') }}
|
2021-01-17 17:57:57 +00:00
|
|
|
</fancycheckbox>
|
2020-09-26 21:02:37 +00:00
|
|
|
</div>
|
2021-01-17 17:57:57 +00:00
|
|
|
</div>
|
|
|
|
<div class="field">
|
2021-06-23 23:24:57 +00:00
|
|
|
<label class="label">{{ $t('task.attributes.percentDone') }}</label>
|
2021-01-17 17:57:57 +00:00
|
|
|
<div class="control single-value-control">
|
|
|
|
<percent-done-select
|
|
|
|
v-model.number="filters.percentDone"
|
2022-09-13 15:30:33 +00:00
|
|
|
@update:model-value="setPercentDoneFilter"
|
2022-05-22 15:08:37 +00:00
|
|
|
:disabled="!filters.usePercentDone || undefined"
|
2021-01-17 17:57:57 +00:00
|
|
|
/>
|
|
|
|
<fancycheckbox
|
|
|
|
v-model="filters.usePercentDone"
|
2022-09-24 13:20:40 +00:00
|
|
|
@update:model-value="setPercentDoneFilter"
|
2021-01-17 17:57:57 +00:00
|
|
|
>
|
2021-06-23 23:24:57 +00:00
|
|
|
{{ $t('filters.attributes.enablePercentDone') }}
|
2021-01-17 17:57:57 +00:00
|
|
|
</fancycheckbox>
|
2020-12-19 21:39:25 +00:00
|
|
|
</div>
|
2021-01-17 17:57:57 +00:00
|
|
|
</div>
|
|
|
|
<div class="field">
|
2021-06-23 23:24:57 +00:00
|
|
|
<label class="label">{{ $t('task.attributes.dueDate') }}</label>
|
2021-01-17 17:57:57 +00:00
|
|
|
<div class="control">
|
2022-07-13 15:52:42 +00:00
|
|
|
<datepicker-with-range
|
2022-09-13 15:30:33 +00:00
|
|
|
v-model="filters.dueDate"
|
|
|
|
@update:model-value="values => setDateFilter('due_date', values)"
|
|
|
|
>
|
2022-02-06 17:46:53 +00:00
|
|
|
<template #trigger="{toggle, buttonText}">
|
|
|
|
<x-button @click.prevent.stop="toggle()" variant="secondary" :shadow="false" class="mb-2">
|
|
|
|
{{ buttonText }}
|
|
|
|
</x-button>
|
|
|
|
</template>
|
|
|
|
</datepicker-with-range>
|
2020-12-19 21:39:25 +00:00
|
|
|
</div>
|
2021-01-17 17:57:57 +00:00
|
|
|
</div>
|
|
|
|
<div class="field">
|
2021-06-23 23:24:57 +00:00
|
|
|
<label class="label">{{ $t('task.attributes.startDate') }}</label>
|
2021-01-17 17:57:57 +00:00
|
|
|
<div class="control">
|
2022-07-13 15:52:42 +00:00
|
|
|
<datepicker-with-range
|
2022-09-13 15:30:33 +00:00
|
|
|
v-model="filters.startDate"
|
|
|
|
@update:model-value="values => setDateFilter('start_date', values)"
|
|
|
|
>
|
2022-02-06 17:46:53 +00:00
|
|
|
<template #trigger="{toggle, buttonText}">
|
|
|
|
<x-button @click.prevent.stop="toggle()" variant="secondary" :shadow="false" class="mb-2">
|
|
|
|
{{ buttonText }}
|
|
|
|
</x-button>
|
|
|
|
</template>
|
|
|
|
</datepicker-with-range>
|
2020-09-26 21:02:37 +00:00
|
|
|
</div>
|
2021-01-17 17:57:57 +00:00
|
|
|
</div>
|
|
|
|
<div class="field">
|
2021-06-23 23:24:57 +00:00
|
|
|
<label class="label">{{ $t('task.attributes.endDate') }}</label>
|
2021-01-17 17:57:57 +00:00
|
|
|
<div class="control">
|
2022-07-13 15:52:42 +00:00
|
|
|
<datepicker-with-range
|
2022-09-13 15:30:33 +00:00
|
|
|
v-model="filters.endDate"
|
|
|
|
@update:model-value="values => setDateFilter('end_date', values)"
|
|
|
|
>
|
2022-02-06 17:46:53 +00:00
|
|
|
<template #trigger="{toggle, buttonText}">
|
|
|
|
<x-button @click.prevent.stop="toggle()" variant="secondary" :shadow="false" class="mb-2">
|
|
|
|
{{ buttonText }}
|
|
|
|
</x-button>
|
|
|
|
</template>
|
|
|
|
</datepicker-with-range>
|
2020-09-26 21:02:37 +00:00
|
|
|
</div>
|
2021-01-17 17:57:57 +00:00
|
|
|
</div>
|
|
|
|
<div class="field">
|
2021-06-23 23:24:57 +00:00
|
|
|
<label class="label">{{ $t('task.attributes.reminders') }}</label>
|
2021-01-17 17:57:57 +00:00
|
|
|
<div class="control">
|
2022-07-13 15:52:42 +00:00
|
|
|
<datepicker-with-range
|
2022-09-13 15:30:33 +00:00
|
|
|
v-model="filters.reminders"
|
|
|
|
@update:model-value="values => setDateFilter('reminders', values)"
|
|
|
|
>
|
2022-02-06 17:46:53 +00:00
|
|
|
<template #trigger="{toggle, buttonText}">
|
|
|
|
<x-button @click.prevent.stop="toggle()" variant="secondary" :shadow="false" class="mb-2">
|
|
|
|
{{ buttonText }}
|
|
|
|
</x-button>
|
|
|
|
</template>
|
|
|
|
</datepicker-with-range>
|
2021-01-17 17:57:57 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="field">
|
2021-06-23 23:24:57 +00:00
|
|
|
<label class="label">{{ $t('task.attributes.assignees') }}</label>
|
2021-01-17 17:57:57 +00:00
|
|
|
<div class="control">
|
2022-11-06 20:49:28 +00:00
|
|
|
<SelectUser
|
2022-11-06 20:03:43 +00:00
|
|
|
v-model="entities.users"
|
2022-11-06 20:49:28 +00:00
|
|
|
@select="changeMultiselectFilter('users', 'assignees')"
|
|
|
|
@remove="changeMultiselectFilter('users', 'assignees')"
|
2021-01-17 17:57:57 +00:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="field">
|
2021-07-09 08:22:20 +00:00
|
|
|
<label class="label">{{ $t('task.attributes.labels') }}</label>
|
2021-07-20 20:46:39 +00:00
|
|
|
<div class="control labels-list">
|
2022-11-06 20:49:28 +00:00
|
|
|
<edit-labels
|
2023-05-30 17:54:01 +00:00
|
|
|
:creatable="false"
|
2022-11-06 20:49:28 +00:00
|
|
|
v-model="entities.labels"
|
|
|
|
@update:model-value="changeLabelFilter"
|
|
|
|
/>
|
2020-12-19 19:23:46 +00:00
|
|
|
</div>
|
2021-01-17 17:57:57 +00:00
|
|
|
</div>
|
2020-12-19 21:39:25 +00:00
|
|
|
|
2022-07-13 15:52:42 +00:00
|
|
|
<template
|
2022-11-13 21:04:57 +00:00
|
|
|
v-if="['filters.create', 'project.edit', 'filter.settings.edit'].includes($route.name as string)">
|
2020-09-26 21:02:37 +00:00
|
|
|
<div class="field">
|
2023-08-24 09:18:03 +00:00
|
|
|
<label class="label">{{ $t('project.projects') }}</label>
|
2020-12-19 21:39:25 +00:00
|
|
|
<div class="control">
|
2022-11-13 21:04:57 +00:00
|
|
|
<SelectProject
|
|
|
|
v-model="entities.projects"
|
|
|
|
@select="changeMultiselectFilter('projects', 'project_id')"
|
|
|
|
@remove="changeMultiselectFilter('projects', 'project_id')"
|
2021-01-06 22:36:31 +00:00
|
|
|
/>
|
2020-09-26 21:02:37 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
2021-01-17 17:57:57 +00:00
|
|
|
</template>
|
|
|
|
</card>
|
2020-06-11 15:34:13 +00:00
|
|
|
</template>
|
|
|
|
|
2022-02-15 12:07:34 +00:00
|
|
|
<script lang="ts">
|
2022-11-06 20:03:43 +00:00
|
|
|
export const ALPHABETICAL_SORT = 'title'
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
import {computed, nextTick, onMounted, reactive, ref, shallowReactive, toRefs, watch} from 'vue'
|
|
|
|
import {camelCase} from 'camel-case'
|
|
|
|
|
|
|
|
import type {ILabel} from '@/modelTypes/ILabel'
|
|
|
|
import type {IUser} from '@/modelTypes/IUser'
|
2022-11-13 21:04:57 +00:00
|
|
|
import type {IProject} from '@/modelTypes/IProject'
|
2022-02-15 12:07:59 +00:00
|
|
|
|
2022-09-21 14:23:57 +00:00
|
|
|
import {useLabelStore} from '@/stores/labels'
|
|
|
|
|
2022-06-23 01:08:35 +00:00
|
|
|
import DatepickerWithRange from '@/components/date/datepickerWithRange.vue'
|
2021-07-25 13:27:15 +00:00
|
|
|
import PrioritySelect from '@/components/tasks/partials/prioritySelect.vue'
|
|
|
|
import PercentDoneSelect from '@/components/tasks/partials/percentDoneSelect.vue'
|
2022-11-06 20:03:43 +00:00
|
|
|
import EditLabels from '@/components/tasks/partials/editLabels.vue'
|
|
|
|
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
|
2022-11-06 20:49:28 +00:00
|
|
|
import SelectUser from '@/components/input/SelectUser.vue'
|
2022-11-13 21:04:57 +00:00
|
|
|
import SelectProject from '@/components/input/SelectProject.vue'
|
2022-11-06 20:03:43 +00:00
|
|
|
|
2022-01-09 16:27:28 +00:00
|
|
|
import {parseDateOrString} from '@/helpers/time/parseDateOrString'
|
2022-11-06 20:03:43 +00:00
|
|
|
import {dateIsValid, formatISO} from '@/helpers/time/formatDate'
|
|
|
|
import {objectToSnakeCase} from '@/helpers/case'
|
2020-09-26 21:02:37 +00:00
|
|
|
|
2020-12-20 12:41:47 +00:00
|
|
|
import UserService from '@/services/user'
|
2022-11-13 21:04:57 +00:00
|
|
|
import ProjectService from '@/services/project'
|
2020-12-19 21:39:25 +00:00
|
|
|
|
2022-11-06 20:03:43 +00:00
|
|
|
// FIXME: do not use this here for now. instead create new version from DEFAULT_PARAMS
|
2022-10-27 13:47:48 +00:00
|
|
|
import {getDefaultParams} from '@/composables/useTaskList'
|
2021-11-14 15:56:52 +00:00
|
|
|
|
2022-11-13 21:04:57 +00:00
|
|
|
// FIXME: merge with DEFAULT_PARAMS in taskProject.js
|
2021-09-10 13:02:52 +00:00
|
|
|
const DEFAULT_PARAMS = {
|
|
|
|
sort_by: [],
|
|
|
|
order_by: [],
|
|
|
|
filter_by: [],
|
|
|
|
filter_value: [],
|
|
|
|
filter_comparator: [],
|
|
|
|
filter_include_nulls: true,
|
|
|
|
filter_concat: 'or',
|
|
|
|
s: '',
|
2022-11-06 20:03:43 +00:00
|
|
|
} as const
|
2021-09-10 13:02:52 +00:00
|
|
|
|
|
|
|
const DEFAULT_FILTERS = {
|
|
|
|
done: false,
|
|
|
|
dueDate: '',
|
|
|
|
requireAllFilters: false,
|
|
|
|
priority: 0,
|
|
|
|
usePriority: false,
|
|
|
|
startDate: '',
|
|
|
|
endDate: '',
|
|
|
|
percentDone: 0,
|
|
|
|
usePercentDone: false,
|
|
|
|
reminders: '',
|
|
|
|
assignees: '',
|
|
|
|
labels: '',
|
2022-11-13 21:04:57 +00:00
|
|
|
project_id: '',
|
2022-11-06 20:03:43 +00:00
|
|
|
} as const
|
|
|
|
|
|
|
|
const props = defineProps({
|
|
|
|
modelValue: {
|
|
|
|
required: true,
|
|
|
|
},
|
|
|
|
hasTitle: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
const emit = defineEmits(['update:modelValue'])
|
|
|
|
|
|
|
|
const {modelValue} = toRefs(props)
|
|
|
|
|
|
|
|
const labelStore = useLabelStore()
|
|
|
|
|
|
|
|
const params = ref({...DEFAULT_PARAMS})
|
|
|
|
const filters = ref({...DEFAULT_FILTERS})
|
|
|
|
|
|
|
|
const services = {
|
|
|
|
users: shallowReactive(new UserService()),
|
2022-11-13 21:04:57 +00:00
|
|
|
projects: shallowReactive(new ProjectService()),
|
2021-09-10 13:02:52 +00:00
|
|
|
}
|
|
|
|
|
2022-11-06 20:03:43 +00:00
|
|
|
interface Entities {
|
|
|
|
users: IUser[]
|
|
|
|
labels: ILabel[]
|
2022-11-13 21:04:57 +00:00
|
|
|
projects: IProject[]
|
2022-11-06 20:03:43 +00:00
|
|
|
}
|
2021-12-21 16:29:49 +00:00
|
|
|
|
2023-03-25 13:54:20 +00:00
|
|
|
type EntityType = 'users' | 'labels' | 'projects'
|
2020-12-19 21:39:25 +00:00
|
|
|
|
2022-11-06 20:03:43 +00:00
|
|
|
const entities: Entities = reactive({
|
|
|
|
users: [],
|
|
|
|
labels: [],
|
2022-11-13 21:04:57 +00:00
|
|
|
projects: [],
|
2022-11-06 20:03:43 +00:00
|
|
|
})
|
2020-12-20 12:41:47 +00:00
|
|
|
|
2022-11-06 20:03:43 +00:00
|
|
|
onMounted(() => {
|
|
|
|
filters.value.requireAllFilters = params.value.filter_concat === 'and'
|
|
|
|
})
|
2020-12-21 23:13:39 +00:00
|
|
|
|
2022-11-06 20:03:43 +00:00
|
|
|
watch(
|
|
|
|
modelValue,
|
|
|
|
(value) => {
|
|
|
|
// FIXME: filters should only be converted to snake case in
|
|
|
|
// the last moment
|
|
|
|
params.value = objectToSnakeCase(value)
|
|
|
|
prepareFilters()
|
2020-09-05 20:35:52 +00:00
|
|
|
},
|
2022-11-06 20:03:43 +00:00
|
|
|
{immediate: true},
|
|
|
|
)
|
|
|
|
|
|
|
|
const sortAlphabetically = computed({
|
|
|
|
get() {
|
|
|
|
return params.value?.sort_by?.find(sortBy => sortBy === ALPHABETICAL_SORT) !== undefined
|
2020-09-05 20:35:52 +00:00
|
|
|
},
|
2022-11-06 20:03:43 +00:00
|
|
|
set(sortAlphabetically) {
|
|
|
|
params.value.sort_by = sortAlphabetically
|
|
|
|
? [ALPHABETICAL_SORT]
|
|
|
|
: getDefaultParams().sort_by
|
|
|
|
|
|
|
|
change()
|
2021-06-03 20:23:04 +00:00
|
|
|
},
|
2022-11-06 20:03:43 +00:00
|
|
|
})
|
2021-04-15 19:45:34 +00:00
|
|
|
|
2022-11-06 20:03:43 +00:00
|
|
|
function change() {
|
|
|
|
const newParams = {...params.value}
|
|
|
|
newParams.filter_value = newParams.filter_value.map(v => v instanceof Date ? v.toISOString() : v)
|
|
|
|
emit('update:modelValue', newParams)
|
|
|
|
}
|
2020-09-26 21:02:37 +00:00
|
|
|
|
2022-11-06 20:03:43 +00:00
|
|
|
function prepareFilters() {
|
|
|
|
prepareDone()
|
|
|
|
prepareDate('due_date', 'dueDate')
|
|
|
|
prepareDate('start_date', 'startDate')
|
|
|
|
prepareDate('end_date', 'endDate')
|
|
|
|
prepareSingleValue('priority', 'priority', 'usePriority', true)
|
|
|
|
prepareSingleValue('percent_done', 'percentDone', 'usePercentDone', true)
|
|
|
|
prepareDate('reminders')
|
|
|
|
prepareRelatedObjectFilter('users', 'assignees')
|
2022-11-13 21:04:57 +00:00
|
|
|
prepareRelatedObjectFilter('projects', 'project_id')
|
2022-11-06 20:03:43 +00:00
|
|
|
|
|
|
|
prepareSingleValue('labels')
|
|
|
|
|
|
|
|
const newLabels = typeof filters.value.labels === 'string'
|
|
|
|
? filters.value.labels
|
|
|
|
: ''
|
|
|
|
const labelIds = newLabels.split(',').map(i => parseInt(i))
|
|
|
|
|
|
|
|
entities.labels = labelStore.getLabelsByIds(labelIds)
|
|
|
|
}
|
|
|
|
|
|
|
|
function removePropertyFromFilter(filterName) {
|
|
|
|
// Because of the way arrays work, we can only ever remove one element at once.
|
|
|
|
// To remove multiple filter elements of the same name this function has to be called multiple times.
|
|
|
|
for (const i in params.value.filter_by) {
|
|
|
|
if (params.value.filter_by[i] === filterName) {
|
|
|
|
params.value.filter_by.splice(i, 1)
|
|
|
|
params.value.filter_comparator.splice(i, 1)
|
|
|
|
params.value.filter_value.splice(i, 1)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-26 21:02:37 +00:00
|
|
|
|
2022-11-06 20:03:43 +00:00
|
|
|
function setDateFilter(filterName, {dateFrom, dateTo}) {
|
|
|
|
dateFrom = parseDateOrString(dateFrom, null)
|
|
|
|
dateTo = parseDateOrString(dateTo, null)
|
|
|
|
|
|
|
|
// Only filter if we have a date
|
|
|
|
if (dateFrom !== null && dateTo !== null) {
|
|
|
|
|
|
|
|
// Check if we already have values in params and only update them if we do
|
|
|
|
let foundStart = false
|
|
|
|
let foundEnd = false
|
|
|
|
params.value.filter_by.forEach((f, i) => {
|
|
|
|
if (f === filterName && params.value.filter_comparator[i] === 'greater_equals') {
|
|
|
|
foundStart = true
|
|
|
|
params.value.filter_value[i] = dateFrom
|
2020-09-26 21:02:37 +00:00
|
|
|
}
|
2022-11-06 20:03:43 +00:00
|
|
|
if (f === filterName && params.value.filter_comparator[i] === 'less_equals') {
|
|
|
|
foundEnd = true
|
|
|
|
params.value.filter_value[i] = dateTo
|
2020-09-26 21:02:37 +00:00
|
|
|
}
|
2022-11-06 20:03:43 +00:00
|
|
|
})
|
2020-09-26 21:02:37 +00:00
|
|
|
|
2022-11-06 20:03:43 +00:00
|
|
|
if (!foundStart) {
|
|
|
|
params.value.filter_by.push(filterName)
|
|
|
|
params.value.filter_comparator.push('greater_equals')
|
|
|
|
params.value.filter_value.push(dateFrom)
|
|
|
|
}
|
|
|
|
if (!foundEnd) {
|
|
|
|
params.value.filter_by.push(filterName)
|
|
|
|
params.value.filter_comparator.push('less_equals')
|
|
|
|
params.value.filter_value.push(dateTo)
|
|
|
|
}
|
2020-09-26 21:02:37 +00:00
|
|
|
|
2022-11-06 20:03:43 +00:00
|
|
|
filters.value[camelCase(filterName)] = {
|
|
|
|
// Passing the dates as string values avoids an endless loop between values changing
|
|
|
|
// in the datepicker (bubbling up to here) and changing here and bubbling down to the
|
|
|
|
// datepicker (because there's a new date instance every time this function gets called).
|
|
|
|
// See https://kolaente.dev/vikunja/frontend/issues/2384
|
|
|
|
dateFrom: dateIsValid(dateFrom) ? formatISO(dateFrom) : dateFrom,
|
|
|
|
dateTo: dateIsValid(dateTo) ? formatISO(dateTo) : dateTo,
|
|
|
|
}
|
|
|
|
change()
|
|
|
|
return
|
|
|
|
}
|
2020-12-21 23:13:39 +00:00
|
|
|
|
2022-11-06 20:03:43 +00:00
|
|
|
removePropertyFromFilter(filterName)
|
|
|
|
removePropertyFromFilter(filterName)
|
|
|
|
change()
|
|
|
|
}
|
2020-09-26 21:02:37 +00:00
|
|
|
|
2022-11-06 20:03:43 +00:00
|
|
|
function prepareDate(filterName, variableName) {
|
|
|
|
if (typeof params.value.filter_by === 'undefined') {
|
|
|
|
return
|
|
|
|
}
|
2020-09-26 21:02:37 +00:00
|
|
|
|
2022-11-06 20:03:43 +00:00
|
|
|
let foundDateStart = false
|
|
|
|
let foundDateEnd = false
|
|
|
|
for (const i in params.value.filter_by) {
|
|
|
|
if (params.value.filter_by[i] === filterName && params.value.filter_comparator[i] === 'greater_equals') {
|
|
|
|
foundDateStart = i
|
|
|
|
}
|
|
|
|
if (params.value.filter_by[i] === filterName && params.value.filter_comparator[i] === 'less_equals') {
|
|
|
|
foundDateEnd = i
|
|
|
|
}
|
2020-09-26 21:02:37 +00:00
|
|
|
|
2022-11-06 20:03:43 +00:00
|
|
|
if (foundDateStart !== false && foundDateEnd !== false) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2020-09-26 21:02:37 +00:00
|
|
|
|
2022-11-06 20:03:43 +00:00
|
|
|
if (foundDateStart !== false && foundDateEnd !== false) {
|
|
|
|
const startDate = new Date(params.value.filter_value[foundDateStart])
|
|
|
|
const endDate = new Date(params.value.filter_value[foundDateEnd])
|
|
|
|
filters.value[variableName] = {
|
|
|
|
dateFrom: !isNaN(startDate)
|
|
|
|
? `${startDate.getFullYear()}-${startDate.getMonth() + 1}-${startDate.getDate()}`
|
|
|
|
: params.value.filter_value[foundDateStart],
|
|
|
|
dateTo: !isNaN(endDate)
|
|
|
|
? `${endDate.getFullYear()}-${endDate.getMonth() + 1}-${endDate.getDate()}`
|
|
|
|
: params.value.filter_value[foundDateEnd],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-12-21 23:13:39 +00:00
|
|
|
|
2022-11-06 20:03:43 +00:00
|
|
|
function setSingleValueFilter(filterName, variableName, useVariableName = '', comparator = 'equals') {
|
|
|
|
if (useVariableName !== '' && !filters.value[useVariableName]) {
|
|
|
|
removePropertyFromFilter(filterName)
|
|
|
|
return
|
|
|
|
}
|
2020-12-21 23:13:39 +00:00
|
|
|
|
2022-11-06 20:03:43 +00:00
|
|
|
let found = false
|
|
|
|
params.value.filter_by.forEach((f, i) => {
|
|
|
|
if (f === filterName) {
|
|
|
|
found = true
|
|
|
|
params.value.filter_value[i] = filters.value[variableName]
|
|
|
|
}
|
|
|
|
})
|
2022-10-07 17:49:57 +00:00
|
|
|
|
2022-11-06 20:03:43 +00:00
|
|
|
if (!found) {
|
|
|
|
params.value.filter_by.push(filterName)
|
|
|
|
params.value.filter_comparator.push(comparator)
|
|
|
|
params.value.filter_value.push(filters.value[variableName])
|
|
|
|
}
|
2021-10-11 17:37:20 +00:00
|
|
|
|
2022-11-06 20:03:43 +00:00
|
|
|
change()
|
|
|
|
}
|
2020-12-19 21:39:25 +00:00
|
|
|
|
2022-11-06 20:03:43 +00:00
|
|
|
function prepareSingleValue(
|
|
|
|
/** The filter name in the api. */
|
|
|
|
filterName,
|
|
|
|
/** The name of the variable in filters ref. */
|
|
|
|
variableName = null,
|
|
|
|
/** The name of the variable of the "Use this filter" variable. Will only be set if the parameter is not null. */
|
|
|
|
useVariableName = null,
|
|
|
|
/** Toggles if the value should be parsed as a number. */
|
|
|
|
isNumber = false,
|
|
|
|
) {
|
|
|
|
if (variableName === null) {
|
|
|
|
variableName = filterName
|
|
|
|
}
|
2020-12-19 21:39:25 +00:00
|
|
|
|
2022-11-06 20:03:43 +00:00
|
|
|
let found = false
|
|
|
|
for (const i in params.value.filter_by) {
|
|
|
|
if (params.value.filter_by[i] === filterName) {
|
|
|
|
found = i
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2020-12-20 12:41:47 +00:00
|
|
|
|
2022-11-06 20:03:43 +00:00
|
|
|
if (found === false && useVariableName !== null) {
|
|
|
|
filters.value[useVariableName] = false
|
|
|
|
return
|
|
|
|
}
|
2020-12-20 12:41:47 +00:00
|
|
|
|
2022-11-06 20:03:43 +00:00
|
|
|
if (isNumber) {
|
|
|
|
filters.value[variableName] = Number(params.value.filter_value[found])
|
|
|
|
} else {
|
|
|
|
filters.value[variableName] = params.value.filter_value[found]
|
|
|
|
}
|
|
|
|
|
|
|
|
if (useVariableName !== null) {
|
|
|
|
filters.value[useVariableName] = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function prepareDone() {
|
|
|
|
// Set filters.done based on params
|
|
|
|
if (typeof params.value.filter_by === 'undefined') {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
filters.value.done = params.value.filter_by.some((f) => f === 'done') === false
|
|
|
|
}
|
|
|
|
|
|
|
|
async function prepareRelatedObjectFilter(kind: EntityType, filterName = null, servicePrefix: Omit<EntityType, 'labels'> | null = null) {
|
|
|
|
if (filterName === null) {
|
|
|
|
filterName = kind
|
|
|
|
}
|
|
|
|
|
|
|
|
if (servicePrefix === null) {
|
|
|
|
servicePrefix = kind
|
|
|
|
}
|
|
|
|
|
|
|
|
prepareSingleValue(filterName)
|
|
|
|
if (typeof filters.value[filterName] === 'undefined' || filters.value[filterName] === '') {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't load things if we already have something loaded.
|
|
|
|
// This is not the most ideal solution because it prevents a re-population when filters are changed
|
|
|
|
// from the outside. It is still fine because we're not changing them from the outside, other than
|
|
|
|
// loading them initially.
|
|
|
|
if (entities[kind].length > 0) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
entities[kind] = await services[servicePrefix].getAll({}, {s: filters.value[filterName]})
|
|
|
|
}
|
|
|
|
|
|
|
|
function setDoneFilter() {
|
|
|
|
if (filters.value.done) {
|
|
|
|
removePropertyFromFilter('done')
|
|
|
|
} else {
|
|
|
|
params.value.filter_by.push('done')
|
|
|
|
params.value.filter_comparator.push('equals')
|
|
|
|
params.value.filter_value.push('false')
|
|
|
|
}
|
|
|
|
change()
|
|
|
|
}
|
|
|
|
|
|
|
|
function setFilterConcat() {
|
|
|
|
if (filters.value.requireAllFilters) {
|
|
|
|
params.value.filter_concat = 'and'
|
|
|
|
} else {
|
|
|
|
params.value.filter_concat = 'or'
|
|
|
|
}
|
|
|
|
change()
|
|
|
|
}
|
|
|
|
|
|
|
|
function setPriority() {
|
|
|
|
setSingleValueFilter('priority', 'priority', 'usePriority')
|
|
|
|
}
|
|
|
|
|
|
|
|
function setPercentDoneFilter() {
|
|
|
|
setSingleValueFilter('percent_done', 'percentDone', 'usePercentDone')
|
|
|
|
}
|
|
|
|
|
2022-11-06 20:49:28 +00:00
|
|
|
async function changeMultiselectFilter(kind: EntityType, filterName) {
|
|
|
|
await nextTick()
|
2022-11-06 20:03:43 +00:00
|
|
|
|
|
|
|
if (entities[kind].length === 0) {
|
|
|
|
removePropertyFromFilter(filterName)
|
|
|
|
change()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
const ids = entities[kind].map(u => kind === 'users' ? u.username : u.id)
|
|
|
|
|
|
|
|
filters.value[filterName] = ids.join(',')
|
|
|
|
setSingleValueFilter(filterName, filterName, '', 'in')
|
|
|
|
}
|
|
|
|
|
|
|
|
function changeLabelFilter() {
|
|
|
|
if (entities.labels.length === 0) {
|
|
|
|
removePropertyFromFilter('labels')
|
|
|
|
change()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
const labelIDs = entities.labels.map(u => u.id)
|
|
|
|
filters.value.labels = labelIDs.join(',')
|
|
|
|
setSingleValueFilter('labels', 'labels', '', 'in')
|
|
|
|
}
|
2020-06-11 15:34:13 +00:00
|
|
|
</script>
|
2020-09-26 21:02:37 +00:00
|
|
|
|
2022-02-06 18:38:36 +00:00
|
|
|
<style lang="scss" scoped>
|
2020-09-26 21:02:37 +00:00
|
|
|
.single-value-control {
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
.fancycheckbox {
|
|
|
|
margin-left: .5rem;
|
|
|
|
}
|
|
|
|
}
|
2022-01-09 16:27:28 +00:00
|
|
|
|
2022-02-06 18:38:36 +00:00
|
|
|
:deep(.datepicker-with-range-container .popup) {
|
2022-01-09 16:27:28 +00:00
|
|
|
right: 0;
|
|
|
|
}
|
2020-09-26 21:02:37 +00:00
|
|
|
</style>
|