Add task filter for lists and namespaces (#351)

Add filter population method for labels

Add filter population methods for lists and namespaces

Re-extract one-line methods

Who said you could abstract too much?

Fix populating saved filters with assignees

Add namespaces filter

Add lists filter

Abstract finding related entities

Co-authored-by: kolaente <k@knt.li>
Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/351
Co-Authored-By: konrad <konrad@kola-entertainments.de>
Co-Committed-By: konrad <konrad@kola-entertainments.de>
This commit is contained in:
konrad 2020-12-21 23:13:39 +00:00
parent 4de8030732
commit 6e3a884d60

View file

@ -107,16 +107,16 @@
:close-on-select="true" :close-on-select="true"
:hide-selected="true" :hide-selected="true"
:internal-search="true" :internal-search="true"
:loading="userService.loading" :loading="usersService.loading"
:multiple="true" :multiple="true"
:options="foundUsers" :options="foundusers"
:options-limit="300" :options-limit="300"
:searchable="true" :searchable="true"
:showNoOptions="false" :showNoOptions="false"
:taggable="false" :taggable="false"
@search-change="findUser" @search-change="query => find('users', query)"
@select="user => addUser(user)" @select="() => add('users', 'assignees')"
@remove="removeUser" @remove="() => remove('users', 'assignees')"
label="username" label="username"
placeholder="Type to search for a user..." placeholder="Type to search for a user..."
track-by="id" track-by="id"
@ -124,7 +124,7 @@
> >
<template slot="clear" slot-scope="props"> <template slot="clear" slot-scope="props">
<div <div
@mousedown.prevent.stop="clearAllUsers(props.search)" @mousedown.prevent.stop="clear('users', props.search)"
class="multiselect__clear" class="multiselect__clear"
v-if="users.length"></div> v-if="users.length"></div>
</template> </template>
@ -172,6 +172,73 @@
</multiselect> </multiselect>
</div> </div>
</div> </div>
<template v-if="$route.name === 'filters.create' || $route.name === 'list.edit'">
<div class="field">
<label class="label">Lists</label>
<div class="control">
<multiselect
:clear-on-select="true"
:close-on-select="true"
:hide-selected="true"
:internal-search="true"
:loading="listsService.loading"
:multiple="true"
:options="foundlists"
:options-limit="300"
:searchable="true"
:showNoOptions="false"
:taggable="false"
@search-change="query => find('lists', query)"
@select="() => add('lists', 'list_id')"
@remove="() => remove('lists', 'list_id')"
label="title"
placeholder="Type to search for a list..."
track-by="id"
v-model="lists"
>
<template slot="clear" slot-scope="props">
<div
@mousedown.prevent.stop="clear('lists', props.search)"
class="multiselect__clear"
v-if="lists.length"></div>
</template>
</multiselect>
</div>
</div>
<div class="field">
<label class="label">Namespaces</label>
<div class="control">
<multiselect
:clear-on-select="true"
:close-on-select="true"
:hide-selected="true"
:internal-search="true"
:loading="namespaceService.loading"
:multiple="true"
:options="foundnamespace"
:options-limit="300"
:searchable="true"
:showNoOptions="false"
:taggable="false"
@search-change="query => find('namespace', query)"
@select="() => add('namespace', 'namespace')"
@remove="() => remove('namespace', 'namespace')"
label="title"
placeholder="Type to search for a namespace..."
track-by="id"
v-model="namespace"
>
<template slot="clear" slot-scope="props">
<div
@mousedown.prevent.stop="clear('namespace', props.search)"
class="multiselect__clear"
v-if="namespace.length"></div>
</template>
</multiselect>
</div>
</div>
</template>
</div> </div>
</div> </div>
</template> </template>
@ -190,6 +257,8 @@ import PercentDoneSelect from '@/components/tasks/partials/percentDoneSelect'
import UserService from '@/services/user' import UserService from '@/services/user'
import LabelService from '@/services/label' import LabelService from '@/services/label'
import ListService from '@/services/list'
import NamespaceService from '@/services/namespace'
export default { export default {
name: 'filters', name: 'filters',
@ -223,6 +292,9 @@ export default {
usePercentDone: false, usePercentDone: false,
reminders: '', reminders: '',
assignees: '', assignees: '',
labels: '',
list_id: '',
namespace: '',
}, },
flatPickerConfig: { flatPickerConfig: {
altFormat: 'j M Y H:i', altFormat: 'j M Y H:i',
@ -233,18 +305,28 @@ export default {
mode: 'range', mode: 'range',
}, },
userService: UserService, usersService: UserService,
foundUsers: [], foundusers: [],
users: [], users: [],
labelService: LabelService, labelService: LabelService,
foundLabels: [], foundLabels: [],
labels: [], labels: [],
listsService: ListService,
foundlists: [],
lists: [],
namespaceService: NamespaceService,
foundnamespace: [],
namespace: [],
} }
}, },
created() { created() {
this.userService = new UserService() this.usersService = new UserService()
this.labelService = new LabelService() this.labelService = new LabelService()
this.listsService = new ListService()
this.namespaceService = new NamespaceService()
}, },
mounted() { mounted() {
this.params = this.value this.params = this.value
@ -269,11 +351,16 @@ export default {
}, },
prepareFilters() { prepareFilters() {
this.prepareDone() this.prepareDone()
this.prepareDueDate() this.prepareDate('due_date', 'dueDate')
this.prepareStartDate() this.prepareDate('start_date', 'startDate')
this.prepareEndDate() this.prepareDate('end_date', 'endDate')
this.preparePriority() this.prepareSingleValue('priority', 'priority', 'usePriority', true)
this.preparePercentDone() this.prepareSingleValue('percent_done', 'percentDone', 'usePercentDone', true)
this.prepareDate('reminders')
this.prepareRelatedObjectFilter('users', 'assignees')
this.prepareRelatedObjectFilter('labels', 'labels', 'label')
this.prepareRelatedObjectFilter('lists', 'list_id')
this.prepareRelatedObjectFilter('namespace')
}, },
removePropertyFromFilter(propertyName) { removePropertyFromFilter(propertyName) {
for (const i in this.params.filter_by) { for (const i in this.params.filter_by) {
@ -370,7 +457,18 @@ export default {
this.change() this.change()
}, },
prepareSingleValue(filterName, variableName, useVariableName, isNumber = false) { /**
*
* @param filterName The filter name in the api.
* @param variableName The name of the variable in this.filters.
* @param useVariableName The name of the variable of the "Use this filter" variable. Will only be set if the parameter is not null.
* @param isNumber Toggles if the value should be parsed as a number.
*/
prepareSingleValue(filterName, variableName = null, useVariableName = null, isNumber = false) {
if (variableName === null) {
variableName = filterName
}
let found = false let found = false
for (const i in this.params.filter_by) { for (const i in this.params.filter_by) {
if (this.params.filter_by[i] === filterName) { if (this.params.filter_by[i] === filterName) {
@ -379,7 +477,7 @@ export default {
} }
} }
if (found === false) { if (found === false && useVariableName !== null) {
this.filters[useVariableName] = false this.filters[useVariableName] = false
return return
} }
@ -390,7 +488,9 @@ export default {
this.filters[variableName] = this.params.filter_value[found] this.filters[variableName] = this.params.filter_value[found]
} }
this.filters[useVariableName] = true if (useVariableName !== null) {
this.filters[useVariableName] = true
}
}, },
prepareDone() { prepareDone() {
// Set filters.done based on params // Set filters.done based on params
@ -408,6 +508,24 @@ export default {
this.$set(this.filters, 'done', true) this.$set(this.filters, 'done', true)
} }
}, },
prepareRelatedObjectFilter(kind, filterName = null, servicePrefix = null) {
if (filterName === null) {
filterName = kind
}
if (servicePrefix === null) {
servicePrefix = kind
}
this.prepareSingleValue(filterName)
if (this.filters[filterName] !== '') {
this[`${servicePrefix}Service`].getAll({}, {s: this.filters[filterName]})
.then(r => {
this.$set(this, kind, r)
})
.catch(e => this.error(e, this))
}
},
setDoneFilter() { setDoneFilter() {
if (this.filters.done) { if (this.filters.done) {
this.removePropertyFromFilter('done') this.removePropertyFromFilter('done')
@ -441,39 +559,21 @@ export default {
this.setSingleValueFilter('percent_done', 'percentDone', 'usePercentDone') this.setSingleValueFilter('percent_done', 'percentDone', 'usePercentDone')
}, },
setReminderFilter() { setReminderFilter() {
this.setDateFilter('reminders', 'reminders') this.setDateFilter('reminders')
}, },
prepareDueDate() { clear(kind) {
this.prepareDate('due_date', 'dueDate') this.$set(this, `found${kind}`, [])
}, },
preparePriority() { find(kind, query) {
this.prepareSingleValue('priority', 'priority', 'usePriority', true)
},
prepareStartDate() {
this.prepareDate('start_date', 'startDate')
},
prepareEndDate() {
this.prepareDate('end_date', 'endDate')
},
preparePercentDone() {
this.prepareSingleValue('percent_done', 'percentDone', 'usePercentDone', true)
},
prepareReminders() {
this.prepareDate('reminders', 'reminders')
},
clearUsers() {
this.$set(this, 'foundUsers', [])
},
findUser(query) {
if (query === '') { if (query === '') {
this.clearUsers() this.clear(kind)
} }
this.userService.getAll({}, {s: query}) this[`${kind}Service`].getAll({}, {s: query})
.then(response => { .then(response => {
// Filter the results to not include users who are already assigneid // Filter the results to not include users who are already assigneid
this.$set(this, 'foundUsers', differenceWith(response, this.users, (first, second) => { this.$set(this, `found${kind}`, differenceWith(response, this[kind], (first, second) => {
return first.id === second.id return first.id === second.id
})) }))
}) })
@ -481,30 +581,30 @@ export default {
this.error(e, this) this.error(e, this)
}) })
}, },
addUser() { add(kind, filterName) {
this.$nextTick(() => { this.$nextTick(() => {
this.changeAssigneeFilter() this.changeMultiselectFilter(kind, filterName)
}) })
}, },
removeUser() { remove(kind, filterName) {
this.$nextTick(() => { this.$nextTick(() => {
this.changeAssigneeFilter() this.changeMultiselectFilter(kind, filterName)
}) })
}, },
changeAssigneeFilter() { changeMultiselectFilter(kind, filterName) {
if (this.users.length === 0) { if (this[kind].length === 0) {
this.removePropertyFromFilter('assignees') this.removePropertyFromFilter(filterName)
this.change() this.change()
return return
} }
let userIDs = [] let ids = []
this.users.forEach(u => { this[kind].forEach(u => {
userIDs.push(u.id) ids.push(u.id)
}) })
this.$set(this.filters, 'assignees', userIDs.join(',')) this.$set(this.filters, filterName, ids.join(','))
this.setSingleValueFilter('assignees', 'assignees', '', 'in') this.setSingleValueFilter(filterName, filterName, '', 'in')
}, },
clearLabels() { clearLabels() {
this.$set(this, 'foundLabels', []) this.$set(this, 'foundLabels', [])