From 571bcf8996a1e11e03d65969fdeec17339d9acfb Mon Sep 17 00:00:00 2001 From: kolaente Date: Sat, 17 Feb 2024 19:10:24 +0100 Subject: [PATCH] feat(filters): show user name and avatar for assignee filters --- .../project/partials/FilterInput.vue | 66 +++++++++++++++++-- 1 file changed, 61 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/project/partials/FilterInput.vue b/frontend/src/components/project/partials/FilterInput.vue index 30a72dc71..101abc5ba 100644 --- a/frontend/src/components/project/partials/FilterInput.vue +++ b/frontend/src/components/project/partials/FilterInput.vue @@ -2,6 +2,9 @@ import {computed, nextTick, ref, watch} from 'vue' import {useAutoHeightTextarea} from '@/composables/useAutoHeightTextarea' import DatepickerWithValues from '@/components/date/datepickerWithValues.vue' +import UserService from "@/services/user"; +import {getAvatarUrl, getDisplayName} from "@/models/user"; +import {createRandomID} from "@/helpers/randomId"; const { modelValue, @@ -23,11 +26,18 @@ watch( {immediate: true}, ) +const userService = new UserService() + const dateFields = [ 'dueDate', 'startDate', 'endDate', 'doneAt', + 'reminders', +] + +const assigneeFields = [ + 'assignees', ] const availableFilterFields = [ @@ -35,10 +45,9 @@ const availableFilterFields = [ 'priority', 'usePriority', 'percentDone', - 'reminders', - 'assignees', 'labels', ...dateFields, + ...assigneeFields, ] const filterOperators = [ @@ -81,17 +90,51 @@ function unEscapeHtml(unsafe: string): string { const highlightedFilterQuery = computed(() => { let highlighted = escapeHtml(filterQuery.value) dateFields - .map(o => escapeHtml(o)) .forEach(o => { const pattern = new RegExp(o + '\\s*(<|>|<=|>=|=|!=)\\s*([\'"]?)([^\'"\\s]+\\1?)?', 'ig'); - highlighted = highlighted.replaceAll(pattern, (match, token, start, value, position, last) => { - console.log({position, last}) + highlighted = highlighted.replaceAll(pattern, (match, token, start, value, position) => { if (typeof value === 'undefined') { value = '' } return `${o} ${token} ${value}` }) }) + assigneeFields + .forEach(f => { + const pattern = new RegExp(f + '\\s*(<|>|<=|>=|=|!=)\\s*([\'"]?)([^\'"\\s]+\\1?)?', 'ig'); + highlighted = highlighted.replaceAll(pattern, (match, token, start, value) => { + if (typeof value === 'undefined') { + value = '' + } + + const id = createRandomID(32) + + userService.getAll({}, {s: value}).then(users => { + if (users.length > 0) { + const displayName = getDisplayName(users[0]) + const nameTag = document.createElement('span') + nameTag.innerText = displayName + + const avatar = document.createElement('img') + avatar.src = getAvatarUrl(users[0], 20) + avatar.height = 20 + avatar.width = 20 + avatar.alt = displayName + + // TODO: caching + + nextTick(() => { + const assigneeValue = document.getElementById(id) + assigneeValue.innerText = '' + assigneeValue?.appendChild(avatar) + assigneeValue?.appendChild(nameTag) + }) + } + }) + + return `${f} ${token} ${value}` + }) + }) filterOperators .map(o => ` ${escapeHtml(o)} `) .forEach(o => { @@ -194,6 +237,19 @@ function updateDateInQuery(newDate: string) { padding: .125rem .25rem; display: inline-block; } + + &.filter-query__assignee_value { + padding: .125rem .25rem; + border-radius: $radius; + background-color: var(--grey-200); + color: var(--grey-700); + display: inline-flex; + align-items: center; + + > img { + margin-right: .25rem; + } + } } button.filter-query__date_value {