feature/feat-filters-script-setup3 #2671

Merged
konrad merged 2 commits from dpschen/frontend:feature/feat-filters-script-setup3 into main 2022-11-12 10:43:29 +00:00
5 changed files with 574 additions and 431 deletions

View File

@ -0,0 +1,63 @@
<template>
<multiselect
v-model="selectedLists"
:search-results="foundLists"
:loading="listService.loading"
:multiple="true"
:placeholder="$t('list.search')"
label="title"
@search="findLists"
/>
</template>
<script setup lang="ts">
import {computed, ref, shallowReactive, watchEffect, type PropType} from 'vue'
import Multiselect from '@/components/input/multiselect.vue'
import type {IList} from '@/modelTypes/IList'
import ListService from '@/services/list'
import {includesById} from '@/helpers/utils'
const props = defineProps({
modelValue: {
type: Array as PropType<IList[]>,
default: () => [],
},
})
const emit = defineEmits<{
(e: 'update:modelValue', value: IList[]): void
}>()
const lists = ref<IList[]>([])
watchEffect(() => {
lists.value = props.modelValue
})
const selectedLists = computed({
get() {
return lists.value
},
set: (value) => {
lists.value = value
emit('update:modelValue', value)
},
})
const listService = shallowReactive(new ListService())
const foundLists = ref<IList[]>([])
async function findLists(query: string) {
if (query === '') {
foundLists.value = []
return
}
const response = await listService.getAll({}, {s: query}) as IList[]
// Filter selected items from the results
foundLists.value = response.filter(({id}) => !includesById(lists.value, id))
}
</script>

View File

@ -0,0 +1,63 @@
<template>
<multiselect
v-model="selectedNamespaces"
:search-results="foundNamespaces"
:loading="namespaceService.loading"
:multiple="true"
:placeholder="$t('namespace.search')"
label="namespace"
@search="findNamespaces"
/>
</template>
<script setup lang="ts">
import {computed, ref, shallowReactive, watchEffect, type PropType} from 'vue'
import Multiselect from '@/components/input/multiselect.vue'
import type {INamespace} from '@/modelTypes/INamespace'
import NamespaceService from '@/services/namespace'
import {includesById} from '@/helpers/utils'
const props = defineProps({
modelValue: {
type: Array as PropType<INamespace[]>,
default: () => [],
},
})
const emit = defineEmits<{
(e: 'update:modelValue', value: INamespace[]): void
}>()
const namespaces = ref<INamespace[]>([])
watchEffect(() => {
namespaces.value = props.modelValue
})
const selectedNamespaces = computed({
get() {
return namespaces.value
},
set: (value) => {
namespaces.value = value
emit('update:modelValue', value)
},
})
const namespaceService = shallowReactive(new NamespaceService())
const foundNamespaces = ref<INamespace[]>([])
async function findNamespaces(query: string) {
if (query === '') {
foundNamespaces.value = []
return
}
const response = await namespaceService.getAll({}, {s: query}) as INamespace[]
// Filter selected items from the results
foundNamespaces.value = response.filter(({id}) => !includesById(namespaces.value, id))
}
</script>

View File

@ -0,0 +1,63 @@
<template>
<multiselect
v-model="selectedUsers"
:search-results="foundUsers"
:loading="userService.loading"
:multiple="true"
:placeholder="$t('team.edit.search')"
label="username"
@search="findUsers"
/>
</template>
<script setup lang="ts">
import {computed, ref, shallowReactive, watchEffect, type PropType} from 'vue'
import Multiselect from '@/components/input/multiselect.vue'
import type {IUser} from '@/modelTypes/IUser'
import UserService from '@/services/user'
import {includesById} from '@/helpers/utils'
const props = defineProps({
modelValue: {
type: Array as PropType<IUser[]>,
default: () => [],
},
})
const emit = defineEmits<{
(e: 'update:modelValue', value: IUser[]): void
}>()
const users = ref<IUser[]>([])
watchEffect(() => {
users.value = props.modelValue
})
const selectedUsers = computed({
get() {
return users.value
},
set: (value) => {
users.value = value
emit('update:modelValue', value)
},
})
const userService = shallowReactive(new UserService())
const foundUsers = ref<IUser[]>([])
async function findUsers(query: string) {
if (query === '') {
foundUsers.value = []
return
}
const response = await userService.getAll({}, {s: query}) as IUser[]
// Filter selected items from the results
foundUsers.value = response.filter(({id}) => !includesById(users.value, id))
}
</script>

View File

@ -13,11 +13,14 @@
> >
{{ $t('filters.attributes.requireAll') }} {{ $t('filters.attributes.requireAll') }}
</fancycheckbox> </fancycheckbox>
<fancycheckbox v-model="filters.done" @update:model-value="setDoneFilter"> <fancycheckbox
v-model="filters.done"
@update:model-value="setDoneFilter"
>
{{ $t('filters.attributes.showDoneTasks') }} {{ $t('filters.attributes.showDoneTasks') }}
</fancycheckbox> </fancycheckbox>
<fancycheckbox <fancycheckbox
v-if="!$route.name.includes('list.kanban') || !$route.name.includes('list.table')" v-if="!['list.kanban', 'list.table'].includes($route.name as string)"
v-model="sortAlphabetically" v-model="sortAlphabetically"
@update:model-value="change()" @update:model-value="change()"
> >
@ -40,9 +43,9 @@
<label class="label">{{ $t('task.attributes.priority') }}</label> <label class="label">{{ $t('task.attributes.priority') }}</label>
<div class="control single-value-control"> <div class="control single-value-control">
<priority-select <priority-select
:disabled="!filters.usePriority || undefined"
v-model.number="filters.priority" v-model.number="filters.priority"
@update:model-value="setPriority" @update:model-value="setPriority"
:disabled="!filters.usePriority || undefined"
/> />
<fancycheckbox <fancycheckbox
v-model="filters.usePriority" v-model="filters.usePriority"
@ -132,16 +135,10 @@
<div class="field"> <div class="field">
<label class="label">{{ $t('task.attributes.assignees') }}</label> <label class="label">{{ $t('task.attributes.assignees') }}</label>
<div class="control"> <div class="control">
<multiselect <SelectUser
:loading="usersService.loading" v-model="entities.users"
:placeholder="$t('team.edit.search')" @select="changeMultiselectFilter('users', 'assignees')"
@search="query => find('users', query)" @remove="changeMultiselectFilter('users', 'assignees')"
:search-results="foundusers"
@select="() => add('users', 'assignees')"
label="username"
:multiple="true"
@remove="() => remove('users', 'assignees')"
v-model="users"
/> />
</div> </div>
</div> </div>
@ -149,41 +146,32 @@
<div class="field"> <div class="field">
<label class="label">{{ $t('task.attributes.labels') }}</label> <label class="label">{{ $t('task.attributes.labels') }}</label>
<div class="control labels-list"> <div class="control labels-list">
<edit-labels v-model="labels" @update:model-value="changeLabelFilter"/> <edit-labels
v-model="entities.labels"
@update:model-value="changeLabelFilter"
/>
</div> </div>
</div> </div>
<template <template
v-if="$route.name === 'filters.create' || $route.name === 'list.edit' || $route.name === 'filter.settings.edit'"> v-if="['filters.create', 'list.edit', 'filter.settings.edit'].includes($route.name as string)">
<div class="field"> <div class="field">
<label class="label">{{ $t('list.lists') }}</label> <label class="label">{{ $t('list.lists') }}</label>
<div class="control"> <div class="control">
<multiselect <SelectList
:loading="listsService.loading" v-model="entities.lists"
:placeholder="$t('list.search')" @select="changeMultiselectFilter('lists', 'list_id')"
@search="query => find('lists', query)" @remove="changeMultiselectFilter('lists', 'list_id')"
:search-results="foundlists"
@select="() => add('lists', 'list_id')"
label="title"
@remove="() => remove('lists', 'list_id')"
:multiple="true"
v-model="lists"
/> />
</div> </div>
</div> </div>
<div class="field"> <div class="field">
<label class="label">{{ $t('namespace.namespaces') }}</label> <label class="label">{{ $t('namespace.namespaces') }}</label>
<div class="control"> <div class="control">
<multiselect <SelectNamespace
:loading="namespaceService.loading" v-model="entities.namespace"
:placeholder="$t('namespace.search')" @select="changeMultiselectFilter('namespace', 'namespace')"
@search="query => find('namespace', query)" @remove="changeMultiselectFilter('namespace', 'namespace')"
:search-results="foundnamespace"
@select="() => add('namespace', 'namespace')"
label="title"
@remove="() => remove('namespace', 'namespace')"
:multiple="true"
v-model="namespace"
/> />
</div> </div>
</div> </div>
@ -192,28 +180,39 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import {defineComponent} from 'vue' 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'
import type {INamespace} from '@/modelTypes/INamespace'
import type {IList} from '@/modelTypes/IList'
import {useLabelStore} from '@/stores/labels' import {useLabelStore} from '@/stores/labels'
import DatepickerWithRange from '@/components/date/datepickerWithRange.vue' import DatepickerWithRange from '@/components/date/datepickerWithRange.vue'
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
import {includesById} from '@/helpers/utils'
import PrioritySelect from '@/components/tasks/partials/prioritySelect.vue' import PrioritySelect from '@/components/tasks/partials/prioritySelect.vue'
import PercentDoneSelect from '@/components/tasks/partials/percentDoneSelect.vue' import PercentDoneSelect from '@/components/tasks/partials/percentDoneSelect.vue'
import Multiselect from '@/components/input/multiselect.vue' import EditLabels from '@/components/tasks/partials/editLabels.vue'
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
import SelectUser from '@/components/input/SelectUser.vue'
import SelectList from '@/components/input/SelectList.vue'
import SelectNamespace from '@/components/input/SelectNamespace.vue'
import {parseDateOrString} from '@/helpers/time/parseDateOrString' import {parseDateOrString} from '@/helpers/time/parseDateOrString'
import {dateIsValid, formatISO} from '@/helpers/time/formatDate'
import {objectToSnakeCase} from '@/helpers/case'
import UserService from '@/services/user' import UserService from '@/services/user'
import ListService from '@/services/list' import ListService from '@/services/list'
import NamespaceService from '@/services/namespace' import NamespaceService from '@/services/namespace'
import EditLabels from '@/components/tasks/partials/editLabels.vue'
import {dateIsValid, formatISO} from '@/helpers/time/formatDate' // FIXME: do not use this here for now. instead create new version from DEFAULT_PARAMS
import {objectToSnakeCase} from '@/helpers/case'
import {getDefaultParams} from '@/composables/useTaskList' import {getDefaultParams} from '@/composables/useTaskList'
import {camelCase} from 'camel-case'
// FIXME: merge with DEFAULT_PARAMS in taskList.js // FIXME: merge with DEFAULT_PARAMS in taskList.js
const DEFAULT_PARAMS = { const DEFAULT_PARAMS = {
@ -225,7 +224,7 @@ const DEFAULT_PARAMS = {
filter_include_nulls: true, filter_include_nulls: true,
filter_concat: 'or', filter_concat: 'or',
s: '', s: '',
} } as const
const DEFAULT_FILTERS = { const DEFAULT_FILTERS = {
done: false, done: false,
@ -242,45 +241,9 @@ const DEFAULT_FILTERS = {
labels: '', labels: '',
list_id: '', list_id: '',
namespace: '', namespace: '',
} } as const
export const ALPHABETICAL_SORT = 'title' const props = defineProps({
export default defineComponent({
name: 'filters',
components: {
DatepickerWithRange,
EditLabels,
PrioritySelect,
Fancycheckbox,
PercentDoneSelect,
Multiselect,
},
data() {
return {
params: DEFAULT_PARAMS,
filters: DEFAULT_FILTERS,
usersService: new UserService(),
foundusers: [],
users: [],
labelQuery: '',
labels: [],
listsService: new ListService(),
foundlists: [],
lists: [],
namespaceService: new NamespaceService(),
foundnamespace: [],
namespace: [],
}
},
mounted() {
this.filters.requireAllFilters = this.params.filter_concat === 'and'
},
props: {
modelValue: { modelValue: {
required: true, required: true,
}, },
@ -288,79 +251,109 @@ export default defineComponent({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
}, })
emits: ['update:modelValue'],
watch: { const emit = defineEmits(['update:modelValue'])
modelValue: {
handler(value) { const {modelValue} = toRefs(props)
const labelStore = useLabelStore()
const params = ref({...DEFAULT_PARAMS})
const filters = ref({...DEFAULT_FILTERS})
const services = {
users: shallowReactive(new UserService()),
lists: shallowReactive(new ListService()),
namespace: shallowReactive(new NamespaceService()),
}
interface Entities {
users: IUser[]
labels: ILabel[]
lists: IList[]
namespace: INamespace[]
}
type EntityType = 'users' | 'labels' | 'lists' | 'namespace'
const entities: Entities = reactive({
users: [],
labels: [],
lists: [],
namespace: [],
})
onMounted(() => {
filters.value.requireAllFilters = params.value.filter_concat === 'and'
})
watch(
modelValue,
(value) => {
// FIXME: filters should only be converted to snake case in // FIXME: filters should only be converted to snake case in
// the last moment // the last moment
this.params = objectToSnakeCase(value) params.value = objectToSnakeCase(value)
this.prepareFilters() prepareFilters()
}, },
immediate: true, {immediate: true},
}, )
},
computed: { const sortAlphabetically = computed({
sortAlphabetically: {
get() { get() {
return this.params?.sort_by?.find(sortBy => sortBy === ALPHABETICAL_SORT) !== undefined return params.value?.sort_by?.find(sortBy => sortBy === ALPHABETICAL_SORT) !== undefined
}, },
set(sortAlphabetically) { set(sortAlphabetically) {
this.params.sort_by = sortAlphabetically params.value.sort_by = sortAlphabetically
? [ALPHABETICAL_SORT] ? [ALPHABETICAL_SORT]
: getDefaultParams().sort_by : getDefaultParams().sort_by
this.change() change()
},
}, },
})
foundLabels() { function change() {
const labelStore = useLabelStore() const newParams = {...params.value}
return labelStore.filterLabelsByQuery(this.labels, this.labelQuery) newParams.filter_value = newParams.filter_value.map(v => v instanceof Date ? v.toISOString() : v)
}, emit('update:modelValue', newParams)
}, }
methods: {
change() {
const params = {...this.params}
params.filter_value = params.filter_value.map(v => v instanceof Date ? v.toISOString() : v)
this.$emit('update:modelValue', params)
},
prepareFilters() {
this.prepareDone()
this.prepareDate('due_date', 'dueDate')
this.prepareDate('start_date', 'startDate')
this.prepareDate('end_date', 'endDate')
this.prepareSingleValue('priority', 'priority', 'usePriority', true)
this.prepareSingleValue('percent_done', 'percentDone', 'usePercentDone', true)
this.prepareDate('reminders')
this.prepareRelatedObjectFilter('users', 'assignees')
this.prepareRelatedObjectFilter('lists', 'list_id')
this.prepareRelatedObjectFilter('namespace')
this.prepareSingleValue('labels') 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')
prepareRelatedObjectFilter('lists', 'list_id')
prepareRelatedObjectFilter('namespace')
const labels = typeof this.filters.labels === 'string' prepareSingleValue('labels')
? this.filters.labels
const newLabels = typeof filters.value.labels === 'string'
? filters.value.labels
: '' : ''
const labelIds = labels.split(',').map(i => parseInt(i)) const labelIds = newLabels.split(',').map(i => parseInt(i))
const labelStore = useLabelStore() entities.labels = labelStore.getLabelsByIds(labelIds)
this.labels = labelStore.getLabelsByIds(labelIds) }
},
removePropertyFromFilter(propertyName) { function removePropertyFromFilter(filterName) {
// Because of the way arrays work, we can only ever remove one element at once. // 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. // To remove multiple filter elements of the same name this function has to be called multiple times.
for (const i in this.params.filter_by) { for (const i in params.value.filter_by) {
if (this.params.filter_by[i] === propertyName) { if (params.value.filter_by[i] === filterName) {
this.params.filter_by.splice(i, 1) params.value.filter_by.splice(i, 1)
this.params.filter_comparator.splice(i, 1) params.value.filter_comparator.splice(i, 1)
this.params.filter_value.splice(i, 1) params.value.filter_value.splice(i, 1)
break break
} }
} }
}, }
setDateFilter(filterName, {dateFrom, dateTo}) {
function setDateFilter(filterName, {dateFrom, dateTo}) {
dateFrom = parseDateOrString(dateFrom, null) dateFrom = parseDateOrString(dateFrom, null)
dateTo = parseDateOrString(dateTo, null) dateTo = parseDateOrString(dateTo, null)
@ -370,29 +363,29 @@ export default defineComponent({
// Check if we already have values in params and only update them if we do // Check if we already have values in params and only update them if we do
let foundStart = false let foundStart = false
let foundEnd = false let foundEnd = false
this.params.filter_by.forEach((f, i) => { params.value.filter_by.forEach((f, i) => {
if (f === filterName && this.params.filter_comparator[i] === 'greater_equals') { if (f === filterName && params.value.filter_comparator[i] === 'greater_equals') {
foundStart = true foundStart = true
this.params.filter_value[i] = dateFrom params.value.filter_value[i] = dateFrom
} }
if (f === filterName && this.params.filter_comparator[i] === 'less_equals') { if (f === filterName && params.value.filter_comparator[i] === 'less_equals') {
foundEnd = true foundEnd = true
this.params.filter_value[i] = dateTo params.value.filter_value[i] = dateTo
} }
}) })
if (!foundStart) { if (!foundStart) {
this.params.filter_by.push(filterName) params.value.filter_by.push(filterName)
this.params.filter_comparator.push('greater_equals') params.value.filter_comparator.push('greater_equals')
this.params.filter_value.push(dateFrom) params.value.filter_value.push(dateFrom)
} }
if (!foundEnd) { if (!foundEnd) {
this.params.filter_by.push(filterName) params.value.filter_by.push(filterName)
this.params.filter_comparator.push('less_equals') params.value.filter_comparator.push('less_equals')
this.params.filter_value.push(dateTo) params.value.filter_value.push(dateTo)
} }
this.filters[camelCase(filterName)] = { filters.value[camelCase(filterName)] = {
// Passing the dates as string values avoids an endless loop between values changing // 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 // 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). // datepicker (because there's a new date instance every time this function gets called).
@ -400,26 +393,27 @@ export default defineComponent({
dateFrom: dateIsValid(dateFrom) ? formatISO(dateFrom) : dateFrom, dateFrom: dateIsValid(dateFrom) ? formatISO(dateFrom) : dateFrom,
dateTo: dateIsValid(dateTo) ? formatISO(dateTo) : dateTo, dateTo: dateIsValid(dateTo) ? formatISO(dateTo) : dateTo,
} }
this.change() change()
return return
} }
this.removePropertyFromFilter(filterName) removePropertyFromFilter(filterName)
this.removePropertyFromFilter(filterName) removePropertyFromFilter(filterName)
this.change() change()
}, }
prepareDate(filterName, variableName) {
if (typeof this.params.filter_by === 'undefined') { function prepareDate(filterName, variableName) {
if (typeof params.value.filter_by === 'undefined') {
return return
} }
let foundDateStart = false let foundDateStart = false
let foundDateEnd = false let foundDateEnd = false
for (const i in this.params.filter_by) { for (const i in params.value.filter_by) {
if (this.params.filter_by[i] === filterName && this.params.filter_comparator[i] === 'greater_equals') { if (params.value.filter_by[i] === filterName && params.value.filter_comparator[i] === 'greater_equals') {
foundDateStart = i foundDateStart = i
} }
if (this.params.filter_by[i] === filterName && this.params.filter_comparator[i] === 'less_equals') { if (params.value.filter_by[i] === filterName && params.value.filter_comparator[i] === 'less_equals') {
foundDateEnd = i foundDateEnd = i
} }
@ -429,84 +423,90 @@ export default defineComponent({
} }
if (foundDateStart !== false && foundDateEnd !== false) { if (foundDateStart !== false && foundDateEnd !== false) {
const startDate = new Date(this.params.filter_value[foundDateStart]) const startDate = new Date(params.value.filter_value[foundDateStart])
const endDate = new Date(this.params.filter_value[foundDateEnd]) const endDate = new Date(params.value.filter_value[foundDateEnd])
this.filters[variableName] = { filters.value[variableName] = {
dateFrom: !isNaN(startDate) dateFrom: !isNaN(startDate)
? `${startDate.getFullYear()}-${startDate.getMonth() + 1}-${startDate.getDate()}` ? `${startDate.getFullYear()}-${startDate.getMonth() + 1}-${startDate.getDate()}`
: this.params.filter_value[foundDateStart], : params.value.filter_value[foundDateStart],
dateTo: !isNaN(endDate) dateTo: !isNaN(endDate)
? `${endDate.getFullYear()}-${endDate.getMonth() + 1}-${endDate.getDate()}` ? `${endDate.getFullYear()}-${endDate.getMonth() + 1}-${endDate.getDate()}`
: this.params.filter_value[foundDateEnd], : params.value.filter_value[foundDateEnd],
} }
} }
}, }
setSingleValueFilter(filterName, variableName, useVariableName = '', comparator = 'equals') {
if (useVariableName !== '' && !this.filters[useVariableName]) { function setSingleValueFilter(filterName, variableName, useVariableName = '', comparator = 'equals') {
this.removePropertyFromFilter(filterName) if (useVariableName !== '' && !filters.value[useVariableName]) {
removePropertyFromFilter(filterName)
return return
} }
let found = false let found = false
this.params.filter_by.forEach((f, i) => { params.value.filter_by.forEach((f, i) => {
if (f === filterName) { if (f === filterName) {
found = true found = true
this.params.filter_value[i] = this.filters[variableName] params.value.filter_value[i] = filters.value[variableName]
} }
}) })
if (!found) { if (!found) {
this.params.filter_by.push(filterName) params.value.filter_by.push(filterName)
this.params.filter_comparator.push(comparator) params.value.filter_comparator.push(comparator)
this.params.filter_value.push(this.filters[variableName]) params.value.filter_value.push(filters.value[variableName])
} }
this.change() change()
}, }
/**
* function prepareSingleValue(
* @param filterName The filter name in the api. /** The filter name in the api. */
* @param variableName The name of the variable in this.filters. filterName,
* @param useVariableName The name of the variable of the "Use this filter" variable. Will only be set if the parameter is not null. /** The name of the variable in filters ref. */
* @param isNumber Toggles if the value should be parsed as a number. variableName = null,
*/ /** The name of the variable of the "Use this filter" variable. Will only be set if the parameter is not null. */
prepareSingleValue(filterName, variableName = null, useVariableName = null, isNumber = false) { useVariableName = null,
/** Toggles if the value should be parsed as a number. */
isNumber = false,
) {
if (variableName === null) { if (variableName === null) {
variableName = filterName variableName = filterName
} }
let found = false let found = false
for (const i in this.params.filter_by) { for (const i in params.value.filter_by) {
if (this.params.filter_by[i] === filterName) { if (params.value.filter_by[i] === filterName) {
found = i found = i
break break
} }
} }
if (found === false && useVariableName !== null) { if (found === false && useVariableName !== null) {
this.filters[useVariableName] = false filters.value[useVariableName] = false
return return
} }
if (isNumber) { if (isNumber) {
this.filters[variableName] = Number(this.params.filter_value[found]) filters.value[variableName] = Number(params.value.filter_value[found])
} else { } else {
this.filters[variableName] = this.params.filter_value[found] filters.value[variableName] = params.value.filter_value[found]
} }
if (useVariableName !== null) { if (useVariableName !== null) {
this.filters[useVariableName] = true filters.value[useVariableName] = true
} }
}, }
prepareDone() {
function prepareDone() {
// Set filters.done based on params // Set filters.done based on params
if (typeof this.params.filter_by === 'undefined') { if (typeof params.value.filter_by === 'undefined') {
return return
} }
this.filters.done = this.params.filter_by.some((f) => f === 'done') === false filters.value.done = params.value.filter_by.some((f) => f === 'done') === false
}, }
async prepareRelatedObjectFilter(kind, filterName = null, servicePrefix = null) {
async function prepareRelatedObjectFilter(kind: EntityType, filterName = null, servicePrefix: Omit<EntityType, 'labels'> | null = null) {
if (filterName === null) { if (filterName === null) {
filterName = kind filterName = kind
} }
@ -515,8 +515,8 @@ export default defineComponent({
servicePrefix = kind servicePrefix = kind
} }
this.prepareSingleValue(filterName) prepareSingleValue(filterName)
if (typeof this.filters[filterName] === 'undefined' || this.filters[filterName] === '') { if (typeof filters.value[filterName] === 'undefined' || filters.value[filterName] === '') {
return return
} }
@ -524,113 +524,67 @@ export default defineComponent({
// This is not the most ideal solution because it prevents a re-population when filters are changed // 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 // from the outside. It is still fine because we're not changing them from the outside, other than
// loading them initially. // loading them initially.
if (this[kind].length > 0) { if (entities[kind].length > 0) {
return return
} }
this[kind] = await this[`${servicePrefix}Service`].getAll({}, {s: this.filters[filterName]}) entities[kind] = await services[servicePrefix].getAll({}, {s: filters.value[filterName]})
}, }
setDoneFilter() {
if (this.filters.done) { function setDoneFilter() {
this.removePropertyFromFilter('done') if (filters.value.done) {
removePropertyFromFilter('done')
} else { } else {
this.params.filter_by.push('done') params.value.filter_by.push('done')
this.params.filter_comparator.push('equals') params.value.filter_comparator.push('equals')
this.params.filter_value.push('false') params.value.filter_value.push('false')
} }
this.change() change()
}, }
setFilterConcat() {
if (this.filters.requireAllFilters) { function setFilterConcat() {
this.params.filter_concat = 'and' if (filters.value.requireAllFilters) {
params.value.filter_concat = 'and'
} else { } else {
this.params.filter_concat = 'or' params.value.filter_concat = 'or'
}
change()
} }
this.change()
},
setPriority() {
this.setSingleValueFilter('priority', 'priority', 'usePriority')
},
setPercentDoneFilter() {
this.setSingleValueFilter('percent_done', 'percentDone', 'usePercentDone')
},
clear(kind) {
this[`found${kind}`] = []
},
async find(kind, query) {
if (query === '') { function setPriority() {
this.clear(kind) setSingleValueFilter('priority', 'priority', 'usePriority')
}
function setPercentDoneFilter() {
setSingleValueFilter('percent_done', 'percentDone', 'usePercentDone')
}
async function changeMultiselectFilter(kind: EntityType, filterName) {
await nextTick()
if (entities[kind].length === 0) {
removePropertyFromFilter(filterName)
change()
return return
} }
const response = await this[`${kind}Service`].getAll({}, {s: query}) const ids = entities[kind].map(u => kind === 'users' ? u.username : u.id)
// Filter users from the results who are already assigned filters.value[filterName] = ids.join(',')
this[`found${kind}`] = response.filter(({id}) => !includesById(this[kind], id)) setSingleValueFilter(filterName, filterName, '', 'in')
}, }
add(kind, filterName) {
this.$nextTick(() => { function changeLabelFilter() {
this.changeMultiselectFilter(kind, filterName) if (entities.labels.length === 0) {
}) removePropertyFromFilter('labels')
}, change()
remove(kind, filterName) {
this.$nextTick(() => {
this.changeMultiselectFilter(kind, filterName)
})
},
changeMultiselectFilter(kind, filterName) {
if (this[kind].length === 0) {
this.removePropertyFromFilter(filterName)
this.change()
return return
} }
const ids = [] const labelIDs = entities.labels.map(u => u.id)
this[kind].forEach(u => { filters.value.labels = labelIDs.join(',')
ids.push(kind === 'users' ? u.username : u.id) setSingleValueFilter('labels', 'labels', '', 'in')
})
this.filters[filterName] = ids.join(',')
this.setSingleValueFilter(filterName, filterName, '', 'in')
},
findLabels(query) {
this.labelQuery = query
},
addLabel() {
this.$nextTick(() => {
this.changeLabelFilter()
})
},
removeLabel(label) {
this.$nextTick(() => {
for (const l in this.labels) {
if (this.labels[l].id === label.id) {
this.labels.splice(l, 1)
} }
break
}
this.changeLabelFilter()
})
},
changeLabelFilter() {
if (this.labels.length === 0) {
this.removePropertyFromFilter('labels')
this.change()
return
}
const labelIDs = []
this.labels.forEach(u => {
labelIDs.push(u.id)
})
this.filters.labels = labelIDs.join(',')
this.setSingleValueFilter('labels', 'labels', '', 'in')
},
},
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -6,7 +6,7 @@ export function findById<T extends {id: string | number}>(array : T[], id : stri
return array.find(({id: currentId}) => currentId === id) return array.find(({id: currentId}) => currentId === id)
} }
export function includesById(array: [], id: string | number) { export function includesById(array: any[], id: string | number) {
return array.some(({id: currentId}) => currentId === id) return array.some(({id: currentId}) => currentId === id)
} }