From 6e5b31f1e041dbdfa8981e645c4967513a93707c Mon Sep 17 00:00:00 2001 From: kolaente Date: Sat, 13 Apr 2024 23:34:25 +0200 Subject: [PATCH] fix(filters): always persist filter or search in query path and load it correctly into filter query input when loading the page Previously, when using the filter query as a search input, it would load the search as requested but the filter query parameter in the url would be empty, which meant the search would not be loaded correctly when reloading (or otherwise newly accessing) the page. We're now persisting the filter and search in the task loading logic, to make sure they are always populated correctly. --- .../project/partials/filter-popup.vue | 36 +++++------------- .../components/project/partials/filters.vue | 18 +++++++-- frontend/src/composables/useTaskList.ts | 38 +++++++++++++------ 3 files changed, 50 insertions(+), 42 deletions(-) diff --git a/frontend/src/components/project/partials/filter-popup.vue b/frontend/src/components/project/partials/filter-popup.vue index 7d3247a07..c846e0819 100644 --- a/frontend/src/components/project/partials/filter-popup.vue +++ b/frontend/src/components/project/partials/filter-popup.vue @@ -30,45 +30,27 @@ import {computed, ref, watch} from 'vue' import Filters from '@/components/project/partials/filters.vue' -import {getDefaultTaskFilterParams, type TaskFilterParams} from '@/services/taskCollection' -import {useRouteQuery} from '@vueuse/router' +import {type TaskFilterParams} from '@/services/taskCollection' -const modelValue = defineModel({}) +const props = defineProps(['modelValue']) +const emit = defineEmits(['update:modelValue']) const value = ref({}) -const filter = useRouteQuery('filter') watch( - () => modelValue.value, + () => props.modelValue, (modelValue: TaskFilterParams) => { value.value = modelValue - if (value.value.filter !== '' && value.value.filter !== getDefaultTaskFilterParams().filter) { - filter.value = value.value.filter - } - }, - {immediate: true}, -) - -watch( - () => filter.value, - val => { - if (modelValue.value?.filter === val || typeof val === 'undefined') { - return - } - - modelValue.value.filter = val }, {immediate: true}, ) function emitChanges(newValue: TaskFilterParams) { - filter.value = newValue.filter - if (modelValue.value?.filter === newValue.filter && modelValue.value?.s === newValue.s) { - return - } - - modelValue.value.filter = newValue.filter - modelValue.value.s = newValue.s + emit('update:modelValue', { + ...value.value, + filter: newValue.filter, + s: newValue.s, + }) } const hasFilters = computed(() => { diff --git a/frontend/src/components/project/partials/filters.vue b/frontend/src/components/project/partials/filters.vue index 79ea4415c..14edc480e 100644 --- a/frontend/src/components/project/partials/filters.vue +++ b/frontend/src/components/project/partials/filters.vue @@ -5,7 +5,7 @@ role="search" > @@ -28,7 +28,7 @@ {{ $t('filters.clear') }} @@ -87,6 +87,16 @@ const params = ref({ s: '', }) +const filterQuery = ref('') +watch( + () => [params.value.filter, params.value.s], + () => { + const filter = params.value.filter || '' + const s = params.value.s || '' + filterQuery.value = filter || s + }, +) + // Using watchDebounced to prevent the filter re-triggering itself. watch( () => modelValue, @@ -107,7 +117,7 @@ const projectStore = useProjectStore() function change() { const filter = transformFilterStringForApi( - params.value.filter, + filterQuery.value, labelTitle => labelStore.filterLabelsByQuery([], labelTitle)[0]?.id || null, projectTitle => { const found = projectStore.findProjectByExactname(projectTitle) @@ -142,7 +152,7 @@ function changeAndEmitButton() { } function clearFiltersAndEmit() { - params.value.filter = '' + filterQuery.value = '' changeAndEmitButton() } diff --git a/frontend/src/composables/useTaskList.ts b/frontend/src/composables/useTaskList.ts index 27a8ab6eb..09b065c1e 100644 --- a/frontend/src/composables/useTaskList.ts +++ b/frontend/src/composables/useTaskList.ts @@ -1,5 +1,5 @@ import {ref, shallowReactive, watch, computed, type ComputedGetter} from 'vue' -import {useRoute} from 'vue-router' +import {useRoute, useRouter} from 'vue-router' import {useRouteQuery} from '@vueuse/router' import TaskCollectionService, {getDefaultTaskFilterParams, type TaskFilterParams} from '@/services/taskCollection' @@ -66,7 +66,6 @@ export function useTaskList( const params = ref({...getDefaultTaskFilterParams()}) - const search = ref('') const page = useRouteQuery('page', '1', { transform: Number }) const sortBy = ref({ ...sortByDefault }) @@ -74,10 +73,6 @@ export function useTaskList( const allParams = computed(() => { const loadParams = {...params.value} - if (search.value !== '') { - loadParams.s = search.value - } - return formatSortOrder(sortBy.value, loadParams) }) @@ -122,16 +117,38 @@ export function useTaskList( const route = useRoute() watch(() => route.query, (query) => { - const { page: pageQueryValue, search: searchQuery } = query - if (searchQuery !== undefined) { - search.value = searchQuery as string + const { + page: pageQueryValue, + s, + filter, + } = query + if (s !== undefined) { + params.value.s = s as string } if (pageQueryValue !== undefined) { page.value = Number(pageQueryValue) } - + if (filter !== undefined) { + params.value.filter = filter + } }, { immediate: true }) + const router = useRouter() + watch( + () => [page.value, params.value.filter, params.value.s], + () => { + router.replace({ + name: route.name, + params: route.params, + query: { + page: page.value, + filter: params.value.filter || undefined, + s: params.value.s || undefined, + }, + }) + }, + { deep: true }, + ) // Only listen for query path changes watch(() => JSON.stringify(getAllTasksParams.value), (newParams, oldParams) => { @@ -148,7 +165,6 @@ export function useTaskList( totalPages, currentPage: page, loadTasks, - searchTerm: search, params, sortByParam: sortBy, }