feat(filter): autocomplete for assignees

This commit is contained in:
kolaente 2024-03-08 11:50:38 +01:00
parent 8fa2f6686a
commit b1d9dc6fc3
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
2 changed files with 53 additions and 11 deletions

View File

@ -8,28 +8,33 @@ import {createRandomID} from '@/helpers/randomId'
import AutocompleteDropdown from '@/components/input/AutocompleteDropdown.vue'
import {useLabelStore} from '@/stores/labels'
import XLabel from '@/components/tasks/partials/label.vue'
import User from '@/components/misc/user.vue'
import ProjectUserService from '@/services/projectUsers'
const {
modelValue,
projectId,
} = defineProps<{
modelValue: string,
projectId?: number,
}>()
const filterQuery = ref('')
const model = defineModel<string>()
const filterQuery = ref<string>('')
const {
textarea: filterInput,
height,
} = useAutoHeightTextarea(filterQuery)
watch(
() => modelValue,
() => model.value,
() => {
filterQuery.value = modelValue
filterQuery.value = model.value
},
{immediate: true},
)
const userService = new UserService()
const projectUserService = new ProjectUserService()
const dateFields = [
'dueDate',
@ -47,6 +52,11 @@ const labelFields = [
'labels',
]
const autocompleteFields = [
...labelFields,
...assigneeFields,
]
const availableFilterFields = [
'done',
'priority',
@ -210,15 +220,27 @@ function handleFieldInput(e, autocompleteOnInput) {
const cursorPosition = filterInput.value.selectionStart
const textUpToCursor = filterQuery.value.substring(0, cursorPosition)
labelFields.forEach(l => {
const pattern = new RegExp('(' + l + '\\s*' + FILTER_OPERATORS_REGEX + '\\s*)([\'"]?)([^\'"&\|\(\)]+\\1?)?$', 'ig')
autocompleteFields.forEach(field => {
const pattern = new RegExp('(' + field + '\\s*' + FILTER_OPERATORS_REGEX + '\\s*)([\'"]?)([^\'"&\|\(\)]+\\1?)?$', 'ig')
const match = pattern.exec(textUpToCursor)
if (match !== null) {
const [matched, prefix, operator, space, keyword] = match
if (keyword) {
autocompleteResultType.value = 'labels'
autocompleteResults.value = labelStore.filterLabelsByQuery([], keyword)
if (matched.startsWith('label')) {
autocompleteResultType.value = 'labels'
autocompleteResults.value = labelStore.filterLabelsByQuery([], keyword)
}
if (matched.startsWith('assignee')) {
autocompleteResultType.value = 'assignees'
if (projectId) {
projectUserService.getAll({projectId}, {s: keyword})
.then(users => autocompleteResults.value = users.length > 1 ? users : [])
} else {
userService.getAll({}, {s: keyword})
.then(users => autocompleteResults.value = users.length > 1 ? users : [])
}
}
autocompleteMatchText.value = keyword
autocompleteMatchPosition.value = prefix.length - 1
}
@ -228,7 +250,9 @@ function handleFieldInput(e, autocompleteOnInput) {
function autocompleteSelect(value) {
filterQuery.value = filterQuery.value.substring(0, autocompleteMatchPosition.value + 1) +
value.title +
(autocompleteResultType.value === 'labels'
? value.title
: value.username) +
filterQuery.value.substring(autocompleteMatchPosition.value + autocompleteMatchText.value.length + 1)
autocompleteResults.value = []
@ -281,6 +305,11 @@ function autocompleteSelect(value) {
v-if="autocompleteResultType === 'labels'"
:label="item"
/>
<User
v-else-if="autocompleteResultType === 'assignees'"
:user="item"
:avatar-size="25"
/>
<template v-else> {{ item }}</template>
</template>
</AutocompleteDropdown>

View File

@ -31,7 +31,10 @@
</Fancycheckbox>
</div>
<FilterInput v-model="filterQuery"/>
<FilterInput
v-model="filterQuery"
:project-id="projectId"
/>
<div class="field">
<label class="label">{{ $t('misc.search') }}</label>
@ -231,6 +234,7 @@ import ProjectService from '@/services/project'
// FIXME: do not use this here for now. instead create new version from DEFAULT_PARAMS
import {getDefaultParams} from '@/composables/useTaskList'
import FilterInput from '@/components/project/partials/FilterInput.vue'
import {useRoute} from 'vue-router'
const props = defineProps({
modelValue: {
@ -244,6 +248,15 @@ const props = defineProps({
const emit = defineEmits(['update:modelValue'])
const route = useRoute()
const projectId = computed(() => {
if (route.name?.startsWith('project.')) {
return Number(route.params.projectId)
}
return undefined
})
// FIXME: merge with DEFAULT_PARAMS in taskProject.js
const DEFAULT_PARAMS = {
sort_by: [],